fix: clarify audit side effects and boot-check scope

This commit is contained in:
martino
2026-09-17 18:23:56 +02:00
parent dede876f0b
commit 60f71c0c8a
6 changed files with 177 additions and 14 deletions
+66
View File
@@ -0,0 +1,66 @@
// Node 20+: use the same TypeScript compiler harness as the document tests.
// No browser, React rendering, API or host-management imports.
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const { createRequire } = require('node:module');
const app = path.resolve(__dirname, '../AppImage');
const appRequire = createRequire(path.join(app, 'package.json'));
const ts = appRequire('typescript');
const read = rel => fs.readFileSync(path.join(app, rel), 'utf8');
const cache = new Map();
function load(file) {
file = path.resolve(file);
if (cache.has(file)) return cache.get(file).exports;
const mod = { exports: {} };
cache.set(file, mod);
const compiled = ts.transpileModule(fs.readFileSync(file, 'utf8'), {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
}).outputText;
// Evaluate only local repository modules compiled above, never external input.
new Function('require', 'module', 'exports', compiled)(name => {
if (!name.startsWith('.')) return appRequire(name);
return load(path.resolve(path.dirname(file), name + '.ts'));
}, mod, mod.exports);
return mod.exports;
}
const messages = JSON.parse(read('messages/en/common.json'));
const used = [];
const t = (key, values = {}) => {
used.push(key);
let text = key.split('.').reduce((o, k) => o?.[k], messages);
assert.equal(typeof text, 'string', key);
for (const [k, v] of Object.entries(values)) text = text.replaceAll(`{${k}}`, v);
return text;
};
const contract = 'The assessment inspects host settings and health. It can write reports and logs; boot status checks can temporarily mount EFI system partitions.';
global.window = { location: { origin: 'http://localhost' } };
const { buildAuditDocument } = load(path.join(app, 'lib/audit-document.ts'));
const html = buildAuditDocument({
t, locale: 'en', profile: 'full', run: null, findings: [],
inventory: { sections: {}, unavailable: {} },
});
assert.ok(used.includes('audit.presentation.readOnlyScope'), 'document did not look up its scope contract');
assert.ok(html.includes(contract), 'real document omitted the safety contract');
// Check the JSX-to-catalog binding structurally, not quote style, line breaks,
// class names, or paragraph formatting. This is not a React visibility test.
const report = ts.createSourceFile('audit-report.tsx', read('components/audit-report.tsx'),
ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
const noticeKeys = [];
function visit(node) {
if (ts.isJsxExpression(node) && node.expression && ts.isCallExpression(node.expression)) {
const call = node.expression;
const key = call.arguments[0];
if (ts.isIdentifier(call.expression) && call.expression.text === 't' &&
key && ts.isStringLiteral(key) && key.text === 'audit.readOnlyNotice') {
noticeKeys.push(key.text);
}
}
ts.forEachChild(node, visit);
}
visit(report);
assert.ok(noticeKeys.length > 0, 'assessment notice consumer not found');
for (const key of noticeKeys) assert.equal(t(key), contract);
assert.equal(t('audit.document.scopeReadOnly'), contract);
console.log('PASS: real full document safety contract, JSX notice binding, legacy scope agreement');
+95
View File
@@ -0,0 +1,95 @@
"""English safety contract at isolated producer seams; no host-module imports.
Run: python3 -m unittest discover -s tests -p test_audit_safety_wording.py -v
The external boot-tool mount behaviour is documented in the review evidence;
these fixtures exercise ProxMenux's actual command selection and evidence text.
"""
import ast
import json
from pathlib import Path
import re
from types import SimpleNamespace
import unittest
ROOT = Path(__file__).resolve().parents[1]
SCOPE = (
"The assessment inspects host settings and health. It can write reports and "
"logs; boot status checks can temporarily mount EFI system partitions."
)
BOOT_EFFECT = (
"proxmox-boot-tool status can temporarily mount EFI system partitions; "
"no boot is attempted."
)
def boot_check(present, output):
"""Extract only the check and its two pure helpers, with all I/O replaced."""
source = ROOT / "AppImage/scripts/audit_checks_pve.py"
tree = ast.parse(source.read_text())
names = {"_boot_loader", "_version_key", "_unverified"}
nodes = [n for n in tree.body if isinstance(n, ast.FunctionDef) and n.name in names]
assert {n.name for n in nodes} == names
for node in nodes:
node.decorator_list = []
commands = []
def path(value):
assert value == "/etc/kernel/proxmox-boot-uuids", value
return SimpleNamespace(exists=lambda: present)
def run(argv, **kwargs):
commands.append((argv, kwargs))
if argv == ["proxmox-boot-tool", "status"]:
return 0, output
if argv == ["uname", "-r"]:
return 0, "6.8.12-1-pve"
raise AssertionError(f"Unmocked command: {argv}")
env = {"Path": path, "re": re, "json": json,
"CLASS_CONFORMANT": "conformant", "CLASS_WARNING": "warning",
"CLASS_OBSERVATION": "observation", "CLASS_UNVERIFIED": "unverified"}
exec(compile(ast.Module(body=nodes, type_ignores=[]), str(source), "exec"), env)
return env["_boot_loader"](SimpleNamespace(run=run)), commands
class SafetyWording(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.audit = json.loads((ROOT / "AppImage/messages/en/common.json").read_text())["audit"]
def test_boot_evidence_and_rationale_disclose_command_side_effect(self):
for partitions in (1, 2):
with self.subTest(partitions=partitions):
output = "\n".join(
f"ABCD-000{i} is configured with: uefi (versions: 6.8.12-1-pve)"
for i in range(partitions))
result, commands = boot_check(True, output)
self.assertEqual(commands, [
(["proxmox-boot-tool", "status"], {"timeout": 25, "allowed_codes": (0, 1)}),
(["uname", "-r"], {}),
])
self.assertTrue(result["evidence"].endswith(BOOT_EFFECT), result["evidence"])
self.assertTrue(self.audit["checks"]["system"]["boot_loader"]["rationale"].endswith(BOOT_EFFECT))
self.assertEqual(result["summary_params"]["total"], str(partitions))
def test_absent_boot_configuration_does_not_invoke_tool(self):
result, commands = boot_check(False, "")
self.assertIsNone(result)
self.assertEqual(commands, [])
def test_unreadable_boot_status_does_not_claim_success(self):
result, commands = boot_check(True, "E: no configured partitions")
self.assertEqual(result["classification"], "unverified")
self.assertEqual(len(commands), 1)
def test_all_english_scope_surfaces_share_bounded_contract(self):
# The document currently consumes presentation.readOnlyScope;
# document.scopeReadOnly is retained for existing consumers.
for text in (self.audit["readOnlyNotice"],
self.audit["presentation"]["readOnlyScope"],
self.audit["document"]["scopeReadOnly"]):
self.assertEqual(text, SCOPE)
if __name__ == "__main__":
unittest.main()