diff --git a/BUILD-LOG.md b/BUILD-LOG.md index 700f7a7d1..bd25808e9 100644 --- a/BUILD-LOG.md +++ b/BUILD-LOG.md @@ -5411,3 +5411,5 @@ Row 299 (573, #1806, Gander, Filbert): before, the P10 band test timed a fault r 2026-10-23T17:29:46Z | Gyro Vikunja571 sage-3542 Connections amendment | Branch gyro/editor-connections-design cut exactly from landed PRD1.17 30e6bb680; read REQ-LIB-5 in that PRD. Replaced stale J-b waits in parent and W4. Proposed W8 owns current authorized Markdown/DOCX links/backlinks list, labels, versioned refresh, leakage refusals and fixture tests. Build order under mos-1470/comment961 is W7 closed routing candidate, W8 landing, then W7 human first-real-import opening. No REQ-LIB-8 condition or acceptance change. Q234/mos-1363/S0 M1-M14 and post-10-27/MVP priority fixed. Static diff, twelve brief sections and append-only checks pass. No repo Node, tests, build, browser, queue mutation or live action. Fenton reviews after authorized docs branch push; Sage lands on approval. 2026-10-23T17:32:37Z | Gyro sage-3544 fenton-0016 N1/N2 delta | Before: W7 Gate/final real-conversion list omit the W8 build-order cross-reference and W8 human-view filtering cites agent settings. After: both locations explicitly put W8 landing before human opening as mos-1470 build order, not acceptance; comment/tracked-deletion links are not current story links and never appear in the human Connections view regardless of agent settings. New commit atop approved 5af060fb5, no amend. Static diff and append-only checks pass. Docs only, no repo Node/tests/live action. Fenton checks only this delta. + +2026-10-23 | Gladstone | Gitea #1816 / Vikunja 614 hostcheck item 5 and bundle verification | Before: dpkg's correct 750 form falsely fails item 5, and two run-sheet bundle verifies require a missing repository. Draft after: exact root/mosaic/path with octal 0750 equivalence; stat/output unchanged; both verifies use cleaned-up bare scratch repos. Three additive-only test blocks, two planned mutants. Untested under Sage-3738; Sage-3740 authorizes untested branch push for static review. Queue row pending; Sage owns gate and landing. diff --git a/agents/gladstone/work/hostcheck-item5/BUILD.md b/agents/gladstone/work/hostcheck-item5/BUILD.md new file mode 100644 index 000000000..3d5e31e54 --- /dev/null +++ b/agents/gladstone/work/hostcheck-item5/BUILD.md @@ -0,0 +1,51 @@ +# Hostcheck item 5 and bundle verification (Gitea #1816, Vikunja 614) + +Queue row pending from Sage. Owner Gladstone, reviewer Morgana, gate/landing Sage. +Base: fetched origin/refactor 93694c5185d0a7f989d2fe78fb620e44599b744e; +branch gladstone/hostcheck-item5. Scope: mos-1531 / ops-0256, sage-3738 and sage-3740. + +## Before and after + +Ubuntu 24.04 dpkg 1.22.6ubuntu6.6 prints `root mosaic 750 /usr/bin/bwrap`. +Item 5 compared the literal `0750`, falsely refusing a correctly configured host; +--enforce would unload the profile, close bwrap and record DRIFT. +The candidate accepts exactly four fields, root, mosaic, octal 0750, and $BWRAP. +`0*750` represents all octal spellings numerically equal to 0750 without arithmetic +conversion/overflow. Horizontal whitespace separates fields; extra fields and +embedded newlines refuse. Owner, group and path remain exact. The stat comparison +and existing output strings remain unchanged. Only the item 5 comment also changes. + +The two release-transport bundle checks previously ran outside any repository. +Each now creates an empty bare scratch repository, verifies with git -C, and removes +the scratch directory using an EXIT trap. A failure exits the shell before cloning +or fetching. No other run-sheet text changes. + +## Tests and mutants + +Only packages/cli/tests/case1-hostcheck.test.mjs changes among tests, by appending +three new test() blocks; the existing passing 0750 fake and failing fake are intact. +The pass test exercises 750, 0750 and 000750. The enforce test checks no unload, +chown, chmod, DRIFT or hostcheck log for 750. Refusals independently cover 755, +0755, 4750, 75, 7500, 075, non-octal strings, other owner/group/path, missing path, +extra fields, multiple lines, blank/empty output and a missing fixture file. + +| Mutant | Change | Killer | Result | +|---|---|---|---| +| M1 | Restore the old literal root mosaic 0750 comparison (drop 750) | new check pass test | Unrun | +| M2 | Accept any mode while preserving owner/group/path | new refusal test | Unrun | + +Mutant procedure: preserve script bytes, assert one replacement, run the named +killer alone, require failure for its intended assertion, restore in finally and +check the original bytes/hash. Run the original targeted file again after restore. + +## Verification status + +Untested. Sage-3738 prohibits all Node until CLEAR because the joint full gate +runs alone. Sage-3740 explicitly directs pushing this untested build for Morgana's +static review before CLEAR. This supersedes test-first execution for this draft; +no Node, tests, node --check, full suites or live-host operations have run. +Queue next refused linked-worktree execution; Sage owns queue creation/movement. +Push only gladstone/*, with MOSAIC_GITEA_CREDENTIAL_FILE unset; no amend/force. + +Static draft receipt: `git diff --check` passed. Test-file numstat against the +base is `46 0 packages/cli/tests/case1-hostcheck.test.mjs` (added/deleted). diff --git a/docs/SESSIONS.md b/docs/SESSIONS.md index c45d62d98..db5207cc8 100644 --- a/docs/SESSIONS.md +++ b/docs/SESSIONS.md @@ -1593,3 +1593,4 @@ are never rewritten or removed; corrections are new entries. 2026-10-23T17:48:43Z | Scrooge (629b6cd3) | Row 252 Console inbox Approve and Deny (#1753) | Rebased onto 306 landing as 580037105 (range-diff all =), slot run bus 907/0 webui 410/0 cli 943/0, in-review revs 3608/3609, Webby r1 APPROVE, J1-J19 specs to Sage; Sage landed 45ea3fa0d (done rev 3616). Part 1 mutant results were never in BUILD.md; they went to Sage by T3 (scrooge-0252-mutant-specs). 2026-10-23 | Sage (1ef1e4f8) | Rows 308 and 252 landings, 571 Connections design amendment, Vikunja 597 | 308 (PRD 1.17, #1815) landed 30e6bb680 and 252 (#1753) landed 45ea3fa0d (replay of 580037105 onto 30e6bb680, range-diff all =), each after a full gate on the landed tree (252 also J1-J19, J11c and J18 equivalent); Gyro's 571 amendment (Fenton APPROVE) landed e8bb13df8; Vikunja 570, 435 and 597 done, canonical webui esbuild 0.28.2; queue revs 3589-3616 committed 59f013247 2026-10-23 | Sage (1ef1e4f8) | Vikunja 571 W build rows; rows 13, 42, 257 and 289 check | Filed the design's ten W rows as queue rows 309-318 (revs 3617-3626, owners set to unassigned 3627-3636, queue commit 293b42fee pushed); Vikunja 571 done (comment 994). Row 13's candidate fd72d2689 is on refactor, gate is Monday's row 7 ledger run; 42 paused under mos-1184; 257 post-v1; 289 waits on 214 and 288. Slot released 17:56Z. +2026-10-23 | Gladstone (433058fc-9cea-46e3-8554-6ddefb312762, Codex gpt-6.1-sol) | #1816 / Vikunja 614, mos-1531 | Built hostcheck octal-mode fix and two bare-scratch bundle verifies on gladstone/hostcheck-item5 from 93694c518. Three append-only test blocks; Node/tests held by Sage-3738, untested push authorized sage-3740. Queue number pending; Morgana static review, Sage gate/landing. diff --git a/docs/guides/host-run-sheet.md b/docs/guides/host-run-sheet.md index e7c3b204f..3f9aded87 100644 --- a/docs/guides/host-run-sheet.md +++ b/docs/guides/host-run-sheet.md @@ -189,7 +189,12 @@ First install: ```sh # Copy the bundle in, then check it before anything reads it. echo " /root/.bundle" | sha256sum -c - -git bundle verify /root/.bundle +( + bundle_check="$(mktemp -d)" || exit 1 + trap 'rm -rf "$bundle_check"' EXIT + git init -q --bare "$bundle_check" && + git -C "$bundle_check" bundle verify /root/.bundle +) || exit 1 # Root's checkout. stack-upgrade.sh and case2-unit.sh run from here, never from a copy. git clone -q -b refactor /root/.bundle /root/mosaic-stack @@ -206,7 +211,12 @@ Each upgrade, before `stack-upgrade.sh upgrade`: ```sh echo " /root/.bundle" | sha256sum -c - -git bundle verify /root/.bundle +( + bundle_check="$(mktemp -d)" || exit 1 + trap 'rm -rf "$bundle_check"' EXIT + git init -q --bare "$bundle_check" && + git -C "$bundle_check" bundle verify /root/.bundle +) || exit 1 git -C /root/mosaic-stack fetch -q /root/.bundle refs/heads/refactor:refs/remotes/origin/refactor git -C /root/mosaic-stack checkout -q --detach install -o mosaic -g mosaic -m 0600 /root/.bundle /srv/mosaic/incoming/.bundle diff --git a/packages/cli/tests/case1-hostcheck.test.mjs b/packages/cli/tests/case1-hostcheck.test.mjs index 4ed58ef38..99a65df99 100644 --- a/packages/cli/tests/case1-hostcheck.test.mjs +++ b/packages/cli/tests/case1-hostcheck.test.mjs @@ -333,3 +333,49 @@ test("row 240 pins and usage: the shipped pins are r6's, the script reads the pi assert.deepEqual([u.status, u.stderr], [2, "hostcheck: refusing: run it as root\n"]); } }); + +test("Vikunja 614 check: item 5 accepts dpkg's bare octal mode and leading-zero equivalents", (t) => { + for (const mode of ["750", "0750", "000750"]) { + const h = host(t); + put(join(h.hc, "statoverride"), `root mosaic ${mode} /usr/bin/bwrap\n`); + const r = run(h, "--check"); + assert.equal(r.status, 0, r.out + r.err); + assert.deepEqual(fails(r), []); + assert.ok(r.lines.some((line) => line.startsWith("ok 5 ")), r.out); + } +}); + +test("Vikunja 614 enforce: dpkg's bare 750 leaves bwrap open and writes no DRIFT", (t) => { + const h = host(t); + put(join(h.hc, "statoverride"), "root mosaic 750 /usr/bin/bwrap\n"); + const r = run(h, "--enforce"); + assert.equal(r.status, 0, r.out + r.err); + assert.deepEqual(fails(r), []); + assert.equal(r.lines.at(-1), "hostcheck: all seven hold"); + assert.deepEqual([calls(h, "apparmor_parser"), calls(h, "chown"), calls(h, "chmod")], [[], [], []]); + assert.equal(existsSync(drift(h)), false); + assert.equal(existsSync(hlog(h)), false); +}); + +test("Vikunja 614 check: item 5 refuses other modes, owners, groups, paths and malformed overrides", (t) => { + const overrides = [ + ...["755", "0755", "4750", "75", "7500", "075", "0780", "8", "0o750", "+750", "-750", "7x0", "750+0", ""].map((mode) => `root mosaic ${mode} /usr/bin/bwrap\n`), + "mosaic mosaic 750 /usr/bin/bwrap\n", + "root root 750 /usr/bin/bwrap\n", + "root mosaic 750 /usr/bin/other\n", + "root mosaic 750 /usr/bin/bwrap extra\n", + "root mosaic 750\n", + "root mosaic 750 /usr/bin/bwrap\nroot mosaic 750 /usr/bin/bwrap\n", + "\n", + "", + null, + ]; + for (const override of overrides) { + const h = host(t); + if (override === null) rmSync(join(h.hc, "statoverride")); + else put(join(h.hc, "statoverride"), override); + const r = run(h, "--check"); + assert.equal(r.status, 1, `${JSON.stringify(override)}: ${r.out}${r.err}`); + assert.deepEqual(fails(r).map((line) => line.split(" ")[1]), ["5"], r.out); + } +}); diff --git a/scripts/case1-hostcheck.sh b/scripts/case1-hostcheck.sh index bbd4f0c7c..1e59c4882 100755 --- a/scripts/case1-hostcheck.sh +++ b/scripts/case1-hostcheck.sh @@ -16,7 +16,7 @@ # 3. `grep -rlw bwrap /etc/apparmor.d` lists that one file and nothing else. # 4. A bwrap child started as mosaic under the units' no-new-privs reads the label # `bwrap//&unpriv_bwrap (enforce)`, exactly. -# 5. The statoverride on /usr/bin/bwrap reads `root mosaic 0750`, and stat agrees. +# 5. The statoverride on /usr/bin/bwrap has root mosaic and octal mode 0750 (750 accepted), and stat agrees. # 6. kernel.apparmor_restrict_unprivileged_userns and fs.protected_hardlinks are 1. # 7. net.ipv4.ip_unprivileged_port_start is above the Console's --port in the installed unit, or # above 1023, the highest --port case2-unit.sh takes, while no unit is installed. @@ -114,7 +114,7 @@ item5() { local so st so="$(dpkg-statoverride --list "$BWRAP" 2>/dev/null)" st="$(stat -c '%U %G %a' "$ROOT$BWRAP" 2>/dev/null)" - [ "$so" = "root mosaic 0750 $BWRAP" ] || { fail "5 the statoverride on $BWRAP reads '$so', not 'root mosaic 0750'"; return; } + [[ "$so" =~ ^root[[:blank:]]+mosaic[[:blank:]]+0*750[[:blank:]]+"$BWRAP"$ ]] || { fail "5 the statoverride on $BWRAP reads '$so', not 'root mosaic 0750'"; return; } [ "$st" = "root mosaic 750" ] || { fail "5 $BWRAP is '$st', not 'root mosaic 750'"; return; } pass "5 $BWRAP is root mosaic 0750, by the statoverride and by stat" }