🇨🇳 中文 · 🇯🇵 日本語 · 🇰🇷 한국어 · 🇧🇷 Português · 🇪🇸 Español · 🇩🇪 Deutsch · 🇫🇷 Français · 🇷🇺 Русский · 🇮🇳 हिन्दी · 🇹🇷 Türkçe
Agentic apps merge code that leaks secrets, misses RLS, or accepts prompt injection. purplegate runs red-team probes and a blue-team defense scan on every PR — and fails the build before those merges ship.
uses: sameermohan-git/purplegate@v0.1.0-alpha.9 |
GHCR image →
|
Quickstart →
Agentic-AI apps are a new class of attack surface. Traditional SAST misses the LLM-specific bugs (prompt injection, system-prompt leakage, cross-user data exposure). Traditional security Actions miss the AI-specific supply chain (MCP servers, vendored SDKs, vulnerable corpora). You need both. purplegate is both.
- 🔴 Red-team — eight probes covering every class of agentic-app risk: secrets, SAST, dependencies, IaC / RLS, workflow injection, prompt injection (isolated Claude-as-judge), MCP config risks, and HTTP security headers.
- 🔵 Blue-team — a defense scanner that detects runtime guardrails (LLM Guard, Guardrails AI), rate limiters, and allowlisted findings — then adjusts severity DOWN on findings that are already mitigated. Severity is never raised above the red-team baseline.
- 🟣 Purple-team gate — one CI action, one SARIF report. Critical / High findings fail the build by default; Medium / Low report only. Fully configurable.
| Class | Example finding | Tool |
|---|---|---|
| LLM prompt injection | "Who is Trump?" answered despite a finance-app scope guard | isolated Claude judge via promptfoo |
| System-prompt leakage | Attacker extracts your app's instructions via a crafted payload | same judge, 3-rep agreement |
| Cross-user data disclosure | App references another user's transactions when asked | purple-team dedicated probe |
| Missing Supabase RLS | CREATE TABLE public.transactions without ENABLE ROW LEVEL SECURITY |
custom static check |
| Workflow command injection | ${{ github.event.issue.title }} inside a run: step |
wraps zizmor |
| Live credential in git | a real sk_live_... committed today |
trufflehog --only-verified |
| Vulnerable MCP SDK | mcp-remote pinned < 0.1.16 (CVE-2025-6514, CVSS 9.7), @modelcontextprotocol/server-filesystem < 2025.7.1 (CVE-2025-53109), mcp PyPI < 1.23.0 (CVE-2025-66416), plus 7 more |
vendored GHSA/NVD advisory list in src/probes/mcp.py |
| Generic advice leak | "RRSPs are generally good" from a finance app that should only answer about user data | judge rubric v1 |
Every finding maps to OWASP LLM Top 10 v2025, OWASP Agentic 2026, and MITRE ATLAS v5.4.0 — surfaced in SARIF ruleId so GitHub Code Scanning + downstream SIEM tools filter by framework.
# .github/workflows/security-audit.yml
name: Security Audit
on: [pull_request, workflow_dispatch]
permissions:
contents: read
security-events: write
pull-requests: write
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: step-security/harden-runner@<sha>
with: { egress-policy: audit }
- uses: actions/checkout@<sha>
with: { fetch-depth: 0, persist-credentials: false }
- uses: sameermohan-git/purplegate@v0.1.0-alpha.9 # or pin by 40-char commit SHA
with:
config: .purplegate/config.yml
fail-on: high
llm-provider: anthropic
llm-api-key: ${{ secrets.AUDIT_ANTHROPIC_KEY }}
target-url: ${{ secrets.STAGING_API_URL }}
# Required: Docker container actions don't get github.token in
# their env: namespace, so the consumer passes it explicitly.
github-token: ${{ secrets.GITHUB_TOKEN }}
# Optional: keep the JSON / SARIF / Markdown reports for offline analysis.
# purplegate writes them to <workspace>/.purplegate-reports/ during the run.
- uses: actions/upload-artifact@<sha>
if: always()
with:
name: purplegate-reports
path: .purplegate-reports/
if-no-files-found: warnThen add .purplegate/config.yml — see docs/CONFIG.md for the full schema. Full walkthrough in docs/QUICKSTART.md.
┌─ Consumer repo ─────────────────────────┐
│ .purplegate/config.yml │
│ .purplegate/allowlist.yml │
│ .github/workflows/security-audit.yml ──┼─▶ purplegate Docker image
└─────────────────────────────────────────┘ │
▼
┌───────────────────────────────────────┐
│ Orchestrator │
│ ├─ secrets (gitleaks + th) │
│ ├─ sast (Semgrep + AST) │
│ ├─ deps (osv-scanner) │
│ ├─ iac (Checkov + RLS) │
│ ├─ workflows (zizmor) │
│ ├─ prompt_injection ──▶ isolated │
│ │ Claude judge│
│ ├─ mcp (static scan) │
│ ├─ sbom (Syft) │
│ └─ headers (httpx) │
├───────────────────────────────────────┤
│ Blue-team defense scanner │
│ (severity adjuster — never raises) │
├───────────────────────────────────────┤
│ Report (SARIF + Markdown + JSON) │
│ Gate (fail-on: critical / high / …) │
└───────────────────────────────────────┘
This tool's single most important property is that it does not become the attack vector it's meant to defend against. Consumers do not have to trust us — they can verify.
Shipping today (current tag v0.1.0-alpha.9)
- Multi-arch Docker image (
linux/amd64+linux/arm64) published toghcr.io/sameermohan-git/purplegate. Pin by digest:uses: sameermohan-git/purplegate@v0.1.0-alpha.9 # tag works because images are cosign-signed # or, for direct image consumption: # docker pull ghcr.io/sameermohan-git/purplegate@sha256:<digest>
- Cosign keyless signature on every published image (Sigstore + Rekor transparency log).
- SLSA Level 3 build provenance via
actions/attest-build-provenance— verify before first use:Successful verification proves the image was built bygh attestation verify oci://ghcr.io/sameermohan-git/purplegate:v0.1.0-alpha.9 \ --owner sameermohan-git
release.yml@refs/tags/v0.1.0-alpha.9on agithub-hostedrunner. - SBOMs (SPDX + CycloneDX) signed via
cosign sign-blobkeyless — bothsbom.spdx.jsonandsbom.cdx.jsonship alongside a.sigstore(Sigstore new-bundle format: signature + Fulcio cert + Rekor inclusion proof in one file) on every GitHub Release. Verify with:cosign verify-blob \ --bundle sbom.spdx.json.sigstore \ --new-bundle-format \ --certificate-identity-regexp 'https://github.com/sameermohan-git/purplegate/.github/workflows/release\.yml@.+' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ sbom.spdx.json - Dockerfile base images pinned by sha256 digest —
debian:12-slimandpython:3.12.7-slim-bookwormresolve to a fixed multi-arch index so re-builds are bit-identical until Renovate proposes a digest bump. - Workflow permissions scoped at job level — top-level is
contents: read; only thebuildjob inrelease.ymlopts in to the four write scopes it needs (contents,packages,id-token,attestations). - CodeQL static analysis runs on every PR and push to
mainvia.github/workflows/codeql.yml, queries:security-extended. Findings appear in Security → Code scanning. - Every scanner binary inside the image is pinned to a specific version and SHA256-verified against upstream checksums (gitleaks, trufflehog, syft, actionlint) or a locally-computed SHA256 anyone can reproduce (osv-scanner). Python scanners (semgrep, checkov, zizmor, pip-audit) are pinned to exact versions via isolated pipx venvs.
- Every third-party
uses:in our own workflows pinned by 40-char commit SHA — never by tag. Mar 2025 (tj-actions) and Mar 2026 (trivy-action) taught us why. - License manifest for every bundled binary at
LICENSE-3RD-PARTY.md, including the AGPL-3.0 trufflehog posture.
Hardening targets for v1.0
- OSSF Scorecard ≥ 8/10 (live score badge above; current ~6/10, the remaining gap is structural — solo maintainer Code-Review, project-age Maintained, OpenSSF Best-Practices badge).
- Branch protection on
mainrequiring signed commits + 2 reviewers. - Migration of the Action's own
uses:lines to the latest signed SHAs (Dependabot is currently proposing them). - pip dependencies pinned by hash (
--require-hashes) inside the Dockerfile.
Full policy in docs/SUPPLY_CHAIN.md. Threat model in THREAT_MODEL.md.
| Severity | Default gate | Examples |
|---|---|---|
| 🔴 Critical | Fails CI | Verified live credential · public table without RLS · workflow command injection · vulnerable MCP SDK · verified system-prompt extraction |
| 🟠 High | Fails CI | Route without auth · generic-advice leak · CVE ≥ 7.0 · missing runtime LLM guardrails |
| 🟡 Medium | Reports only | Missing CSP · unpinned non-MCP dep |
| 🟢 Low | Reports only | Suboptimal Referrer-Policy |
Override via the fail-on: input. Allowlist entries need a reason, an acknowledged_by, and an expires within 365 days — see docs/SUPPRESSIONS.md.
Baked into the tool because supply-chain choices are security choices:
| Project | Reason |
|---|---|
tj-actions/* · reviewdog/action-setup / -shellcheck / -staticcheck / -ast-grep / -typos / -composite-template |
CVE-2025-30066 / CVE-2025-30154 (Mar 2025 force-push compromise) |
aquasecurity/trivy-action by tag |
Mar 2026 tag force-push. The Trivy binary is fine; we invoke it directly from our vendored image. |
tfsec |
Deprecated, absorbed into Trivy — use Checkov. |
protectai/rebuff |
Archived May 2025. |
| HarmBench / AdvBench corpora in CI | MIT but contain toxic content. |
- v0.1 — scaffold: orchestrator + 9 probes + blue-team + SARIF + gate
- v0.2 — 37-test fixture suite + self-test CI
- v0.3 — pinned Dockerfile binaries + signed GHCR image
- v0.4 — promptfoo integration with
owasp:llmpreset + Lakera Mosscap/Gandalf corpora - v0.5 — Checkov wire-up + live Supabase catalog drift check
- v0.6 — Consumer-specific SARIF suppression helpers
- v1.0 — Marketplace publish, Scorecard ≥ 8, SLSA L3 signed, docs complete
PRs welcome once v1.0 is cut; until then we're stabilising the interface. Security issues → SECURITY.md. Probe additions → open an issue first to discuss severity + taxonomy mapping.
MIT. See LICENSE.