Skip to content

ci: Warden security clearance for PR diffs - #3379

Merged
benjaminshafii merged 7 commits into
devfrom
warden/diff-security-clearance
Aug 1, 2026
Merged

ci: Warden security clearance for PR diffs#3379
benjaminshafii merged 7 commits into
devfrom
warden/diff-security-clearance

Conversation

@benjaminshafii

Copy link
Copy Markdown
Member

Summary

  • Adds Sentry Warden (open-source, runs on our own Anthropic key — no Sentry account/DSN involved) to review every PR diff with one custom skill: diff-security-review, which answers a single question: does this diff introduce a new security issue?
  • Adds a "security clearance" flow: when the diff is clean, a dedicated GitHub App posts a review that satisfies the required-review gate in the dev branch ruleset. Humans still review and merge — this only removes the wait-for-review blocker on clean diffs.
  • Deliberately avoids "approval" language everywhere; the concept is a clearance with verdicts clear / flagged.

Why

  • The ruleset on dev requires 1 approving review with require_last_push_approval. Clean PRs (docs, chores, agent-authored fixes) stall waiting for a reviewer. This gives them an independent, automated security gate while keeping humans on the merge button and on anything the gate refuses to clear.

Issue

  • Closes # (none)

Scope

  • warden.toml — single skill, PR trigger (+ local trigger so devs can run warden before pushing)
  • .warden/skills/diff-security-review/SKILL.md — the custom prompt (new security issues only; explicit do-not-report list)
  • .github/workflows/warden.yml — analyze + report on PRs to dev, uploads {head_sha, findings_count} summary artifact
  • .github/workflows/warden-clearance.ymlworkflow_run gatekeeper: grants clearance on clear, revokes on flagged, refuses (guarded) when the PR touches review machinery
  • .gitignore — ignore .warden/logs/

Out of scope

  • Creating the GitHub App and secrets (admin setup, listed below)
  • Auto-fix / fix PRs (--fix, createFixPR) — intentionally off; the clearance identity must never push
  • CODEOWNERS

Testing

Ran

  • actionlint .github/workflows/warden.yml .github/workflows/warden-clearance.yml
  • Live skill test with the real CLI (pnpm dlx @sentry/warden preview-server.ts --json --log, anthropic/claude-sonnet-4-6, team Anthropic key) against three scratch diffs:
    1. planted command injection + reflected XSS
    2. subtly weak version (fixed /tmp output path)
    3. genuinely clean version

Result

  • pass/fail: pass
    • actionlint: 0 issues
    • vulnerable diff -> 2 high findings (exec injection, reflected XSS), correct files/lines -> would be flagged
    • subtle diff -> 1 medium (symlink overwrite via predictable /tmp/preview.png) -> flagged (legitimate catch, not planted)
    • clean diff -> 0 findings -> clear
  • ~$0.09 / ~1 min per run at small-diff size

CI status

  • pass: expected — both new workflows no-op safely until secrets exist (warden.yml needs WARDEN_ANTHROPIC_API_KEY; warden-clearance.yml logs "clearance disabled" without WARDEN_APP_ID)
  • code-related failures: none known
  • external/env/auth blockers: app + secrets not yet created (see below)

Manual verification

Admin setup required first:

  1. Create the GitHub App: npx @sentry/warden setup-app --org different-ai (name e.g. "Warden Clearance"; needs only Pull requests: write, Contents: read; never pushes)
  2. Repo secrets: WARDEN_ANTHROPIC_API_KEY (+ optional WARDEN_MODEL); environment warden-clearance restricted to dev with WARDEN_APP_ID, WARDEN_PRIVATE_KEY
  3. Recommended: turn OFF "Allow GitHub Actions to create and approve pull requests" so the app is the only automated review identity

Then prove end-to-end on live PRs:

  1. Open a test PR with a planted vuln -> expect inline findings, no clearance
  2. Push a clean fix -> expect prior state dismissed, clearance granted, required-review gate green
  3. Open a PR touching .github/** -> expect guarded, no clearance (this PR itself demonstrates that case)

Evidence

  • N/A for video (CI/GitHub-side flow, no app runtime path — fraimz does not apply). Validation is the CLI runs above; repro commands included. Live-PR proof happens right after admin setup via the steps in Manual verification.

Risk

  • An LLM reviewer can be prompt-injected by diff content into staying silent; residual risk accepted because: clearance never covers review-machinery paths, CodeQL + signed commits + thread resolution still gate, fork PRs never auto-clear, and humans still merge.
  • Verdict gate is findings_count == 0 (any severity blocks clearance).
  • Note: once merged, this system refuses to clear PRs like this one (touches .github/ + warden.toml) — review-machinery changes always need a human.

Rollback

  • Delete the two workflows (or the app secrets) — everything no-ops. No app code touched.

Warden runs a single custom skill (diff-security-review) on every PR to
dev and answers one question: does this diff introduce a new security
issue? A workflow_run gatekeeper grants a 'security clearance' review
when the diff is clean, satisfying the required-review gate in the
branch ruleset. Humans still review and merge.

- Clearance identity is a dedicated GitHub App that never pushes, so
  require_last_push_approval holds by construction.
- The gatekeeper executes from the default branch and refuses to clear
  PRs touching .github/, warden.toml, .warden/, or agent skill dirs.
- dismiss_stale_reviews_on_push revokes clearance on every new push;
  Warden re-runs and re-clears if still clean.
- No-ops until WARDEN_MODEL, WARDEN_ANTHROPIC_API_KEY, WARDEN_APP_ID,
  and WARDEN_PRIVATE_KEY secrets are configured.
@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openwork-app Ready Ready Preview Aug 1, 2026 9:27am
openwork-den Ready Ready Preview Aug 1, 2026 9:27am
openwork-den-worker-proxy Ready Ready Preview Aug 1, 2026 9:27am
openwork-diagnostics Ready Ready Preview Aug 1, 2026 9:27am
openwork-landing Ready Ready Preview, v0 Aug 1, 2026 9:27am

- Drop ready_for_review trigger: warden.toml cannot match it, so the
  run would report findings_count=0 without analyzing and wrongly
  grant clearance on draft-to-ready PRs.
- Skip fork PRs in warden.yml (no secrets on forks; clearance already
  fork-blocked downstream) instead of failing red.
- Add timeout-minutes to both jobs and serialize clearance runs per
  branch (queued, not cancelled, so revokes are never killed).
Agent and auxiliary lanes both use gpt-5.6-luna: auxiliary verification
gates which findings survive, so it stays on the same strong model to
avoid false clearances. Secret becomes WARDEN_OPENAI_API_KEY.

Validated locally against the real CLI: injection+XSS diff -> 1 high
(same-root-cause merge), XSS-only diff -> 1 high, clean diff -> 0
findings. 3-7s per run.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 1, 2026

Copy link
Copy Markdown

Deploying openwork with  Cloudflare Pages  Cloudflare Pages

Latest commit: 463ff2a
Status: ✅  Deploy successful!
Preview URL: https://7f438411.openwork.pages.dev
Branch Preview URL: https://warden-diff-security-clearan.openwork.pages.dev

View logs

Comment thread .github/workflows/warden-clearance.yml
Comment thread warden.toml
Addresses Warden's own finding on this PR: the token-minting action
ran from a mutable tag while holding the app private key. All actions
in both workflows are now SHA-pinned.
Resolving review threads via GraphQL requires contents:write on the
GITHUB_TOKEN; without it the report step fails after fix evaluation
and stale findings block merge under thread-resolution rules.
Comment thread .warden/skills/diff-security-review/SKILL.md
Fixes both open CodeQL actions/missing-workflow-permissions alerts;
neither job writes to the repo or GitHub API.
Comment thread .github/workflows/warden-clearance.yml
Comment thread warden.toml
@benjaminshafii
benjaminshafii enabled auto-merge (squash) August 1, 2026 14:15
@benjaminshafii
benjaminshafii merged commit ddde983 into dev Aug 1, 2026
18 checks passed
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