https://github.com/dmdhrumilmistry/security-harness
security-harness is an open-source Claude Code plugin (also packaged for Gemini CLI, Codex and opencode) that orchestrates subagents — sh-recon, sh-hunter, sh-chainer, sh-verifier, sh-reporter — through a static-analysis pipeline covering 15 vulnerability classes (IDOR/BOLA, SQLi, XSS, SSRF, injection, auth, deserialization, path traversal, secrets, CSRF, XXE, open redirect, crypto, race conditions, file upload), producing README/JSON/SARIF/HTML/PDF reports with PoCs, CVSS and CWE/OWASP mappings. It also ships sh-pr-review, a per-PR merge-gate skill that posts inline findings and a commit status scoped only to code the PR itself introduced or aggravated, with caching, dedup via fingerprints, and a scheduled GitHub Action that keeps its per-class knowledge bases fresh by having one AI agent draft updates from allowlisted sources (OWASP, PortSwigger Research, CWE/CAPEC, HackerOne) while a separate adversarial reviewer agent fails-closed against prompt injection before auto-merge.
What it is
security-harness also ships two concrete operational mechanisms the summary only gestures at: a cache/invalidation design for sh-pr-review, and a two-stage GitHub Actions pipeline (update-knowledge-base.yml → kb-review-and-merge.yml) that auto-merges KB updates.
How it works
sh-pr-review's merge-gate behavior rests on a tiering + caching scheme: Phase 1 reads fingerprints already posted on the PR before any hunter runs, so duplicate findings are never re-paid-for, only re-posted. A local sh-review-cache (OS cache dir, never the repo) hashes files, findings, and verdicts; the cache key includes every sh-kb-* knowledge base, model identity, skill version, and a 7-day TTL — any KB update invalidates all cached findings so a stale "clean" can't suppress a newly-detectable bug. A special case: if a base-branch merge changes the head SHA but touches none of the PR's own files or anything its findings depend on, the previous verdict is re-stamped onto the new SHA with zero agents launched.
The KB-freshness pipeline separates writing from shipping across two workflow files: stage 1 (update-knowledge-base.yml, scheduled every other day or manual) runs a generator agent that edits KB files from an allowlist (.github/kb-update/trusted-sources.md) with no shell and no push access, opens a PR on automated/kb-update, then dispatches stage 2 via workflow_dispatch — chosen specifically because a PR opened by GITHUB_TOKEN cannot trigger a normal pull_request event. Stage 2 runs an adversarial reviewer agent with no web and no shell that fails closed (any suspicion, uncertainty, or missing verdict file → REJECT) and only on APPROVE does a separate merge job run, which also refuses any PR that's closed, from a fork, or outside automated/*.
flowchart LR
A[Generator agent edits KB] -->|opens PR, automated/kb-update| B[workflow_dispatch handoff]
B --> C{Adversarial reviewer}
C -->|REJECT: suspicious/uncertain/no verdict| D[blocked]
C -->|APPROVE| E{Merge job checks}
E -->|fork / closed / wrong branch prefix| D
E -->|passes| F[Auto-merge to main]Preconditions / Affected
Requires repo setting "Allow GitHub Actions to create and approve pull requests," and a credential secret (CLAUDE_CODE_OAUTH_TOKEN, ANTHROPIC_API_KEY, OPENAI_API_KEY, or GEMINI_API_KEY). For PR review, the running user needs write access to the target repo, checked before analysis so a 403 surfaces up front instead of after.
What to do
- Set branch protection on
mainrequiring a PR and an approving review — this is honored by GitHub's own auto-merge path, so the KB-update pipeline queues for a human instead of landing unattended. - For hardening, set
KB_AGENTandKB_REVIEW_AGENTto two different model engines, so a prompt injection tuned for one is less likely to also fool the other. - Inspect cache behavior via
run.md, which records per phase what was launched, reused, or skipped — use--no-cacheto disable or--refresh-cacheto re-baseline if this drifts.
Caveats
The merge job falls back to an immediate (non-queued) merge only on repos where GitHub's native auto-merge is off, which weakens the human-in-the-loop guarantee unless branch protection is separately configured. The page states DAST runtime confirmation is still an unshipped, optional future item.
Technologies named here
- security-harness
- sh-router
- sh-security-review
- sh-kb-*
- sh-recon
- sh-hunter
- sh-chainer
- sh-verifier
- sh-reporter
- Graft
- Syft
- Grype
- Trivy
- OSV-Scanner
- wkhtmltopdf
- pandoc
- sh-pr-review
- sh-pr-post.py
- sh-review-cache
- sh-metrics.py
- ai-agent
- update-knowledge-base.yml
- kb-review-and-merge.yml
- Gemini CLI extension
- sync-agent-skills.py
- Claude Code
- Gemini CLI
- SARIF
- SBOM
- CVSS
- GitHub Actions
Techniques named here
- Source-to-sink data flow tracing
- Attack chaining/escalation
- Static Analysis
- PoC/payload construction and verification
- SBOM and dependency CVE scanning
- Vulnerability classes covered
- PR triage tiering
- Fingerprint-based deduplication
- pr_impact classification
- IDOR
- BOLA
- SSRF
- Path traversal
Related in AppSec
Flowise MCP Security Bypass Enables RCE
Inside an Oracle Database SQL Injection Attack | Huntress
OASIS
