From c17d3f147d6ffda26adbd93f919f9f3f13181e66 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Thu, 14 May 2026 18:59:12 +0200 Subject: [PATCH] chore(work): decompose 2026-05-14-ci-security-and-supply-chain epic Decomposer produced 9 stories under docs/work/2026-05-14-ci- security-and-supply-chain/, ordered to land the schema foundation first and the cross-referencing content (reviewer prompt, guide) last: 01 - trace schema extensions (socketRisk + lastRevalidated) 02 - Socket integration (skill + CI) 03 - Renovate adoption 04 - major-bump re-evaluation flow 05 - trace revalidation workflow 06 - CodeQL + audit signatures 07 - gitleaks pre-commit 08 - reviewer prompt update 09 - CI security guide + docs Also fixes a one-char status typo in the PRD frontmatter (\`appoved\` -> \`approved\`) that landed with the decompose run. Anchored by ADR-023 + the approved PRD at docs/work/prds/2026-05-14-ci-security-and-supply-chain.prd.md. Sequencing: depends on stories 01/02/04/06 of the in-flight library-evaluation epic landing first. --- .../01-trace-schema-extensions/_story.md | 49 +++++ .../02-socket-integration/_story.md | 45 +++++ .../03-renovate-adoption/_story.md | 40 +++++ .../04-major-bump-reevaluation/_story.md | 47 +++++ .../05-trace-revalidation-workflow/_story.md | 44 +++++ .../06-codeql-and-audit-signatures/_story.md | 40 +++++ .../07-gitleaks-precommit/_story.md | 39 ++++ .../08-reviewer-prompt-update/_story.md | 40 +++++ .../09-ci-security-guide-and-docs/_story.md | 58 ++++++ .../_epic.md | 29 +++ docs/work/_state.json | 170 +++++++++++++++++- ...-05-14-ci-security-and-supply-chain.prd.md | 2 +- 12 files changed, 599 insertions(+), 4 deletions(-) create mode 100644 docs/work/2026-05-14-ci-security-and-supply-chain/01-trace-schema-extensions/_story.md create mode 100644 docs/work/2026-05-14-ci-security-and-supply-chain/02-socket-integration/_story.md create mode 100644 docs/work/2026-05-14-ci-security-and-supply-chain/03-renovate-adoption/_story.md create mode 100644 docs/work/2026-05-14-ci-security-and-supply-chain/04-major-bump-reevaluation/_story.md create mode 100644 docs/work/2026-05-14-ci-security-and-supply-chain/05-trace-revalidation-workflow/_story.md create mode 100644 docs/work/2026-05-14-ci-security-and-supply-chain/06-codeql-and-audit-signatures/_story.md create mode 100644 docs/work/2026-05-14-ci-security-and-supply-chain/07-gitleaks-precommit/_story.md create mode 100644 docs/work/2026-05-14-ci-security-and-supply-chain/08-reviewer-prompt-update/_story.md create mode 100644 docs/work/2026-05-14-ci-security-and-supply-chain/09-ci-security-guide-and-docs/_story.md create mode 100644 docs/work/2026-05-14-ci-security-and-supply-chain/_epic.md diff --git a/docs/work/2026-05-14-ci-security-and-supply-chain/01-trace-schema-extensions/_story.md b/docs/work/2026-05-14-ci-security-and-supply-chain/01-trace-schema-extensions/_story.md new file mode 100644 index 0000000..24ede94 --- /dev/null +++ b/docs/work/2026-05-14-ci-security-and-supply-chain/01-trace-schema-extensions/_story.md @@ -0,0 +1,49 @@ +--- +id: 01-trace-schema-extensions +epic: 2026-05-14-ci-security-and-supply-chain +title: Trace schema extensions (socketRisk + lastRevalidated) +type: technical-story +status: in-progress +feature: scripts +depends-on: [] +blocks: + [ + 02-socket-integration, + 04-major-bump-reevaluation, + 05-trace-revalidation-workflow, + ] +--- + +## Goal + +Extend `scripts/library-decisions/schema.mjs` with two new fields — `socketRisk` (the 9th filter result) and `lastRevalidated` (ISO-date or null) — and update `docs/library-decisions/_template.md` to reflect the expanded schema. + +## Why + +Every downstream enforcement layer (the evaluate-library skill's 9th filter, the check.mjs major-bump mode, the revalidate.mjs weekly cron) depends on these two fields being present and validated at the schema layer first. Landing them here in one green commit before any consumer touches them prevents schema-drift between layers. + +**External dependency:** library-evaluation epic story 01 (trace schema foundation) must be complete before this story — `scripts/library-decisions/schema.mjs` and `docs/library-decisions/_template.md` must exist. That epic is marked done. + +## Done when + +- `scripts/library-decisions/schema.mjs` exports an updated Zod schema where `filter-results` includes `socketRisk: z.union([z.literal("clean"), z.literal("flagged"), z.string()])` and the trace frontmatter includes `lastRevalidated: z.string().nullable()` (ISO date or null). +- `docs/library-decisions/_template.md` mirrors both new fields with inline documentation of their allowed values. +- `schema.test.mjs` covers: `socketRisk` round-trips for all three variants (`clean`, `flagged`, arbitrary string); `lastRevalidated` accepts ISO date strings and `null`; a trace missing `socketRisk` in `filter-results` fails validation; `lastRevalidated: null` is valid (default for fresh adoptions). +- `pnpm typecheck && pnpm lint && pnpm test && pnpm conformance && pnpm fallow:audit && pnpm coverage:diff` all pass. + +## In scope + +- `scripts/library-decisions/schema.mjs` — schema additions only (no new exports, no breaking changes to existing field shapes). +- `scripts/library-decisions/schema.test.mjs` — new test cases for the two new fields. +- `docs/library-decisions/_template.md` — field documentation additions. + +## Out of scope + +- `socket-cli` invocation or Socket CI step — Story 02. +- `check.mjs` major-bump mode — Story 04. +- `revalidate.mjs` script — Story 05. +- Backfilling existing traces with the new fields — Story 05 (the revalidation cron handles this on its first run). + +## Tasks + +- [ ] Extend `scripts/library-decisions/schema.mjs` adding `socketRisk` (union: `"clean" | "flagged" | string`) to the `filter-results` Zod object and `lastRevalidated` (nullable ISO-date string) to the trace frontmatter schema; update `docs/library-decisions/_template.md` to document both fields with their enum values; add test cases to `schema.test.mjs` covering `socketRisk` round-trips (all three variants), `lastRevalidated` ISO + null acceptance, and missing-`socketRisk` rejection; all gates pass on this single commit. diff --git a/docs/work/2026-05-14-ci-security-and-supply-chain/02-socket-integration/_story.md b/docs/work/2026-05-14-ci-security-and-supply-chain/02-socket-integration/_story.md new file mode 100644 index 0000000..0467298 --- /dev/null +++ b/docs/work/2026-05-14-ci-security-and-supply-chain/02-socket-integration/_story.md @@ -0,0 +1,45 @@ +--- +id: 02-socket-integration +epic: 2026-05-14-ci-security-and-supply-chain +title: Socket integration (skill + CI) +type: technical-story +status: todo +feature: tooling +depends-on: [01-trace-schema-extensions] +blocks: [08-reviewer-prompt-update] +--- + +## Goal + +Wire Socket.dev into two enforcement layers: (1) the `evaluate-library` skill gains Filter 9 (supply-chain behavior) using `socket-cli`, and (2) `ci.yml` gains a `socket-cli scan` step that fails on `critical` severity findings. + +## Why + +CVE databases are lagging indicators — `event-stream`, `ua-parser-js`, and `tj-actions/changed-files` all shipped malware before any CVE existed. Socket detects behavioral signals (new network calls, new post-install scripts, maintainer-account changes) in real time. Placing it as the 9th filter in `evaluate-library` + as a CI gate closes the behavior-compromise surface that CVE scanning misses. + +**External dependency:** library-evaluation epic story 04 (evaluate-library skill) must be complete — the skill's SKILL.md must exist and have the 8-filter structure. That epic is marked done. + +## Done when + +- `.claude/skills/evaluate-library/SKILL.md` has a "Filter 9 — Supply-chain behavior (Socket)" section. The skill's fail-fast logic positions Socket as expensive (network call), running it after the cheap structural filters. The section documents the `socket-cli` verification command and the JSON output fields used to classify `clean` / `flagged` / ``. The trace's `socket-risk` field in `filter-results` is set from this output. +- `.socket.json` exists at repo root: `{ "issueRules": { "critical": "error", "high": "warn", "medium": "ignore", "low": "ignore" } }`. +- `ci.yml`'s `validate` job has a step that runs `socket-cli scan` against the lockfile, filtered to PRs that touch `package.json` or `pnpm-lock.yaml` (via `paths:` condition). The step exits non-zero on any `critical` finding. +- `docs/guides/ci-security.md` Socket App install instructions are deferred to Story 09 (the human guide). This story ships only the machine-enforced layers. +- `pnpm typecheck && pnpm lint && pnpm test && pnpm conformance && pnpm fallow:audit && pnpm coverage:diff` all pass. + +## In scope + +- `.claude/skills/evaluate-library/SKILL.md` — Filter 9 section addition. +- `.socket.json` — repo-root config file. +- `.github/workflows/ci.yml` — one new step in the `validate` job (with `paths:` filter). + +## Out of scope + +- Paid Socket Team plan or server-side PR-block enforcement — explicitly out of PRD scope. +- Socket GitHub App install — consumer-facing instructions live in Story 09's guide. +- Backfilling existing traces with `socket-risk` — Story 05 (revalidation cron handles this). + +## Tasks + +- [ ] Add `.socket.json` at repo root and extend `.claude/skills/evaluate-library/SKILL.md` with a "Filter 9 — Supply-chain behavior (Socket)" section: position Socket after cheap filters, document `socket-cli` as the verification command, specify how `clean`/`flagged`/`` maps to the trace's `socket-risk` field; one commit, all gates pass. +- [ ] Add a `socket-cli scan` step to `ci.yml`'s `validate` job, scoped to PRs touching `package.json` or `pnpm-lock.yaml` via a `paths:` condition; step exits non-zero on any `critical` finding; one commit, all gates pass. diff --git a/docs/work/2026-05-14-ci-security-and-supply-chain/03-renovate-adoption/_story.md b/docs/work/2026-05-14-ci-security-and-supply-chain/03-renovate-adoption/_story.md new file mode 100644 index 0000000..49413d0 --- /dev/null +++ b/docs/work/2026-05-14-ci-security-and-supply-chain/03-renovate-adoption/_story.md @@ -0,0 +1,40 @@ +--- +id: 03-renovate-adoption +epic: 2026-05-14-ci-security-and-supply-chain +title: Renovate adoption +type: technical-story +status: todo +feature: tooling +depends-on: [] +blocks: [09-ci-security-guide-and-docs] +--- + +## Goal + +Ship `.github/renovate.json` configuring automated dependency management: grouped per-ecosystem npm bumps, Dockerfile base-image tracking, GitHub Actions SHA pinning, automerge for green minor+patch PRs, and a single dependency dashboard issue. + +## Why + +Major-tag pinning for GitHub Actions is documented insecure — the 2025 `tj-actions/changed-files` incident proved it. Renovate's `pinGitHubActionDigests` preset automates the one-time SHA-pin sweep and keeps SHAs current thereafter. Grouping ecosystem clusters (Sentry, OTel, tRPC, Payload, Inversify) into weekly PRs prevents noise while ensuring drift is surfaced. Automerge on green minor+patch PRs removes human toil for routine bumps. + +## Done when + +- `.github/renovate.json` exists and is valid JSON, extending presets: `config:base`, `helpers:pinGitHubActionDigests`, `:separateMajorReleases`, `:automergeMinor`, `:automergePatch`. +- `packageRules` groups `@sentry/*`, `@opentelemetry/*`, `@trpc/*`, `payload*`, and `inversify*` into per-cluster weekly PRs. +- Dockerfile manager is enabled for `.sandcastle/Dockerfile`. +- `dependencyDashboard: true` is set (opens a single Renovate-managed issue summarising open + queued PRs, labeled `renovate/dashboard` per PRD Q6). +- Renovate's bump commits use `chore(deps):` (minor/patch) and `chore(deps-major):` (major) commit-message prefixes so release-please's per-package bump rules apply cleanly. +- `pnpm typecheck && pnpm lint && pnpm test && pnpm conformance && pnpm fallow:audit && pnpm coverage:diff` all pass (no executable code change; JSON config only). + +## In scope + +- `.github/renovate.json` — full Renovate configuration. + +## Out of scope + +- Verifying the first Renovate PR (SHA-pin sweep) — that happens when the GitHub App runs, not at commit time. The success criterion (all `@v` pins rewritten to SHAs) is verified when Renovate's first PR merges. +- Renovate Dependency Dashboard → `docs/work/` task integration — explicitly out of scope in the PRD. + +## Tasks + +- [ ] Create `.github/renovate.json` extending presets `config:base`, `helpers:pinGitHubActionDigests`, `:separateMajorReleases`, `:automergeMinor`, `:automergePatch`; add `packageRules` grouping `@sentry/*`, `@opentelemetry/*`, `@trpc/*`, `payload*`, `inversify*` into weekly per-cluster PRs; enable Dockerfile manager for `.sandcastle/Dockerfile`; set `dependencyDashboard: true`; set `commitMessagePrefix` to enforce `chore(deps):` / `chore(deps-major):` per Conventional Commits; one commit, all gates pass. diff --git a/docs/work/2026-05-14-ci-security-and-supply-chain/04-major-bump-reevaluation/_story.md b/docs/work/2026-05-14-ci-security-and-supply-chain/04-major-bump-reevaluation/_story.md new file mode 100644 index 0000000..59ea95c --- /dev/null +++ b/docs/work/2026-05-14-ci-security-and-supply-chain/04-major-bump-reevaluation/_story.md @@ -0,0 +1,47 @@ +--- +id: 04-major-bump-reevaluation +epic: 2026-05-14-ci-security-and-supply-chain +title: Major-bump re-evaluation flow +type: technical-story +status: todo +feature: scripts +depends-on: [01-trace-schema-extensions] +blocks: [05-trace-revalidation-workflow] +--- + +## Goal + +Extend `scripts/library-decisions/check.mjs` with a major-bump detection mode: when invoked on a Renovate PR, parse the lockfile diff to find bumped deps, classify each as major/minor/patch, and for any feature- or core-tier major bump require the trace's `lastRevalidated` field to be fresh (set today); exit non-zero with a pointer to the `evaluate-library` skill if not. + +## Why + +ADR-022 closes the adoption-time gate; ADR-023 closes the drift gate for major-version changes. A Renovate PR that bumps `@sentry/node` from `7.x → 8.x` is effectively a new adoption decision — the original trace may have evaluated a very different API surface and risk profile. Requiring a fresh `lastRevalidated` ensures the trace is re-walked before the bump merges. Minor and patch bumps don't trigger re-evaluation (backwards-compatible by semver contract). + +**External dependency:** library-evaluation epic story 02 (pre-commit check script) must be complete — `scripts/library-decisions/check.mjs` must exist. That epic is marked done. + +## Done when + +- `check.mjs` has a new mode (invocable as `node scripts/library-decisions/check.mjs --renovate-pr`) that: + - Detects Renovate PRs via `renovate/` branch prefix (from `GITHUB_HEAD_REF` or `--branch` arg). + - Parses the lockfile diff (from `git diff origin/main -- pnpm-lock.yaml` or a `--diff` arg) to extract bumped deps with from/to versions. + - Classifies each bump as major / minor / patch using semver comparison. + - For each feature- or core-tier major bump: reads the corresponding trace from `docs/library-decisions/`, checks `lastRevalidated` equals today's ISO date. If not fresh, exits non-zero with a message referencing the `evaluate-library` skill and the trace path. + - App-tier deps: pass unconditionally (ADR-022 exemption). + - Non-Renovate branch: pass unconditionally (the rule is scoped to Renovate PRs only). + - Multiple bumps in one PR: validated per-dep independently (PRD Q3). +- Integration tests in `check.test.mjs` (or equivalent) cover: minor bump on feature-tier dep → pass; major bump + fresh `lastRevalidated` → pass; major bump + stale `lastRevalidated` → fail with clear pointer; major bump on app-tier dep → pass; patch bump in Renovate branch → pass; non-Renovate branch with major bump → pass. +- `pnpm typecheck && pnpm lint && pnpm test && pnpm conformance && pnpm fallow:audit && pnpm coverage:diff` all pass. + +## In scope + +- `scripts/library-decisions/check.mjs` — new `--renovate-pr` mode only; existing modes unchanged. +- `scripts/library-decisions/check.test.mjs` — integration tests for the new mode (use fixture trace files + fixture lockfile diffs; no network calls). + +## Out of scope + +- Auto-closing `library-policy/re-evaluation` issues when `lastRevalidated` is refreshed — Story 05 (revalidation cron) handles this. +- The CI step that invokes this check on Renovate PRs — the script is the unit; wiring it into CI workflows is part of Story 09's guide and/or the reviewer prompt in Story 08. + +## Tasks + +- [ ] Extend `scripts/library-decisions/check.mjs` with a `--renovate-pr` mode: detect Renovate branch prefix, parse lockfile diff for bumped deps, classify semver deltas, require fresh `lastRevalidated` on feature/core-tier major bumps (fail with `evaluate-library` pointer if stale), pass app-tier + non-Renovate + minor/patch unconditionally; write integration tests in `check.test.mjs` with fixture trace files and lockfile diffs covering all six cases; one commit, all gates pass. diff --git a/docs/work/2026-05-14-ci-security-and-supply-chain/05-trace-revalidation-workflow/_story.md b/docs/work/2026-05-14-ci-security-and-supply-chain/05-trace-revalidation-workflow/_story.md new file mode 100644 index 0000000..e12fa67 --- /dev/null +++ b/docs/work/2026-05-14-ci-security-and-supply-chain/05-trace-revalidation-workflow/_story.md @@ -0,0 +1,44 @@ +--- +id: 05-trace-revalidation-workflow +epic: 2026-05-14-ci-security-and-supply-chain +title: Trace revalidation workflow +type: technical-story +status: todo +feature: scripts +depends-on: [01-trace-schema-extensions, 04-major-bump-reevaluation] +blocks: [09-ci-security-guide-and-docs] +--- + +## Goal + +Write `scripts/library-decisions/revalidate.mjs` — a script that walks every approved + pre-shipped trace, re-runs each trace's `verification-commands`, classifies divergence as soft or hard, and opens/updates/closes GitHub issues accordingly — then wire it into `.github/workflows/trace-revalidation-weekly.yml` (weekly cron + `workflow_dispatch`). + +## Why + +ADR-022 traces go stale silently when new CVEs drop or Socket picks up behavioral changes in a package post-adoption. A weekly automated revalidation creates a feedback loop: soft divergence (minor drift) surfaces as a rolling dashboard issue; hard divergence (a re-evaluation is warranted) surfaces as a per-dep `library-policy/re-evaluation` issue with the trace path + finding + re-walk handoff. No auto-edit of traces and no CI gating on main — the workflow runs in parallel, not on the critical path. + +**External dependency:** library-evaluation epic story 02 (pre-commit check script) must be complete — `check.mjs` and the `docs/library-decisions/` fixture patterns are the prior art this script mirrors. + +## Done when + +- `scripts/library-decisions/revalidate.mjs` walks all traces in `docs/library-decisions/` whose `decision` field is `accepted` or `pre-shipped`; for each trace, re-runs its `verification-commands`; classifies divergence (soft: minor discrepancy from expected output; hard: finding that would change the evaluation decision); opens a rolling `library-policy/dashboard`-labeled issue for soft divergence (creates or updates a single issue); opens a `library-policy/re-evaluation`-labeled per-dep issue for hard divergence with title `re-evaluate: @`, trace path, and `evaluate-library` re-walk pointer; closes open `library-policy/re-evaluation` issues whose dep has since had `lastRevalidated` refreshed; skips rejected traces entirely. +- Integration tests use a fixture trace directory (no real `gh` CLI / no network): no-drift trace → no issue; soft-drift trace → dashboard issue created; hard-drift trace → per-dep issue with correct labels + title format; open per-dep issue already exists → no duplicate opened; `lastRevalidated` refreshed → open issue closed with comment. +- `.github/workflows/trace-revalidation-weekly.yml` triggers on `schedule: - cron: "30 6 * * 1"` and `workflow_dispatch`; job steps: checkout, `pnpm install --frozen-lockfile`, `node scripts/library-decisions/revalidate.mjs`; permissions: `issues: write`, `contents: read` (NO `contents: write`). +- `pnpm typecheck && pnpm lint && pnpm test && pnpm conformance && pnpm fallow:audit && pnpm coverage:diff` all pass. + +## In scope + +- `scripts/library-decisions/revalidate.mjs` — the revalidation script. +- `scripts/library-decisions/revalidate.test.mjs` — integration tests with fixture directory and mocked `gh` CLI surface. +- `.github/workflows/trace-revalidation-weekly.yml` — the workflow file. + +## Out of scope + +- Auto-editing trace files — explicitly forbidden (no `contents: write`). +- Auto-dispatching on `library-policy/re-evaluation` issues — human triage required (PRD out of scope). +- CI gating on main from this workflow — main keeps deploying; revalidation runs in parallel. + +## Tasks + +- [ ] Write `scripts/library-decisions/revalidate.mjs` (walk approved+pre-shipped traces, re-run `verification-commands`, classify soft/hard divergence, open/update/close issues via `gh` CLI; mock-friendly `gh` surface for tests); write `revalidate.test.mjs` integration tests with fixture traces covering: no-drift, soft-drift (dashboard issue), hard-drift (per-dep issue with correct labels+title), duplicate-issue guard, stale-issue close on refreshed `lastRevalidated`, rejected-trace skip; one commit, all gates pass. +- [ ] Create `.github/workflows/trace-revalidation-weekly.yml` (trigger: `schedule: cron: "30 6 * * 1"` + `workflow_dispatch`; steps: checkout, `pnpm install --frozen-lockfile`, `node scripts/library-decisions/revalidate.mjs`; permissions: `issues: write`, `contents: read`); one commit, all gates pass. diff --git a/docs/work/2026-05-14-ci-security-and-supply-chain/06-codeql-and-audit-signatures/_story.md b/docs/work/2026-05-14-ci-security-and-supply-chain/06-codeql-and-audit-signatures/_story.md new file mode 100644 index 0000000..d5e8f6f --- /dev/null +++ b/docs/work/2026-05-14-ci-security-and-supply-chain/06-codeql-and-audit-signatures/_story.md @@ -0,0 +1,40 @@ +--- +id: 06-codeql-and-audit-signatures +epic: 2026-05-14-ci-security-and-supply-chain +title: CodeQL workflow + pnpm audit signatures +type: technical-story +status: todo +feature: tooling +depends-on: [] +blocks: [08-reviewer-prompt-update] +--- + +## Goal + +Add two baseline GitHub-native gates: (1) a `pnpm audit signatures --audit-level=high` step in `ci.yml`'s validate job, and (2) a `.github/workflows/codeql.yml` workflow running javascript-typescript static analysis on push/PR/weekly schedule. + +## Why + +`pnpm audit signatures` catches tampered package signatures before they reach production — a post-install script from a compromised maintainer account would fail this check. CodeQL's javascript-typescript analysis catches common vulnerability patterns (XSS, injection, prototype pollution) that are invisible to dependency-scanning tools. Both are zero-cost on public repos and the GitHub Free plan; CodeQL's template includes a clear no-op on plans that don't support it. + +## Done when + +- `ci.yml`'s `validate` job includes a `pnpm audit signatures --audit-level=high` step. The step fails the job on `high` or `critical` severity signature failures. +- `.github/workflows/codeql.yml` exists; triggers: `push: branches: [main]`, `pull_request`, and `schedule: - cron: "0 2 * * 3"` (Wednesday 02:00 UTC, staggered from the trace-revalidation cron); language: `javascript-typescript`; uses default queries. Includes a comment noting that CodeQL on private repos requires GitHub Advanced Security (consumer-toggleable per PRD constraint). +- `pnpm typecheck && pnpm lint && pnpm test && pnpm conformance && pnpm fallow:audit && pnpm coverage:diff` all pass (no executable code change; CI config only). + +## In scope + +- `.github/workflows/ci.yml` — one new `pnpm audit signatures` step in `validate` job. +- `.github/workflows/codeql.yml` — new workflow file. + +## Out of scope + +- Configuring GitHub branch protection to require CodeQL as a status check — consumer-facing instruction deferred to Story 09's guide. +- OSSF Scorecard — explicitly out of PRD scope. +- StepSecurity Harden Runner — explicitly out of PRD scope. + +## Tasks + +- [ ] Add `pnpm audit signatures --audit-level=high` as a step in `ci.yml`'s `validate` job; one commit, all gates pass. +- [ ] Create `.github/workflows/codeql.yml` (language: `javascript-typescript`; triggers: push to main, pull_request, weekly schedule Wednesday 02:00 UTC; default queries; consumer note about GitHub Advanced Security requirement for private repos); one commit, all gates pass. diff --git a/docs/work/2026-05-14-ci-security-and-supply-chain/07-gitleaks-precommit/_story.md b/docs/work/2026-05-14-ci-security-and-supply-chain/07-gitleaks-precommit/_story.md new file mode 100644 index 0000000..6d68b1d --- /dev/null +++ b/docs/work/2026-05-14-ci-security-and-supply-chain/07-gitleaks-precommit/_story.md @@ -0,0 +1,39 @@ +--- +id: 07-gitleaks-precommit +epic: 2026-05-14-ci-security-and-supply-chain +title: Gitleaks pre-commit hook +type: technical-story +status: todo +feature: tooling +depends-on: [] +blocks: [09-ci-security-guide-and-docs] +--- + +## Goal + +Add `gitleaks protect --staged --redact` as a step in `.husky/pre-commit` and ship a `.gitleaks.toml` allowlist that covers test-fixture patterns in `__seeds__/**`, so a commit containing a known secret pattern is blocked locally before it reaches the remote. + +## Why + +Developer accidents (pasting tokens into config, seeding test fixtures with real-looking keys) are the most common secret-leak vector. A pre-commit hook stops the leak at the earliest possible point — before the secret is ever pushed. GitHub native push protection is the second line of defense (documented in Story 09's guide); the hook is the first. The `__seeds__/**` allowlist prevents false positives from test fixtures that deliberately use token-shaped strings as dummy data. + +## Done when + +- `.husky/pre-commit` has a `gitleaks protect --staged --redact` step that runs before the existing state-sync guard (or after — order between guards doesn't matter, both must run). +- `.gitleaks.toml` exists at repo root with at minimum one allowlist rule scoping `__seeds__/**` test fixtures (using `paths` or `allowlist.paths` depending on the gitleaks version). +- A smoke test (bash script or vitest) pipes a staged commit containing a Stripe-style test key (`sk_test_...`) through the hook and asserts non-zero exit code. The smoke test is documented in the story's Done-when but may live as a manual verification step given gitleaks requires a binary; include instructions in `docs/guides/ci-security.md` (Story 09) for consumers to verify locally. +- `pnpm typecheck && pnpm lint && pnpm test && pnpm conformance && pnpm fallow:audit && pnpm coverage:diff` all pass. + +## In scope + +- `.husky/pre-commit` — new `gitleaks` step. +- `.gitleaks.toml` — allowlist config. + +## Out of scope + +- Installing `gitleaks` as a project devDependency — consumers install it via their OS package manager or `brew`; the hook exits gracefully with a warning if `gitleaks` is not found in `$PATH` (to avoid blocking developers who haven't installed it yet, while still enforcing for those who have). +- GitHub native push protection configuration — consumer-facing instruction deferred to Story 09's guide. + +## Tasks + +- [ ] Add `gitleaks protect --staged --redact` step to `.husky/pre-commit` (exit-gracefully if `gitleaks` not in `$PATH`); create `.gitleaks.toml` at repo root with `__seeds__/**` allowlist for test-fixture patterns; one commit, all gates pass. diff --git a/docs/work/2026-05-14-ci-security-and-supply-chain/08-reviewer-prompt-update/_story.md b/docs/work/2026-05-14-ci-security-and-supply-chain/08-reviewer-prompt-update/_story.md new file mode 100644 index 0000000..9e36cfc --- /dev/null +++ b/docs/work/2026-05-14-ci-security-and-supply-chain/08-reviewer-prompt-update/_story.md @@ -0,0 +1,40 @@ +--- +id: 08-reviewer-prompt-update +epic: 2026-05-14-ci-security-and-supply-chain +title: Sandcastle reviewer prompt update +type: technical-story +status: todo +feature: tooling +depends-on: [02-socket-integration, 06-codeql-and-audit-signatures] +blocks: [09-ci-security-guide-and-docs] +--- + +## Goal + +Extend `.sandcastle/reviewer.prompt.md` with a "CI security checks" section that instructs the reviewer agent to read Socket CI output and CodeQL findings via `gh run view`, and reject the slice with specific notes if any `critical` Socket finding or `error`-severity CodeQL finding is present. + +## Why + +The sandcastle reviewer is the single composable gate for agent PRs (ADR-019 constraint). Without an explicit section, an agent reviewer has no machine-readable instruction to check Socket + CodeQL outputs and may approve a slice that introduced a flagged dependency or a static-analysis error. Landing this after Stories 02 + 06 ensures the reviewer references gates that actually exist in CI. + +**External dependency:** library-evaluation epic story 06 (sandcastle reviewer prompt) must be complete — the reviewer prompt must exist and have the library-trace check section that this story composes with. That epic is marked done. + +## Done when + +- `.sandcastle/reviewer.prompt.md` has a "CI security checks" section added after the existing library-trace check section. +- The section instructs the reviewer to: (a) run `gh run view --log` (or equivalent) for the PR's check suite; (b) scan the output for Socket findings of severity `critical` — if found, reject with notes naming the finding and referencing the failure-mode hierarchy in `docs/guides/ci-security.md`; (c) scan the output for CodeQL findings of severity `error` — same rejection pattern. +- The reviewer composes these checks with the existing library-trace presence check (both must pass for approval). +- `pnpm typecheck && pnpm lint && pnpm test && pnpm conformance && pnpm fallow:audit && pnpm coverage:diff` all pass (prose-only change; no executable code). + +## In scope + +- `.sandcastle/reviewer.prompt.md` — new "CI security checks" section only; existing sections unchanged. + +## Out of scope + +- Automated tests for the reviewer prompt — it's a prose runbook for an agent; success is verified manually (PRD testing decisions). +- Extending the reviewer for `pnpm audit signatures` step failures — those surface as standard CI job failures, already handled by the reviewer's existing "all CI checks must pass" instruction. + +## Tasks + +- [ ] Extend `.sandcastle/reviewer.prompt.md` with a "CI security checks" section after the library-trace check: instruct the reviewer to read `gh run view` output for Socket `critical` findings and CodeQL `error` findings, reject on either with notes naming the finding and citing `docs/guides/ci-security.md` failure-mode hierarchy; one commit, all gates pass. diff --git a/docs/work/2026-05-14-ci-security-and-supply-chain/09-ci-security-guide-and-docs/_story.md b/docs/work/2026-05-14-ci-security-and-supply-chain/09-ci-security-guide-and-docs/_story.md new file mode 100644 index 0000000..e35e1df --- /dev/null +++ b/docs/work/2026-05-14-ci-security-and-supply-chain/09-ci-security-guide-and-docs/_story.md @@ -0,0 +1,58 @@ +--- +id: 09-ci-security-guide-and-docs +epic: 2026-05-14-ci-security-and-supply-chain +title: CI security guide + CLAUDE.md +type: technical-story +status: todo +feature: docs +depends-on: + [ + 01-trace-schema-extensions, + 02-socket-integration, + 03-renovate-adoption, + 04-major-bump-reevaluation, + 05-trace-revalidation-workflow, + 06-codeql-and-audit-signatures, + 07-gitleaks-precommit, + 08-reviewer-prompt-update, + ] +blocks: [] +--- + +## Goal + +Write `docs/guides/ci-security.md` — the human reading-room for the four-pillar stack — and add a Key Conventions bullet to `CLAUDE.md` pointing agents and developers to ADR-023 + the guide. + +## Why + +Each prior story lands a machine-enforced layer, but no single document explains the composed system to a developer or consumer who hasn't read all nine stories. `docs/guides/ci-security.md` fills that gap: it explains the four pillars, the failure-mode hierarchy, what settings are consumer-toggleable, and gives two worked examples so the mental model is concrete. The CLAUDE.md bullet ensures the enforcement stack is discoverable during every agent session via the startup context. + +## Done when + +- `docs/guides/ci-security.md` exists and covers: + - Overview of the four pillars (Renovate + Action SHA pinning, Socket, trace revalidation, GitHub-native gates). + - Per-pillar section with what the gate does, what it catches, and how to toggle it in a downstream consumer repo. + - Failure-mode hierarchy table (mirroring ADR-023 §5): pillar, trigger condition, action, label, who resolves. + - Consumer-toggleable settings list: GitHub native push protection, Socket GitHub App install, branch protection rules requiring `library-policy/*`-labeled checks before merge. + - Two worked examples: (a) a passing Renovate minor-bump PR (gates pass, auto-merges); (b) a blocked major-bump PR (Renovate opens PR → `check.mjs` requires `lastRevalidated` refresh → agent re-walks `evaluate-library` → trace updated → PR unblocked) + a hard-divergence revalidation issue (weekly cron finds Socket-flagged dep → per-dep `library-policy/re-evaluation` issue opened → agent closes issue after re-walk). + - Socket GitHub App install instructions for consumers. + - `gitleaks` installation instructions for developers (OS package manager / brew; hook exits gracefully if binary absent). + - Note that CodeQL requires GitHub Advanced Security on private repos. +- `CLAUDE.md` Key Conventions section has a bullet: _"CI security + supply-chain enforcement: Renovate for bumps + Action SHA pinning, Socket for supply-chain behavior, weekly trace revalidation, CodeQL + audit signatures + gitleaks. See ADR-023 + `docs/guides/ci-security.md`."_ +- `pnpm typecheck && pnpm lint && pnpm test && pnpm conformance && pnpm fallow:audit && pnpm coverage:diff` all pass (docs + CLAUDE.md; no executable code). + +## In scope + +- `docs/guides/ci-security.md` — new guide file. +- `CLAUDE.md` — one bullet addition to Key Conventions. + +## Out of scope + +- Configuring GitHub branch protection rules — documented as consumer action, not a tracked file change. +- Installing the Socket GitHub App — documented as consumer action in the guide; no config file change. +- Backfilling existing traces with `last-revalidated` — handled by the first revalidation cron run (Story 05). + +## Tasks + +- [ ] Write `docs/guides/ci-security.md` with: four-pillar overview, per-pillar sections, failure-mode hierarchy table (ADR-023 §5), consumer-toggleable settings list, Socket GitHub App + gitleaks install instructions, CodeQL note for private repos, two worked examples (passing minor-bump PR; blocked major-bump PR + hard-divergence revalidation issue); one commit, all gates pass. +- [ ] Add CI security Key Conventions bullet to `CLAUDE.md` referencing ADR-023 + `docs/guides/ci-security.md`; one commit, all gates pass. diff --git a/docs/work/2026-05-14-ci-security-and-supply-chain/_epic.md b/docs/work/2026-05-14-ci-security-and-supply-chain/_epic.md new file mode 100644 index 0000000..0742291 --- /dev/null +++ b/docs/work/2026-05-14-ci-security-and-supply-chain/_epic.md @@ -0,0 +1,29 @@ +--- +id: 2026-05-14-ci-security-and-supply-chain +prd: docs/work/prds/2026-05-14-ci-security-and-supply-chain.prd.md +title: CI security + supply-chain enforcement stack +type: epic +status: in-progress +features: [scripts, tooling, docs] +created: 2026-05-14 +--- + +## Goal + +Implement a four-pillar CI security stack — Renovate-managed bumps + Action SHA pinning, Socket-based supply-chain-behavior detection, continuous trace revalidation extending ADR-022, and baseline GitHub-native gates — composed via a single failure-mode hierarchy that the sandcastle reviewer prompt enforces machine-readably for agent-driven PRs. Codifies ADR-023. + +## Why + +The repo's security posture has zero security tooling. ADR-022 + the library-evaluation epic close the adoption-time gate for new dependencies but not the drift gate. Six post-adoption threats remain uncovered: CVE disclosures, supply-chain behavior compromise, maintainer-account compromise, GitHub Actions supply-chain (major-tag pinning), license drift, and EU-residency drift. This epic closes all six via the four-pillar stack. + +## Stories + +- [ ] [01 — Trace schema extensions (socketRisk + lastRevalidated)](01-trace-schema-extensions/_story.md) +- [ ] [02 — Socket integration (skill + CI)](02-socket-integration/_story.md) +- [ ] [03 — Renovate adoption](03-renovate-adoption/_story.md) +- [ ] [04 — Major-bump re-evaluation flow](04-major-bump-reevaluation/_story.md) +- [ ] [05 — Trace revalidation workflow](05-trace-revalidation-workflow/_story.md) +- [ ] [06 — CodeQL workflow + pnpm audit signatures](06-codeql-and-audit-signatures/_story.md) +- [ ] [07 — Gitleaks pre-commit hook](07-gitleaks-precommit/_story.md) +- [ ] [08 — Sandcastle reviewer prompt update](08-reviewer-prompt-update/_story.md) +- [ ] [09 — CI security guide + CLAUDE.md](09-ci-security-guide-and-docs/_story.md) diff --git a/docs/work/_state.json b/docs/work/_state.json index 73fce23..2f1db8d 100644 --- a/docs/work/_state.json +++ b/docs/work/_state.json @@ -1,5 +1,5 @@ { - "updated_at": "2026-05-14T16:47:27.076Z", + "updated_at": "2026-05-14T16:59:14.033Z", "epics": { "2026-05-13-binder-wrap-helper": { "status": "done", @@ -39,6 +39,109 @@ } } }, + "2026-05-14-ci-security-and-supply-chain": { + "status": "in-progress", + "title": "CI security + supply-chain enforcement stack", + "prd": "docs/work/prds/2026-05-14-ci-security-and-supply-chain.prd.md", + "stories": { + "01-trace-schema-extensions": { + "status": "in-progress", + "title": "Trace schema extensions (socketRisk + lastRevalidated)", + "ac_total": 1, + "ac_completed": 0, + "depends_on": [], + "blocks": [] + }, + "02-socket-integration": { + "status": "todo", + "title": "Socket integration (skill + CI)", + "ac_total": 2, + "ac_completed": 0, + "depends_on": [ + "01-trace-schema-extensions" + ], + "blocks": [ + "08-reviewer-prompt-update" + ] + }, + "03-renovate-adoption": { + "status": "todo", + "title": "Renovate adoption", + "ac_total": 1, + "ac_completed": 0, + "depends_on": [], + "blocks": [ + "09-ci-security-guide-and-docs" + ] + }, + "04-major-bump-reevaluation": { + "status": "todo", + "title": "Major-bump re-evaluation flow", + "ac_total": 1, + "ac_completed": 0, + "depends_on": [ + "01-trace-schema-extensions" + ], + "blocks": [ + "05-trace-revalidation-workflow" + ] + }, + "05-trace-revalidation-workflow": { + "status": "todo", + "title": "Trace revalidation workflow", + "ac_total": 2, + "ac_completed": 0, + "depends_on": [ + "01-trace-schema-extensions", + "04-major-bump-reevaluation" + ], + "blocks": [ + "09-ci-security-guide-and-docs" + ] + }, + "06-codeql-and-audit-signatures": { + "status": "todo", + "title": "CodeQL workflow + pnpm audit signatures", + "ac_total": 2, + "ac_completed": 0, + "depends_on": [], + "blocks": [ + "08-reviewer-prompt-update" + ] + }, + "07-gitleaks-precommit": { + "status": "todo", + "title": "Gitleaks pre-commit hook", + "ac_total": 1, + "ac_completed": 0, + "depends_on": [], + "blocks": [ + "09-ci-security-guide-and-docs" + ] + }, + "08-reviewer-prompt-update": { + "status": "todo", + "title": "Sandcastle reviewer prompt update", + "ac_total": 1, + "ac_completed": 0, + "depends_on": [ + "02-socket-integration", + "06-codeql-and-audit-signatures" + ], + "blocks": [ + "09-ci-security-guide-and-docs" + ] + }, + "09-ci-security-guide-and-docs": { + "status": "todo", + "title": "CI security guide + CLAUDE.md", + "ac_total": 2, + "ac_completed": 0, + "depends_on": [], + "blocks": [] + } + } + }, "2026-05-14-library-evaluation-policy": { "status": "done", "title": "Library evaluation policy — skill, traces, enforcement stack", @@ -154,7 +257,68 @@ } } }, - "ready": [], - "blocked": [], + "ready": [ + { + "epic": "2026-05-14-ci-security-and-supply-chain", + "story": "01-trace-schema-extensions", + "title": "Trace schema extensions (socketRisk + lastRevalidated)" + }, + { + "epic": "2026-05-14-ci-security-and-supply-chain", + "story": "03-renovate-adoption", + "title": "Renovate adoption" + }, + { + "epic": "2026-05-14-ci-security-and-supply-chain", + "story": "06-codeql-and-audit-signatures", + "title": "CodeQL workflow + pnpm audit signatures" + }, + { + "epic": "2026-05-14-ci-security-and-supply-chain", + "story": "07-gitleaks-precommit", + "title": "Gitleaks pre-commit hook" + }, + { + "epic": "2026-05-14-ci-security-and-supply-chain", + "story": "09-ci-security-guide-and-docs", + "title": "CI security guide + CLAUDE.md" + } + ], + "blocked": [ + { + "epic": "2026-05-14-ci-security-and-supply-chain", + "story": "02-socket-integration", + "title": "Socket integration (skill + CI)", + "waiting_on": [ + "2026-05-14-ci-security-and-supply-chain/01-trace-schema-extensions" + ] + }, + { + "epic": "2026-05-14-ci-security-and-supply-chain", + "story": "04-major-bump-reevaluation", + "title": "Major-bump re-evaluation flow", + "waiting_on": [ + "2026-05-14-ci-security-and-supply-chain/01-trace-schema-extensions" + ] + }, + { + "epic": "2026-05-14-ci-security-and-supply-chain", + "story": "05-trace-revalidation-workflow", + "title": "Trace revalidation workflow", + "waiting_on": [ + "2026-05-14-ci-security-and-supply-chain/01-trace-schema-extensions", + "2026-05-14-ci-security-and-supply-chain/04-major-bump-reevaluation" + ] + }, + { + "epic": "2026-05-14-ci-security-and-supply-chain", + "story": "08-reviewer-prompt-update", + "title": "Sandcastle reviewer prompt update", + "waiting_on": [ + "2026-05-14-ci-security-and-supply-chain/02-socket-integration", + "2026-05-14-ci-security-and-supply-chain/06-codeql-and-audit-signatures" + ] + } + ], "needs_prd_ship": [] } diff --git a/docs/work/prds/2026-05-14-ci-security-and-supply-chain.prd.md b/docs/work/prds/2026-05-14-ci-security-and-supply-chain.prd.md index 3bc23ec..cb95492 100644 --- a/docs/work/prds/2026-05-14-ci-security-and-supply-chain.prd.md +++ b/docs/work/prds/2026-05-14-ci-security-and-supply-chain.prd.md @@ -2,7 +2,7 @@ id: 2026-05-14-ci-security-and-supply-chain title: CI security + supply-chain enforcement stack type: prd -status: appoved +status: approved author: danijel created: 2026-05-14 adr: adr-023