fix(redfix): B-redfix-8 exposure UNDERSTATED — password is served by TWO published commits (2ad38f5 + e64d8e7), not one; oauth2 token is NOT in git history (0/3099 blobs)
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. Scrubbing2ad38f5alone would leavee64d8e7serving 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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018XE43k4DaeMXHMK51wBUu2
This commit is contained in:
@ -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.
|
||||
|
||||
@ -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`:
|
||||
|
||||
Reference in New Issue
Block a user