Skip to content

feat(security): cherry-pick SEC-1 leak-guard CI check + pre-push hook (L1/L3/L4) - #35

Open
asachs01 wants to merge 4 commits into
mainfrom
feat/sec1-leak-guard-cherry-pick
Open

feat(security): cherry-pick SEC-1 leak-guard CI check + pre-push hook (L1/L3/L4)#35
asachs01 wants to merge 4 commits into
mainfrom
feat/sec1-leak-guard-cherry-pick

Conversation

@asachs01

Copy link
Copy Markdown
Member

Summary

  • Cherry-picks two upstream (grandamenium/cortextos) commits, adapted for our fork: 8267bab9 (SEC-1 L1/L4 — server-side leak-guard CI check + broadened .gitignore) and 5a0882d0 (SEC-1 L3 — auto-installed pre-push hook + windowed roster/cron leak-guard heuristic).
  • Scoped, single-mechanism pull per task_1784815034573_65702537not a full upstream sync (fork has diverged too far: 342 local-only vs 247 upstream-only commits touching the same core files).
  • No known active local leak going in — this closes a missing guardrail. Running it against our tree during verification did surface two small pre-existing issues, both fixed in this PR (see below) — proof the mechanism works.

What changed

  1. 8267bab9 / 5a0882d0 cherry-picked as-is — only conflict was a pure-addition .gitignore overlap, resolved by keeping both sides.
  2. fix(security): adapt leak-guard OPERATOR_USERS to our fork's real operator — upstream hardcoded OPERATOR_USERS=cortextos (their own maintainer's machine username). As cherry-picked this was both blind to our real leak class and noisy on legitimate fixtures reusing "cortextos" as a placeholder (tests/sprint7-environment.test.ts, tests/unit/cli/send-telegram-normalize.test.ts). Swapped to our real identity; updated the falsifiability test's planted-leak fixture to match (same split-string technique so the test file carries no literal operator path).
  3. fix(security): remediate pre-existing leak-guard findings on our tree — running the guard for real (not just cherry-picking blind) surfaced:
    • 6 docs/superpowers/plans/*.md files leaked the real operator's home path (/Users/asachs/cortextos) in plain "Working dir:" lines. Verified no secrets/credentials/tokens/IPs in any flagged file before touching anything — this is a username-disclosure-only finding, low severity. Redacted to ~/cortextos in place (real working docs, worth keeping).
    • 11 docs/phase-reports/*.md + WINDOWS_INSTALL_REPORT.md were still tracked despite this repo's own .gitignore already saying docs/ is "personal, not for public repo" — left over from before that rule existed. Content reads as upstream's own demo/test-report material (references James/Luke Stevens, boris/paul/etc. placeholder agent names), not real WYRE fleet data. git rm --cached (not deleted — files stay on disk, already covered by the existing docs/ ignore rule).

Why this matters for review

The two "remediate" findings above are the whole argument for shipping this: the mechanism caught 2 real pre-existing issues before it was even merged. Boss is aware of the operator-path finding and will flag it to Aaron directly (his own path was in the leak).

Test plan

  • tests/leak-guard.test.sh (the mechanism's own falsifiability test) — PASS
  • .github/scripts/leak-guard.sh --tree HEAD (full tree scan, same mode used on push-to-main) — clean
  • Broad secret-shape scan (Anthropic/Supabase/Telegram/Google/Apify token patterns, AWS keys, private-key headers, generic password=/token=/secret= shapes) across every flagged file — zero hits, confirmed before any file was touched
  • npm ci + npm run build — clean
  • npm test — 13 test files / 35 tests fail, confirmed pre-existing on unmodified origin/main (unrelated to this change — hooks/bus dispatch tests, not leak-guard-adjacent). Not introduced by this PR; not fixed by this PR either, out of scope.

🤖 Generated with Claude Code

grandamenium and others added 4 commits July 23, 2026 16:57
… (SEC-1 L1/L4) (grandamenium#698)

L1: .github/workflows/leak-guard.yml runs .github/scripts/leak-guard.sh on every
pull_request (fork-safe, no secrets) and push to main — the server-side backstop
a local pre-push hook cannot provide (covers fork PRs and UI-merges). Blocks on
the operational-LEAK SHAPE (operator home paths, agent-roster+cron-schedule
tables, secret-shaped tokens, dev-report artifact paths), NOT on legitimate
framework convention (agent-name placeholders, lifeos test fixtures). Ships with
a falsifiability test proving it FAILS on a planted leak and PASSES on the clean
tree. Intended as a required status check (applied post-merge, L2).

L4: broaden .gitignore so root-level *INSTALL_REPORT.md / PHASE*-REPORT.md /
docs/phase-reports/ dev artifacts cannot re-track.

Remediation follow-up to the 2026-07-01 fleet-metadata leak.

Co-authored-by: James Goldbach <cortextos@Mac.fios-router.home>
(cherry picked from commit 8267bab)
…k-guard (SEC-1 L3) (grandamenium#704)

* feat(security): auto-install pre-push hook + windowed roster/cron leak-guard (SEC-1 L3)

Two defense-in-depth hardenings on top of the SEC-1 leak-guard work
(L1 CI scan, L2 branch protection, L4 gitignore):

L3(a) — Auto-install the tracked pre-push build+test gate.
`scripts/hooks/pre-push` and `scripts/setup-hooks.sh` already ship, but
nothing wired the installer up, so fresh clones never got the local gate.
- setup-hooks.sh is now NON-CLOBBERING: it installs only when no pre-push
  hook exists (or the existing one is byte-identical); if a different hook
  is already present it is left untouched. Safe/idempotent to re-run and
  never overwrites a user's own hook.
- install.mjs (new section 9b) and `cortextos init` both call the installer
  best-effort and NON-FATAL — a hook-install failure never aborts install
  or init. Gated on non-Windows + repo presence + installer presence.

L3(b) — Windowed roster+cron heuristic in leak-guard.sh.
The same-line ROSTER_CRON_RE misses a leaked ops table that splits an agent
name and its cron expression across adjacent rows. Added an awk windowed
scan (WINDOW=3) that flags a roster name and a cron expression co-occurring
within 3 lines, using the exact cron alternatives from the existing RE. It
fires only when the same-line check did not, so the class reports at most
once per file, and inherits the existing test/fixture skip. Verified no new
false positives against the full tree (leak-guard --tree HEAD stays clean).

Adds tests/leak-guard.test.sh cases: a multi-line roster+cron table must be
flagged; a far-apart name+cron control must stay clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(security): non-clobber guard also catches a broken symlink at hook dest

`[[ -e "$dest" ]]` is false for a broken symlink (it follows the link and the
target is missing), so a broken-symlink pre-push would fall through the guard
and get cp-overwritten — a small hole in the never-overwrite guarantee. Add
`|| -L "$dest"` so a broken symlink is treated as an existing hook and left in
place. Verified: no-hook installs, identical reports already-installed,
different is skipped and preserved, broken symlink is skipped and preserved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 5a0882d)
…rator

Upstream hardcoded OPERATOR_USERS=cortextos - their own maintainer's
machine username, not ours. As cherry-picked, the guard was both blind
to our actual leak class (would never match our real operator's path)
and noisy on legitimate fixtures that happen to reuse "cortextos" as a
generic placeholder (tests/sprint7-environment.test.ts,
tests/unit/cli/send-telegram-normalize.test.ts).

Swaps the pattern to our real identity and updates the falsifiability
test's planted-leak fixture (tests/leak-guard.test.sh) to match, same
split-string technique as upstream so the test file itself carries no
operator-path literal.
Running the newly-cherry-picked leak-guard against our actual tree
(not upstream's) surfaced two real, pre-existing issues:

1. 6 docs/superpowers/plans/*.md files leaked the real operator's home
   path (/Users/asachs/cortextos) in plain "Working dir:" lines. Low
   severity (username disclosure only - verified no secrets/credentials/
   tokens/IPs in any flagged file before touching anything), but exactly
   the leak class this guard exists to catch. Redacted to `~/cortextos`
   in place - these are real working docs, worth keeping.

2. 11 docs/phase-reports/*.md files + WINDOWS_INSTALL_REPORT.md were
   still tracked despite this repo's own .gitignore already stating
   docs/ is "personal, not for public repo" - left over from before
   that rule was added. Content reads as upstream's own demo/test
   report material (references James/Luke Stevens, boris/paul/etc.
   placeholder agent names), not real WYRE fleet data - but the path
   shape alone is exactly what this guard's L4 rule targets, and a
   full-tree scan on every push to main would otherwise make the new
   required check permanently red from the moment it merges. git rm
   --cached (not deleted) - files stay on disk, already covered by the
   existing docs/ gitignore rule so they will not re-track.

Verified: tests/leak-guard.test.sh passes clean, full `--tree HEAD`
scan is clean.
@asachs01

Copy link
Copy Markdown
Member Author

Analyst review — cleared, independently re-verified, not just re-reading the PR body.

Cloned fresh in a disposable /tmp checkout (not this working tree) and ran the actual mechanisms myself:

  • tests/leak-guard.test.sh — PASS
  • .github/scripts/leak-guard.sh --tree HEAD — clean
  • npm test on this branch vs main (08fce65) — diffed the exact set of failing test files between the two: identical, empty diff. Confirmed independently, not just taking the PR body's word: this PR introduces zero new test failures, all failures are pre-existing on unmodified main. One small correction for the record — my local run shows 10 failed files / 27 failed tests, not 13/35 as stated in the PR body; likely a CI-vs-local environment difference (Docker availability, timing), not a discrepancy in the important claim, which holds either way: not introduced by this PR, out of scope to fix here.

Read the full diff, not just the summary:

  • leak-guard.sh / leak-guard.yml cherry-picked faithfully, pull_request (not _target) so fork PRs get no secrets — correct choice for a public repo.
  • OPERATOR_USERS adaptation is the right fix — upstream's hardcoded value would've been both blind to our real leak class and noisy on our own placeholder fixtures. Confirmed the fixture split-string technique keeps the test file itself leak-free.
  • Operator-path redaction diffs (6 files) are surgical — only the /Users/asachs/cortextos~/cortextos substitution, nothing else touched.
  • docs/phase-reports/* + WINDOWS_INSTALL_REPORT.md removal is git rm --cached only per the diff (deletion from tracking, not disk) — matches the PR body's claim, and consistent with the pre-existing .gitignore intent for docs/.
  • install.mjs/init.ts/setup-hooks.sh hook-wiring is non-fatal everywhere (try/catch, warn-not-throw) and non-clobbering (byte-compare before skip) — a hook-install failure can't break install/init, and it won't stomp on anyone's existing hook.

No gaps found. Good work — the mechanism catching 2 real pre-existing issues before merge is a genuinely strong argument for shipping it. Ready for Aaron's batch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants