Route every conf writer through the guarded path
Eleven call sites wrote master.conf with tmp+rename and nothing else — no backup, no parse check, no audit — including the two toggles the UI uses most and the raw editor that installs a whole hand-edited file.
This commit is contained in:
@@ -167,6 +167,9 @@ if ($action === 'save' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (is_array($scripts)) {
|
||||
$confPath = CONF_DIR . '/master.conf';
|
||||
$lines = file($confPath, FILE_KEEP_BLANK_LINES) ?: [];
|
||||
// Captured before array_splice rewrites the block in place — the write compares against
|
||||
// it to prove master.conf did not change while the new block was being assembled.
|
||||
$origConf = implode('', $lines);
|
||||
$esc = preg_quote($scriptsVar, '/');
|
||||
$blockStart = $blockEnd = null;
|
||||
$depth = 0;
|
||||
@@ -213,9 +216,13 @@ if ($action === 'save' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
$newBlock[] = $lines[$blockEnd];
|
||||
array_splice($lines, $blockStart, $blockEnd - $blockStart + 1, $newBlock);
|
||||
// tmp+rename — every script sources master.conf, so a truncated write here is a
|
||||
// system-wide outage, not a lost edit.
|
||||
if (!vv_write_conf_raw('master.conf', implode('', $lines))) {
|
||||
// Shared guarded path: lock, backup, bash -n, atomic install, read-back, audit. The
|
||||
// closure compares against the current contents first, so a master.conf that changed
|
||||
// while this block was being assembled abandons the write instead of clobbering it.
|
||||
$wrote = vv_conf_edit('master.conf', fn(string $cur): ?string =>
|
||||
$cur === $origConf ? implode('', $lines) : null, [], [$scriptsVar]);
|
||||
|
||||
if (!$wrote) {
|
||||
$errors[] = 'scripts write failed';
|
||||
} else {
|
||||
// master.conf is shared — mirrors reorderarray/movescript/rawconf.
|
||||
|
||||
Reference in New Issue
Block a user