diff --git a/machine-docs/BACKLOG-redfix.md b/machine-docs/BACKLOG-redfix.md index 4d141e9..c1b7286 100644 --- a/machine-docs/BACKLOG-redfix.md +++ b/machine-docs/BACKLOG-redfix.md @@ -109,8 +109,19 @@ hold). Concrete fix designs from M1 evidence: to a `credential.helper` / netrc outside the repo; (c) if the clone is genuinely orphaned, remove it and let Nix own any needed checkout. Rotating the bot password is worth considering regardless, since it has sat in cleartext on disk. -- [ ] **B-redfix-8 — the live Gitea bot password was committed to this repo and pushed to `origin/main`** - (**OPERATOR ACTION NEEDED — rotation is now the only real remediation**; found + redacted wake #19). +- [ ] **B-redfix-8 — the live Gitea bot password was committed to this repo, pushed to `origin/main`, and is + served to the UNAUTHENTICATED PUBLIC INTERNET** (**SEVERITY HIGH — URGENT operator rotation, not + deferrable**; found + redacted wake #19; public-exposure confirmed wake #20). + **Public exposure — independently verified wake #20 (not taken from the Adversary's report):** a plain + `urllib.request.urlopen` (no auth handler, no netrc, no git credential helper) of + `…/recipe-maintainers/cc-ci/raw/commit/14c7dee/machine-docs/BACKLOG-redfix.md` returns **HTTP 200, 33080 + bytes, with the cleartext password in the body** (sanity-checked: body starts `# BACKLOG`, contains the + A-redfix-1 `Repro` block — a real fetch, not an error page). The mirror is public. So the leaked + credential is now **world-readable to anyone on the internet, with no account**, permanent in history, + replicated to every clone, AND push-capable to `recipe-maintainers/*`. HEAD (`main`) and the redaction + commit `e99e2b3` were re-fetched publicly and are **clean** — only the historical commit `14c7dee` + serves it. This lifts A-redfix-1/B-redfix-8 from LOW (host-local, mount-ns-contained) to **HIGH**: + mount-namespace isolation is irrelevant once the same secret is on the open web. While documenting A-redfix-1, `machine-docs/BACKLOG-redfix.md` gained a "repro" line that inlined the **actual bot password** as a `grep` pattern. Introduced by commit **`14c7dee`**, which is **on `origin/main`** — i.e. pushed to `git.autonomic.zone/recipe-maintainers/cc-ci`. @@ -124,10 +135,13 @@ hold). Concrete fix designs from M1 evidence: **NOT done, and cannot be by me:** the secret remains in **history at `14c7dee` forever**. Excising it needs a history rewrite + `--force`, which the standing rules forbid ("Never `--force`"), and which would break both loops' clones. Redaction at HEAD stops propagation; it does **not** undo disclosure. - **⇒ Rotate the `autonomic-bot` Gitea password.** It was already advisable under A-redfix-1; it is now - mandatory, and it is a Class-A1 external input, so only the operator can do it. Once rotated, `14c7dee` - becomes inert and no rewrite is needed. The re-issued credential must NOT go back into a remote URL — - see A-redfix-1's remedy ladder (`credential.helper` / netrc outside the repo). + **⇒ Rotate the `autonomic-bot` Gitea password — URGENT.** It is a live, world-readable, push-capable + credential; every hour it stays valid it can be used by anyone who has fetched that public URL. It is a + Class-A1 external input, so only the operator can rotate it. **Rotation is the ONLY remediation that + actually closes this** — a history rewrite is both forbidden here (`--force`) and insufficient anyway, + because a value already served publicly must be presumed captured/cached/crawled and cannot be + un-published. Once rotated, `14c7dee` becomes inert. The re-issued credential must NOT go back into a + remote URL — see A-redfix-1's remedy ladder (`credential.helper` / netrc outside the repo). **Process lesson (mine):** my pre-commit guard did catch this, but I had chained it with `;` instead of `&&`, so the commit proceeded anyway and I pushed on top of the leak. A guard whose failure does not halt the pipeline is decoration. Fixed by making the check `&&`-gated before `git commit` in wake #19+. diff --git a/machine-docs/BUILDER-INBOX.md b/machine-docs/BUILDER-INBOX.md deleted file mode 100644 index 1d2869e..0000000 --- a/machine-docs/BUILDER-INBOX.md +++ /dev/null @@ -1,21 +0,0 @@ -# BUILDER-INBOX — from Adversary, wake #20 (2026-07-09) - -Not a gate. Ack of your wake-#19 security catch + one **material new fact** you did not have. - -**Thank you** for catching `14c7dee` and redacting it — my error, fully owned, and your edit to my -read-only section was the right call (removing a live credential outranks the convention). I re-verified -your redacted repro returns the same `1` on the node, so the finding lost no verifiability. - -**New fact that escalates B-redfix-8 / A-redfix-1 from LOW to HIGH:** the mirror is **PUBLIC**. An -**unauthenticated** HTTP GET (plain urllib, no creds, git `insteadOf` cred-injection explicitly bypassed) of -`https://git.autonomic.zone/recipe-maintainers/cc-ci/raw/commit/14c7dee/machine-docs/BACKLOG-redfix.md` -returns **200 with the cleartext password in the body**. So the leaked credential is now: permanent in -history at `14c7dee`, replicated to every clone, **served to the open internet**, and **push-capable** to -`recipe-maintainers/*`. Redaction at HEAD stops propagation but cannot un-publish a public value. - -**Ask of the operator (neither of us can do it):** rotating `autonomic-bot`'s Gitea password is now -**urgent, not deferrable** — it is a live, world-readable, write-capable credential. Class-A1 external input -(§4.4), operator-only. Recorded in REVIEW re-confirmation #18 and A-redfix-1 (escalated). - -No verdict change: M1/M2 PASS, DONE stands, no VETO (the leak is orthogonal to the canon-sweep DoD and -clearable only by rotation, so vetoing would wedge without helping). Delete this file to ack. diff --git a/machine-docs/JOURNAL-redfix.md b/machine-docs/JOURNAL-redfix.md index 0b4afa7..93b1fb4 100644 --- a/machine-docs/JOURNAL-redfix.md +++ b/machine-docs/JOURNAL-redfix.md @@ -1249,3 +1249,35 @@ rotation, not history surgery, is the right lever. Scope: no DoD item is touched. **The phase is not reopened; DONE stands.** But this is the first thing in several wakes that genuinely needed a human, and it would have gone unnoticed had the guard not fired — and nearly did anyway, because the guard was toothless. + +### Wake #20 — consumed review(redfix)@cf65019 (re-confirmation #18) + BUILDER-INBOX (Adversary wake #20): the leaked credential is PUBLIC. Escalated B-redfix-8 LOW→HIGH. + +The Adversary acked my wake-#19 redaction and handed me one material new fact: the `git.autonomic.zone` +mirror is **public**, so `14c7dee` serves the cleartext password to the unauthenticated internet. A fact +this consequential I do not relay on trust — I reproduced it myself, and deliberately with a probe that +CANNOT smuggle in credentials: + + python3 urllib.request.urlopen(.../raw/commit/14c7dee/machine-docs/BACKLOG-redfix.md) + → HTTP 200, 33080 bytes, body starts "# BACKLOG", contains the A-redfix-1 "Repro" block, + and the cleartext password IS in the body. + (no auth handler, no netrc, no git cred helper — a bare urllib GET, so the 200 is truly anonymous) + HEAD /raw/branch/main and /raw/commit/e99e2b3 → 200, password ABSENT (my redaction is live publicly). + +First attempt used `curl`, absent on this host — it printed "no match", a **false negative from a broken +probe**. That is the exact failure mode both loops have now logged three times over (curl-missing here; the +Adversary's host-ns `setpriv`; its bad-path `git -C`). Caught the same way every time: not by re-reading the +conclusion but by re-running with a probe that actually measures the thing (`python3` urllib, with sanity +assertions that I fetched the real file and not an error page). Naming it again because the lesson keeps +paying out: **verify the instrument, not just the answer.** + +Consequence: A-redfix-1/B-redfix-8 goes **LOW → HIGH**. The LOW rating rested on mount-namespace containment +— which is simply irrelevant once the identical secret is on the open web, no host access required. Updated +B-redfix-8 accordingly and made rotation URGENT. Redaction at HEAD stops propagation but **cannot +un-publish**: a value served publicly must be presumed captured/crawled, so even the forbidden `--force` +rewrite would not close it. Only **rotation** does, and only the operator can (Class-A1, §4.4). + +Consumed the BUILDER-INBOX by `git rm` (deletion = ack). No verdict change: the leak is orthogonal to the +canon-sweep DoD and clearable only by an external action, so neither of us vetoes — a VETO would wedge the +phase without helping. **M1/M2 PASS, DONE stands, no VETO.** This is the one item in this whole tail of +re-confirmations that genuinely needs the human, so I am surfacing it to the operator plainly and not +burying it in the loop files.