ci(security): hardnen caching#1811
Conversation
Signed-off-by: Giles Cope <gilescope@gmail.com>
Signed-off-by: Squirrel <giles.cope@shielded.io>
m2ux
left a comment
There was a problem hiding this comment.
PR Review Summary
PR: #1811 — ci(security): hardnen caching
Reviewers: Code Review Agent · Test Suite Review Agent · Validation Agent · Strategic Review Agent
Reports: Prior Feedback Triage · Code Review · Test Suite Review · Strategic Review
Branch / Commit reviewed: giles-hmac @ fd5c6f9e (base main @ ecab01bf)
Date: 2026-07-07
Executive Summary
This PR closes the documented fork-forgery attack on unsigned passed-<check>-<tree> cache keys by appending a 16-hex HMAC-SHA256 suffix derived from TREE_GUARD_HMAC. Empty-key fail-open, save sentinel suppression, and secret wiring at all 20 PR-touched guard sites are implemented correctly. Fork PRs cannot hit or persist authenticated entries; trusted same-repo runs retain cache reuse after the one-time invalidation of legacy unsigned keys post-merge.
No security or correctness blockers found. Remaining items are optional perf wiring, author housekeeping, and branch sync.
Overall Rating: Comment Only (no blockers; safe to merge from a CI-integrity standpoint)
Prior Feedback Triage
| # | Prior Comment | Author | Disposition | Reasoning |
|---|---|---|---|---|
| 1 | Local Environment Tests pipeline failure @ fd5c6f9e |
datadog-official | Confirmed (non-blocker) | Earthfile local-env/e2e stack failure — unrelated to HMAC guard logic; guard correctly wired at continuous-integration.yml#L1164 |
| 2 | Add PR link to change file | gilescope (inline suggestion) | Resolved | PR link present on head at hmac-authenticate-tree-guard-cache-keys.md#L12 |
Finding Details
No formal reviews or CHANGES_REQUESTED state. Rating cap unset — no unaddressed blocker-class human comments.
Code Review Findings
| # | Finding | Severity |
|---|---|---|
| 1 | scan.yaml guard site lacks hmac-key — trusted scan never caches |
Medium |
| 2 | 64-bit HMAC truncation — adequate for cache-key use | Low |
| 3 | TREE_GUARD_HMAC secret presence not verified by reviewer |
Low |
Finding Details
CR-1. scan.yaml guard site lacks hmac-key (Medium — perf only)
The scan job at scan.yaml#L35-L36 uses the guard without hmac-key. On trusted runs the empty-key branch fires → always miss; save skips no-cache-no-hmac-key sentinel. Security impact: none. Perf impact: scan job never benefits from tree-cache reuse. Pre-existing omission, not in PR diff.
Suggestion: Optional follow-up PR to add hmac-key: ${{ secrets.TREE_GUARD_HMAC }} for consistency.
CR-2. 64-bit HMAC truncation (Low)
Truncating to 16 hex chars (64 bits) provides adequate forgery resistance for rate-limited cache-key guessing via GitHub APIs. Raw secret never appears in the key string.
Suggestion: No change required.
CR-3. Secret presence unverified (Low)
If TREE_GUARD_HMAC is missing from repo settings, all trusted runs hit the empty-key path → universal cache miss (fail-open, perf degradation only). Author checklist marks secret configured.
Suggestion: Confirm secret exists before merge or accept one-time full CI cycle post-merge.
Verified correct: Empty-key opt-out (action.yml#L50-L55), HMAC derivation (action.yml#L72-L73), save sentinel skip (save/action.yml#L14, L18), 20/20 PR-touched sites wired, fork forgery path closed, lean implementation.
Test Review Findings
| # | Gap | Severity |
|---|---|---|
| 1 | scan.yaml without hmac-key — trusted scan never caches |
Low |
Finding Details
TR-1. scan.yaml cache gap (Low)
Static trace confirms all 10 planned test cases pass (PR1811-TC-01 through TC-10). The sole coverage gap is the pre-existing unwired scan.yaml site — security-safe via empty-key opt-out; perf-only. No automated unit tests for composite bash HMAC logic — industry norm for GH Actions; static analysis is primary evidence.
Documentation Review
| # | Gap | Severity |
|---|---|---|
| 1 | PR body TODO still lists "add PR link to change file" — already present on head | Low |
| 2 | PR submission checklist: DCO sign-off and self-review unchecked | Low |
Finding Details
DR-1. Stale PR body TODO (Low)
Change file PR link is present at hmac-authenticate-tree-guard-cache-keys.md#L12. Author should tick or remove the stale TODO.
DR-2. Submission checklist (Low)
DCO sign-off and self-review remain unchecked — author housekeeping before merge, not a review blocker.
Verified correct: Change file attack description and fix summary match implementation.
Validation Findings
| # | Check | Severity |
|---|---|---|
| 1 | Local Environment Tests failed @ fd5c6f9e |
Low |
Finding Details
VF-1. Local Environment Tests failure (Low — non-blocker)
Failed step: Deploy and test against local environment (Earthfile local-env/e2e stack). Unrelated to tree-cache-guard or HMAC logic. 33 other CI checks pass including Lint workflows (actionlint equivalent), Run tests, builds, and security scans.
Verified: YAML parse OK on all 7 touched files; HMAC smoke test produces expected 16-hex suffix format; CI Lint workflows pass.
Branch Hygiene
| # | Item | Severity |
|---|---|---|
| 1 | Branch 8 commits behind main |
Low |
Finding Details
BH-1. Branch freshness (Low)
giles-hmac is 8 commits behind origin/main but GitHub reports MERGEABLE. Recent main commits do not touch tree-cache-guard.
Suggestion: Rebase or merge main before merge to reduce integration risk.
Action Items
Must Address (Blocking):
- None.
Should Address (Recommended):
- Confirm
TREE_GUARD_HMACexists in repo secrets (CR-3) - Complete DCO sign-off and self-review on PR checklist (DR-2)
- Rebase or merge
mainbefore merge (BH-1)
Could Address (Suggested):
- Wire
hmac-keyatscan.yamlguard site for trusted-run cache reuse (CR-1, TR-1) - Tick or remove stale PR body TODO for change-file link (DR-1)
Nice to Have (Optional):
- Re-run or investigate Local Environment Tests failure separately (VF-1)
Review conducted via work-package workflow (review mode). Artifacts under .engineering/artifacts/planning/2026-07-07-review-midnight-node-pr-1811-hmac-cache/.
Overview
Authenticate
tree-cache-guardcache keys with an HMAC so forks cannot forge a "passed" marker.The guard skips a CI job when a
passed-<check>-<tree>cache entry exists, but the existence check usesgh cache list, which sees entries from every branch scope. A fork PR can write cache entries scoped to its own ref (no secrets needed), and the list API surfaces them repo-wide — so an attacker could mint a key for an arbitrary tree hash and make trusted runs skip their checks.Fix: the key now embeds a signature —
passed-<check>-<tree>-<sig>wheresigis the first 16 hex chars (64 bits) ofHMAC-SHA256(TREE_GUARD_HMAC, "<check>-<tree>"):env, never interpolated intorun:.savenow skipsno-cache-*sentinel keys, so opt-out paths (workflow override, missing secret) no longer create junk cache entries.All 20 guard call sites across 5 workflows now pass
hmac-key: ${{ secrets.TREE_GUARD_HMAC }}; save sites need no change (the signature rides inside the key output).Residual trust boundary (intended): anyone who can execute a workflow with secrets access — same-repo branches, merged code — can still compute valid keys. Rotating the secret invalidates all existing guard entries at the cost of one full rerun per tree.
🗹 TODO before merging
TREE_GUARD_HMACsecret added to the repo📌 Submission Checklist
git commit -s) for the DCOpassed-*entries no longer match, so every tree reruns CI once after merge; behaviour is otherwise unchanged🧪 Testing Evidence
yq).passed-<check>-<tree>-<sig16>, length 104 ≪ the 512-char cache-key limit; different secret ⇒ different signature.Please describe any additional testing aside from CI:
🔱 Fork Strategy
Links