diff --git a/machine-docs/REVIEW-1e.md b/machine-docs/REVIEW-1e.md index 0ed3593..3162c1b 100644 --- a/machine-docs/REVIEW-1e.md +++ b/machine-docs/REVIEW-1e.md @@ -5,7 +5,7 @@ Definition of Done = HC1–HC4 each cold-verified PASS here (handshake per plan. ## Definition-of-Done tracker - [ ] **HC1** — Upgrade tier upgrades to PR head (prev published → PR-head via `abra app deploy --chaos`), not a published tag; moved-assertion adapted; DG4.1 deploy-count guard reconciled. -- [ ] **HC2** — Repo-local (PR-authored) `test_*.py` / `install_steps.sh` NOT executed unless recipe is on the cc-ci approval allowlist (default-deny). +- [x] **HC2** — Repo-local (PR-authored) `test_*.py` / `install_steps.sh` NOT executed unless recipe is on the cc-ci approval allowlist (default-deny). **PASS @2026-05-28 (E0, commit c7ae296).** - [ ] **HC3** — Generic runs by default alongside an overlay (additive); skipped only via explicit opt-out; op runs once. - [ ] **HC4** — No regression: D1–D10 / DG1–DG8 re-verified cold; deploy-once (DG4.1) holds; teardown sacred; three new behaviors demonstrated. @@ -15,4 +15,32 @@ Maps to Builder milestones: E0=HC2, E1=HC3, E2=HC1, E3=HC4+docs. - @2026-05-28 — `ssh cc-ci` OK (NixOS 24.11), dashboard HTTP 200 via SOCKS proxy 127.0.0.1:1055. Proxy/SSH path healthy. ## Verdicts -(none yet — Phase 1e not yet started by Builder; awaiting first gate CLAIMED in STATUS-1e.md) + +### E0 / HC2 — repo-local trust gate (default-deny) — PASS @2026-05-28 +Builder claim (STATUS-1e, commit c7ae296 / feat d38a695): repo-local (PR-authored) +`test_*.py`/`install_steps.sh`/`ops.py` consulted only for recipes on `tests/repo-local-approved.txt` +(empty ⇒ deny); centralized `_gated()` in `discovery.py`; 8 unit tests pass. + +**Cold verification (own clone HEAD=c7ae296, shipped to cc-ci, run via `cc-ci-run`):** +1. **Unit suite, independent run:** `cd /tmp/adv-1e && cc-ci-run -m pytest tests/unit -v` → + **8 passed in 0.06s** (incl. repo-local-ignored-when-unapproved / wins-when-approved for + overlay+custom+install_steps+pre_op, and default-allowlist-is-empty). +2. **My own break-it probe** (`hc2_probe.py`, planted a HOSTILE repo-local `install_steps.sh` + `rm -rf /` + `ops.py` `os.system('id')` + `test_install.py`): + - real checked-in allowlist → `approved_recipes() == set()` (default-deny). + - `real-default` → `approved=False`, overlay falls back to **cc-ci**, `install_steps=None`, + `pre_op=None` (hostile repo-local code NOT selected). + - lone `*` → **DENY** (not a wildcard, as the file header promises). + - only-comment / whitespace lines → **DENY**. + - approving a *different* recipe (hedgedoc) → custom-html still **DENY** (no leak). + - `custom-html` listed → `approved=True`, overlay/install_steps/pre_op all flip to **repo-local**. +3. **No bypass:** every execution path in `runner/run_recipe_ci.py` routes through gated + `discovery.*` (`resolve_op`→`resolve_overlay_op`, `custom_tests`, `install_steps`→lifecycle hook). + `snapshot_recipe_tests` reads the repo-local dir ungated but only **copies** it (discover), never + executes — matches the plan's "discovered-but-NOT-executed". `pre_op_hook` not yet wired into the + orchestrator (E1/HC3 work); its discovery fn is already gated. + +Verdict: **PASS** — default-secure, centralized gate, flips only on explicit per-recipe approval; +hostile repo-local code provably not executed under the shipped default. No finding. +**Note (not a defect):** orchestrator still uses single-file override `resolve_op` (1d semantics); +the additive generic floor (HC3) is E1 in-flight — will re-check the gate survives the HC3 refactor.