From fb92c7b992a3f86e92ef07aa8746d656b3e705e1 Mon Sep 17 00:00:00 2001 From: autonomic-bot Date: Thu, 9 Jul 2026 10:58:13 +0000 Subject: [PATCH] =?UTF-8?q?fix(redfix):=20B-redfix-8=20exposure=20UNDERSTA?= =?UTF-8?q?TED=20=E2=80=94=20password=20is=20served=20by=20TWO=20published?= =?UTF-8?q?=20commits=20(2ad38f5=20+=20e64d8e7),=20not=20one;=20oauth2=20t?= =?UTF-8?q?oken=20is=20NOT=20in=20git=20history=20(0/3099=20blobs)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the gap in Adversary wake #57's break-it probe, which covered dashboard/reports/Drone logs but not the public git mirror -- the surface B-redfix-8 is about. - password sha16 3fcea78925015fc9 -> 2 blobs, 2 commits, both ancestors of origin/main. Scrubbing 2ad38f5 alone would leave e64d8e7 serving the live credential. STATUS corrected. - oauth2 token sha16 9c44a1aea2ecb389 -> 0/3099 blobs. Filesystem-only exposure. - Probe hygiene: awk is absent here; awk-based blob lists give a vacuous 0. Positive control (password must return 2) is now mandatory and documented. Operator-scope, no gate impact, no VETO; DONE stands. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_018XE43k4DaeMXHMK51wBUu2 --- machine-docs/BACKLOG-redfix.md | 34 ++++++++++++++++++++++++ machine-docs/STATUS-redfix.md | 47 +++++++++++++++++++++++++++++----- 2 files changed, 75 insertions(+), 6 deletions(-) diff --git a/machine-docs/BACKLOG-redfix.md b/machine-docs/BACKLOG-redfix.md index a686da6..73ba36e 100644 --- a/machine-docs/BACKLOG-redfix.md +++ b/machine-docs/BACKLOG-redfix.md @@ -848,3 +848,37 @@ Still **OPEN**, still **operator-scope**, **no gate / no DoD impact, no VETO**. proposed remedy (strip userinfo from canonical origins + scrub + `chmod 0750 /var/lib/cc-ci-runs`) with the two amendments above (both credentials; canonical-origin fix is required because a one-time scrub is re-armed by the next sweep). Full reasoning + reproductions in REVIEW-redfix.md wake #55. + +### B-redfix-8 — ADDENDUM (wake #58, 2026-07-09): exposure is **two published commits**, not one; and the `oauth2` token is **not** in git history + +Triggered by the Adversary's wake-#57 break-it probe (`5431252`), which verified the token does not leak to +the dashboard / weekly reports / Drone log API — but did **not** probe the **public git mirror**, which is the +surface B-redfix-8 is actually about. Closing that gap surfaced a defect in my own STATUS. + +**Method.** Exhaustive scan of the whole object db (`git cat-file --batch-all-objects` → **3099 blobs**), +literal-value match, `awk`-free (no `awk` on the orchestrator; an `awk`-based pipeline silently yields an +empty blob list and a **vacuous** `0 hits`). Token pulled root-only from a world-readable run-dir +`.git/config` into a `chmod 600` file, verified `sha256[:16]=9c44a1aea2ecb389` (matches the Adversary's +wake-#55 sentinel), `shred`-removed after. Positive control: the known-leaked password **must** be found. + +**Results.** +- **password** (`sha16 3fcea78925015fc9`) → **2** blobs / **2** published commits: + `14c7dee`(blob `fd21fcb8`, 33408 B) and `223cc16`(blob `bcc31b55`, 34353 B). Both are ancestors of + `origin/main` ⇒ both mirror-served. Neither reachable from HEAD. `e99e2b3` and HEAD are clean. + ⇒ STATUS's "**only** the historical commit `14c7dee` serves it" was **wrong and understated the exposure**. + A history scrub targeting `14c7dee` alone would leave `223cc16` serving the live credential. STATUS corrected. +- **oauth2 token** (`sha16 9c44a1aea2ecb389`) → **0** of 3099 blobs. The 271 blobs matching the *string* + `oauth2:` are `recipe-mirror-sync.sh:39` `https://oauth2:${TOKEN}@…` variable interpolation and machine-docs + prose citing the sha16 sentinel — no literal value. + +**Consequence for the operator remedy.** The two credentials are **not symmetric**: +- **password** — exposed on the **public mirror** (permanent, unauthenticated, in every clone) **and** on the + filesystem. Rotation is urgent; scrubbing must cover **both** commits, and cannot recall existing clones. +- **oauth2 token** — exposed on the **filesystem only** (world-readable `.git/config` copies, A-redfix-1), + regenerated weekly by the sweep. Rotation + `chmod`/userinfo-strip suffices; no history rewrite needed. + +**Guard for future probes.** Any scan of this repo reporting **0** password-bearing blobs is a **broken probe** +(missing `awk`, empty sentinel `e3b0c44298fc1c14`, or names-only grep of tracked paths). The correct answer +is **2**. Always run the positive control. + +Still **operator-scope, outside DoD, no gate impact.** `## DONE` stands; no VETO. diff --git a/machine-docs/STATUS-redfix.md b/machine-docs/STATUS-redfix.md index 50ae8a7..652f1ba 100644 --- a/machine-docs/STATUS-redfix.md +++ b/machine-docs/STATUS-redfix.md @@ -62,21 +62,56 @@ re-checked first-hand. Phase DoD is met, but **one HIGH item is open and only an operator can close it.** It does not block `## DONE` (no DoD item depends on it) and is recorded in full in BACKLOG-redfix.md. -**WHAT.** The live Gitea bot password (`GITEA_PASSWORD` in `/srv/cc-ci/.testenv`) was committed in -`14c7dee` (`machine-docs/BACKLOG-redfix.md`), pushed to `origin/main`, and is served **in cleartext to the +**WHAT.** The live Gitea bot password (`GITEA_PASSWORD` in `/srv/cc-ci/.testenv`) was committed to +`machine-docs/BACKLOG-redfix.md`, pushed to `origin/main`, and is served **in cleartext to the unauthenticated public internet** by the public mirror. It is push-capable to `recipe-maintainers/*`. HEAD -and the redaction commit `e99e2b3` are clean; only the historical commit `14c7dee` serves it. Redaction -cannot unpublish it — the value is permanent in history and in every clone. +and the redaction commit `e99e2b3` are clean. Redaction cannot unpublish it — the value is permanent in +history and in every clone. + +**CORRECTED (wake #58, 2026-07-09):** an earlier version of this entry said "**only** the historical commit +`14c7dee` serves it." That **understated the exposure**. An exhaustive scan of all **3099** blobs in the +object db (`git cat-file --batch-all-objects`, literal-value match) finds the password in **two distinct +blobs**, carried by **two published commits** — both ancestors of `origin/main`, so both are mirror-served: + +| commit | blob | `machine-docs/BACKLOG-redfix.md` size | +|---|---|---| +| `14c7dee` | `fd21fcb8ac02bff9917531c8175a29e450c41b19` | 33408 | +| `223cc16` | `bcc31b55860f782056ba3ccdf13165191bd329b5` | 34353 | + +Neither blob is reachable from HEAD. Scrubbing only `14c7dee` would leave `223cc16` serving the credential. **WHERE.** `git.autonomic.zone/recipe-maintainers/cc-ci` → -`/raw/commit/14c7dee/machine-docs/BACKLOG-redfix.md` +`/raw/commit/14c7dee/machine-docs/BACKLOG-redfix.md` **and** +`/raw/commit/223cc16/machine-docs/BACKLOG-redfix.md` **HOW to confirm exposure** (unauthenticated fetch; a raw blob at a fixed sha is immutable, so served size must equal object size): git cat-file -s 14c7dee:machine-docs/BACKLOG-redfix.md # → 33408 + git cat-file -s 223cc16:machine-docs/BACKLOG-redfix.md # → 34353 -An unauthenticated fetch of the raw URL returning **HTTP 200 / 33408 bytes** is the leak, not an error page. +An unauthenticated fetch of **either** raw URL returning **HTTP 200** at the matching byte count is the +leak, not an error page. + +**Exhaustive-scan reproduction** (names/counts only; never prints the value). The `awk`-free form matters — +`awk` is absent on the orchestrator and an `awk`-based blob list yields an **empty** list, i.e. a vacuous +`0 hits`: + + git cat-file --batch-all-objects --batch-check='%(objecttype) %(objectname)' \ + | grep '^blob ' | cut -d' ' -f2 > /tmp/blobs # → 3099 lines; assert >100 before trusting + # then literal-match each blob against a chmod-600 sentinel; positive control MUST return 2 + +**Positive control is mandatory.** Any scan of this repo that reports **0** password-bearing blobs is a +**broken probe**, not a clean repo — the correct answer is **2**. + +**The `oauth2` token (the second exposed credential) does NOT appear in git history.** Same exhaustive scan, +same run, literal match against the live token (`sha256[:16]=9c44a1aea2ecb389`, extracted root-only to a +`chmod 600` file, then `shred`-removed): **0 of 3099 blobs.** The 271 history blobs matching the string +`oauth2:` are `scripts/recipe-mirror-sync.sh:39`'s `https://oauth2:${TOKEN}@…` **variable interpolation** +plus machine-docs **prose citing the sha16 sentinel** — no literal value. So the token's exposure is +**filesystem-only** (A-redfix-1: world-readable `.git/config` copies); it is **not** on the public mirror. +The password's exposure is **both** filesystem **and** public mirror. The two credentials differ in blast +radius; the operator remedy must not treat them as symmetric. **HOW to confirm it is still the live credential** (commits to the value without republishing it). Run this **on the orchestrator** — `/srv/cc-ci/.testenv` lives there, **not** on `cc-ci`: