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:
Gmer4Lfe
2026-08-09 19:07:28 -04:00
parent d9f917ecef
commit 67eabdc17c
10 changed files with 174 additions and 121 deletions
+44 -38
View File
@@ -598,50 +598,56 @@ function vv_conf_flag_value(string $name): bool {
}
// Write a boolean flag value to master.conf.
// Goes through vv_conf_edit() for the lock, the pre-write backup, the syntax check and the audit
// line. This used to call vv_write_conf_raw() directly, which gave it tmp+rename atomicity and
// nothing else — no backup, and no check that the file still sourced afterwards.
function vv_conf_flag_set(string $name, bool $value): bool {
$confPath = CONF_DIR . '/master.conf';
$content = file_get_contents($confPath);
if ($content === false) return false;
$val = $value ? 'true' : 'false';
$new = preg_replace(
'/^(\s*' . preg_quote($name, '/') . '\s*=\s*)(true|false)(\s*(?:#.*)?)$/m',
'${1}' . $val . '${3}',
$content, -1, $count
);
if (!$count) return false;
// tmp+rename — every script sources master.conf, so a truncated write here is a
// system-wide outage, not a lost toggle.
return vv_write_conf_raw('master.conf', $new);
if (!vv_conf_key_valid($name)) return false;
$val = $value ? 'true' : 'false';
return vv_conf_edit('master.conf', function (string $content) use ($name, $val): ?string {
$new = preg_replace(
'/^(\s*' . preg_quote($name, '/') . '\s*=\s*)(true|false)(\s*(?:#.*)?)$/m',
'${1}' . $val . '${3}',
$content, -1, $count
);
// A name that matches no true/false line is a caller error, not an already-correct
// state — unlike the membership toggle below, where absence genuinely means nothing
// to do. Returning null keeps the write from happening and logs reason=no-match.
return $count ? $new : null;
}, [$name => $val]);
}
// Comment or uncomment a script's line in the first master.conf array that contains it.
// Goes through vv_conf_edit() for the lock, the pre-write backup, the syntax check and the audit
// line — see vv_conf_flag_set() above for what that replaced. There is no key to verify here,
// so the audit subject is the script id and a clean source is the whole assertion.
function vv_conf_toggle_script(string $rel, bool $enable): bool {
$confPath = CONF_DIR . '/master.conf';
$lines = file($confPath, FILE_KEEP_BLANK_LINES);
if (!$lines) return false;
$changed = false;
$inArray = false;
$relEsc = preg_quote($rel, '/');
foreach ($lines as &$line) {
if (preg_match('/^\s*[A-Z_]+_SCRIPTS\s*=\s*\(/', $line)) $inArray = true;
if ($inArray && preg_match('/^\s*\)\s*(?:#.*)?$/', $line) && !str_contains($line, '(')) $inArray = false;
if (!$inArray) continue;
if (!preg_match('/^\s*(?:#\s*)?"' . $relEsc . '(?:\s[^"]*)?"/', $line)) continue;
$isCommented = (bool)preg_match('/^\s*#/', $line);
if ($enable && $isCommented) {
$line = preg_replace('/^(\s*)#\s*("' . $relEsc . ')/', '$1$2', $line);
$changed = true;
} elseif (!$enable && !$isCommented) {
$line = preg_replace('/^(\s*)("' . $relEsc . ')/', '$1# $2', $line);
$changed = true;
return vv_conf_edit('master.conf', function (string $content) use ($rel, $enable): ?string {
$lines = preg_split('/(?<=\n)/', $content) ?: [];
$changed = false;
$inArray = false;
$relEsc = preg_quote($rel, '/');
foreach ($lines as &$line) {
if (preg_match('/^\s*[A-Z_]+_SCRIPTS\s*=\s*\(/', $line)) $inArray = true;
if ($inArray && preg_match('/^\s*\)\s*(?:#.*)?$/', $line) && !str_contains($line, '(')) $inArray = false;
if (!$inArray) continue;
if (!preg_match('/^\s*(?:#\s*)?"' . $relEsc . '(?:\s[^"]*)?"/', $line)) continue;
$isCommented = (bool)preg_match('/^\s*#/', $line);
if ($enable && $isCommented) {
$line = preg_replace('/^(\s*)#\s*("' . $relEsc . ')/', '$1$2', $line);
$changed = true;
} elseif (!$enable && !$isCommented) {
$line = preg_replace('/^(\s*)("' . $relEsc . ')/', '$1# $2', $line);
$changed = true;
}
break;
}
break;
}
unset($line);
if (!$changed) return true;
// tmp+rename — every script sources master.conf, so a truncated write here is a
// system-wide outage, not a lost toggle.
return vv_write_conf_raw('master.conf', implode('', $lines));
unset($line);
// A script in no array has nothing to toggle and the conf already reads the way the
// caller asked. Returning the content unchanged reports success without a write.
return $changed ? implode('', $lines) : $content;
}, [], [$rel]);
}
// Parse an orchestrator script to find which child scripts it calls.