Skip to content

ci(security): hardnen caching#1811

Open
gilescope wants to merge 2 commits into
mainfrom
giles-hmac
Open

ci(security): hardnen caching#1811
gilescope wants to merge 2 commits into
mainfrom
giles-hmac

Conversation

@gilescope

@gilescope gilescope commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Overview

Authenticate tree-cache-guard cache 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 uses gh 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> where sig is the first 16 hex chars (64 bits) of HMAC-SHA256(TREE_GUARD_HMAC, "<check>-<tree>"):

  • Fork PRs don't receive secrets, so they can't compute a valid signature for any new tree. Replaying a visible key is harmless — it only asserts a tree that genuinely passed, passed.
  • The raw secret never appears in the key (keys are world-readable via the cache API); it travels input → env, never interpolated into run:.
  • Runs without the secret (fork PRs) always report a miss and never save: full CI runs, no skips, no forgeable writes.
  • save now skips no-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_HMAC secret added to the repo
  • Follow-up commit: add PR link to the change file
  • Ready

📌 Submission Checklist

  • All commits are signed off (git commit -s) for the DCO
  • Changes are backward-compatible (or flagged if breaking) — existing passed-* entries no longer match, so every tree reruns CI once after merge; behaviour is otherwise unchanged
  • Pull request description explains why the change is needed
  • Self-reviewed the diff
  • I have included a change file, or skipped for this reason:
  • If the changes introduce a new feature, I have bumped the node minor version
  • Update documentation (if relevant)
  • Updated AGENTS.md if build commands, architecture, or workflows changed
  • No new todos introduced

🧪 Testing Evidence

  • All 7 touched YAML files parse cleanly (yq).
  • HMAC derivation snippet smoke-tested locally: key format passed-<check>-<tree>-<sig16>, length 104 ≪ the 512-char cache-key limit; different secret ⇒ different signature.
  • This PR's own CI exercises the guard end-to-end (including the actionlint job over the edited workflows).

Please describe any additional testing aside from CI:

  • Additional tests are provided (if possible)

🔱 Fork Strategy

  • Node Runtime Update
  • Node Client Update
  • Other:
  • N/A — CI-only; no node/runtime artefacts change

Links

Signed-off-by: Giles Cope <gilescope@gmail.com>
@gilescope gilescope requested a review from a team as a code owner July 2, 2026 09:34
@datadog-official

datadog-official Bot commented Jul 2, 2026

Copy link
Copy Markdown

Pipelines

⚠️ Warnings

🚦 1 Pipeline job failed

CI + E2E | Local Environment Tests   View in Datadog   GitHub Actions

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: fd5c6f9 | Docs | Give us feedback!

Comment thread changes/node/changed/hmac-authenticate-tree-guard-cache-keys.md Outdated
Signed-off-by: Squirrel <giles.cope@shielded.io>
@chrisferry chrisferry added bot:ai-assisted Authored or substantially edited by an AI agent and removed ai-assisted labels Jul 3, 2026

@m2ux m2ux left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_HMAC exists in repo secrets (CR-3)
  • Complete DCO sign-off and self-review on PR checklist (DR-2)
  • Rebase or merge main before merge (BH-1)

Could Address (Suggested):

  • Wire hmac-key at scan.yaml guard 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/.

@gilescope gilescope requested a review from m2ux July 13, 2026 07:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:ai-assisted Authored or substantially edited by an AI agent skip-changes-check-issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants