⚠ Pre-public draft (Phase α author self-verify clean, awaiting promotion gate). PRIVATE repo until user-explicit promotion. PUBLIC flip criteria:
docs/adr/0006-public-flip-criteria.md.
In plain words: this is a command-line tool that reads your TypeScript / JavaScript / Python source code, asks a small AI model (running on your own laptop, no cloud bill) to look for security problems, and writes back not just the problem list but a draft fix you can review. It costs $0/month by default, needs no API key, and runs offline once you have the local model installed.
For engineers: Local-first AI-agent harness for defensive AppSec on TS/JS/Python codebases — threat modeling (STRIDE + OWASP LLM Top 10) + SAST (OpenGrep + Bandit) + SCA (OSV-Scanner) + LLM-driven false-positive triage + patch suggestion, emitting SARIF 2.1.0 + CycloneDX VEX. Default LLM provider is Ollama gemma3:4b (~3.8 GB local), optional --use-claude-code flag spawns your own claude CLI.
Three stages, source-code level, $0/month default:
- Threat model generation — STRIDE + OWASP LLM Top 10 + OWASP ASI mapping from your repo, output as editable YAML/JSON.
- Vulnerability identification — SAST (OpenGrep + Bandit) + SCA (OSV-Scanner) + LLM-driven enrichment (false-positive triage, severity re-rank, exploit context).
- Patch suggestion — LLM-generated remediation candidate + re-scan validation + SARIF 2.1.0 output + CycloneDX VEX. (Cosign signing + SLSA L2 attestation of patch artifacts = Phase β scope, not implemented in Phase α.)
Daybreak-style OSS replicas don't yet cover the intersection of all five:
| axis | this project | nearest alternative |
|---|---|---|
| analysis target | source code (file:line) | Tachi = architecture-only (Mermaid/PlantUML) |
| posture | defensive (SAST/SCA + patch) | pwnkit / bughunter-ai / numasec = offensive pentest |
| LLM | Ollama default ($0/month) | repomind = Gemini cloud-required |
| language | TS / JS / Python | Buttercup = C / Java only |
| output | confidence-calibrated SARIF + patch | most OSS output binary yes/no, no patch |
The gap I noticed. In 2026 there are good OSS tools for each piece of defensive AppSec on a TS/JS/Python codebase — Tachi for architecture-level threat modeling, OpenGrep and Bandit for SAST, OSV-Scanner for SCA, and several research projects for LLM-driven enrichment. What I could not find was one OSS tool that did all of it at the source-code level, defensively (not as offensive pentest), running on a local LLM with no monthly bill, and emitting a confidence-calibrated SARIF plus a draft patch. Five axes that should compose, and none of the existing OSS tools composed all five. That five-axis gap is the wedge documented in docs/adr/0001-prior-art-audit.md.
How I approached it. Every adopted dependency and every design choice has an ADR (docs/adr/0001–0008) recording the alternatives considered and the trade-offs accepted. Where prior art existed and I could reuse the substance instead of the code, I did — STRIDE-GPT's threat-model prompt templates are ported via decomposed prior art (ADR-0002) rather than vendored as a runtime dep. Where I made a non-trivial scaling decision (Ollama gemma3:4b as the default model), I sourced the within-family monotonicity claim to primary papers from Google DeepMind, Alibaba, and Meta (ADR-0008) rather than asserting it as opinion. Where I had to pick between two mature SCA tools, I rejected one explicitly with five reasons in the ADR (ADR-0003) rather than supporting both.
What this exercise validated. Three things turned out to be worth defending. First, the $0/month + no credit card constraint is not a limitation pretending to be a feature — it is a supply-chain discipline demonstration, and the customer-deployment upgrade path is wired on day one (ADR-0008 §Customer deployment context). Second, the confidence-calibrated finding schema (ADR-0005) gives a reviewer something concrete to triage against, where most OSS finding outputs are binary yes/no. Third, Phase α intentionally stops before sandboxed exploit execution — that work ships as a separate repo (agentic-appsec-exploit-lab) so the kernel-share concerns of running attacker code are isolated rather than papered over (ADR-0004). The 84/84 test-suite pass on the 3-OS CI matrix and ADR-0001 through ADR-0008 all in Accepted status are the verifiable artifacts of those three commitments.
- Phase α author self-verify: 7/7 criteria PASS on current HEAD (
docs/verify/phase-alpha-round-1-self-verify.md) - Test suite: 84/84 PASS on 3-OS CI matrix (Ubuntu / macOS / Windows), coverage line 59.89% / branch 68.44% / function 70.23%
- ADRs: 0001–0008 all Accepted (
docs/adr/) - Awaiting user-explicit promotion gate for PUBLIC flip per
docs/adr/0006-public-flip-criteria.md - Spec status: Stage 1 Discovery + Stage 2 EARS Acceptance Criteria complete (
docs/spec.md) - Phase β (sandboxed exploit lab) ships as a separate repo
agentic-appsec-exploit-laband is out of scope here
| Layer | Choice | Why |
|---|---|---|
| Runtime | Node.js 20 LTS + pnpm 10+ | npm-ecosystem standard, free-tier CI compatible |
| Language | TypeScript (strict, ESM) | type safety, single-binary compile via tsc |
| CLI framework | commander + did-you-mean | minimal deps, sysexits-compliant exit codes |
| Schema validation | zod + ajv (+ ajv-formats) | runtime + JSON Schema validators for SARIF / VEX / threat-model |
| Test runner | vitest (ESM native) | 84/84 PASS on 3-OS CI |
| LLM (default) | Ollama gemma3:4b (~3.8 GB) |
local, $0/month, no credit card, no network egress |
| LLM (optional) | claude-code CLI spawn |
uses your own Claude Code subscription; this tool holds no API key |
| SAST | OpenGrep (LGPL-2.1) + Bandit (Apache-2.0) | TS/JS via OpenGrep, Python via Bandit, both free-tier |
| SCA | OSV-Scanner (Apache-2.0) | Google OSV.dev database, no cloud key required |
| Output formats | SARIF 2.1.0 + CycloneDX VEX | tool-vendor neutral standards |
| Threat-model prompts | STRIDE-GPT decomposed (MIT) | ADR-0002 — prompt templates ported, no Python runtime dep |
| CI | GitHub Actions 3-OS matrix (Ubuntu/macOS/Windows) | free tier, no payment required |
Full rationale per dependency: docs/adr/ (8 ADRs, all Accepted).
Requirements: Node.js 20+, pnpm 10+, optional Ollama (local LLM, $0/month).
git clone https://github.com/leagames0221-sys/agentic-appsec-pilot.git
cd agentic-appsec-pilot
pnpm install
pnpm run build # compiles to dist/
node dist/cli/index.js --helpFor full scan + enrichment, also install:
- Ollama (Windows 10+): https://ollama.com/download/windows — then
ollama pull gemma3:4b - OpenGrep (TS/JS/Python SAST): https://github.com/opengrep/opengrep/releases
- Bandit (Python SAST):
pip install bandit - OSV-Scanner (SCA): https://github.com/google/osv-scanner/releases
# Stage 1: generate STRIDE + OWASP LLM/ASI threat model
agentic-appsec threat-model ./my-repo \
--app-type "Generative AI application" \
--provider ollama \
--output threat-model.json
# Stage 2: SAST + SCA scan with LLM enrichment, emit SARIF + VEX
agentic-appsec scan ./my-repo \
--provider ollama \
--enrich \
--output findings.sarif \
--vex findings.vex.json
# Stage 3: patch suggestion (uses your own Claude Code subscription via spawned CLI; no API key held by this tool)
agentic-appsec patch findings.sarif \
--repo ./my-repo \
--use-claude-code \
--output patch-suggestion.jsonAll three commands default to --provider mock (deterministic, no LLM call, no network egress). Set --provider ollama for local-LLM enrichment, or --use-claude-code to call your existing Claude Code subscription via the CLI.
First-run behavior with no scanners installed: scan gracefully degrades — if OpenGrep / Bandit / OSV-Scanner are absent from PATH, it logs tool status: ...=not-installed on stderr and emits an empty findings: [] SARIF (exit code 0). This is by design (the CLI never crashes on missing optional tooling); install at least one scanner from the Install section above for actual output.
Cost contract: this tool holds no API key and makes no paid-API direct calls (see ADR-0007). --use-claude-code spawns your locally-installed claude CLI, which uses your own Claude Code subscription — billing flows through your existing Anthropic account, not through this tool. --provider ollama and --provider mock are fully offline and free.
The captures below are reproducible from a fresh clone: run the literal commands and you will get byte-similar output (timestamps and UUIDs differ). The literal files live under docs/demo/ so a reviewer can diff against their own run.
| Command | Screenshot |
|---|---|
agentic-appsec --help |
help.png |
cat docs/demo/sample-scan.sarif (post-scan SARIF) |
scan.png |
agentic-appsec threat-model . --provider mock |
threat-model.png |
Rendered from raw stdout/stderr by docs/demo/cli/render.py
(Pillow + MS Gothic, no network egress). The scan PNG renders the literal
sample-scan.sarif — produced by an empty scan
because OpenGrep / Bandit / OSV-Scanner were not installed in the Phase α
fixture run; with the three SAST/SCA tools installed, the runs[0].results
array populates with detector findings. The threat-model PNG renders the
literal mock-provider output (300 bytes, deterministic UUID seed).
To regenerate the PNGs locally:
NO_COLOR=1 node dist/cli/index.js --help > docs/demo/cli/help.txt 2>&1
cp docs/demo/sample-scan.sarif docs/demo/cli/scan.txt
NO_COLOR=1 node dist/cli/index.js threat-model . \
--app-type "Agentic AI application" \
--authentication "JWT bearer + per-route role check" \
--internet-facing yes --sensitive-data "source code" \
--provider mock > docs/demo/cli/threat-model.txt 2>&1
# Render PNGs (system Python >= 3.10 + Pillow)
python docs/demo/cli/render.pyagentic-appsec --help (docs/demo/help.txt):
Usage: agentic-appsec [options] [command]
Local-first AI-agent harness for defensive AppSec on TS/JS/Python codebases.
Threat-model + SAST/SCA + patch-suggestion + SARIF 2.1.0, runs on Ollama by
default.
Commands:
threat-model [options] <repo> Generate STRIDE + OWASP LLM/ASI threat model
for a repo.
scan [options] <repo> Run OpenGrep + Bandit + OSV-Scanner, emit SARIF
2.1.0 + optional CycloneDX VEX.
patch [options] <sarif-path> Generate patch suggestion for a finding in a
SARIF file.
Threat-model output (docs/demo/sample-threat-model.json, --provider mock against an empty repo — used here to show the schema; with --provider ollama against a real repo, the threats array contains STRIDE-classified findings):
{
"schemaVersion": "1.0.0",
"id": "tm-<uuid>",
"target": "<repo path>",
"generatedAt": "<ISO-8601 timestamp>",
"threats": [],
"improvementSuggestions": [
"Provider \"mock\" did not return parseable JSON. Switch to ollama or claude-code-cli for real output."
]
}SARIF 2.1.0 scan output (docs/demo/sample-scan.sarif, validates against the OASIS SARIF JSON Schema referenced via $schema):
{
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
"version": "2.1.0",
"runs": [
{
"tool": {
"driver": {
"name": "agentic-appsec-pilot",
"version": "0.1.0",
"informationUri": "https://github.com/leagames0221-sys/agentic-appsec-pilot",
"rules": []
}
},
"results": []
}
]
}A terminal-recording screenshot (PNG / asciinema cast) of an end-to-end run against a known-vulnerable fixture repo will be added once the user-explicit PUBLIC promotion gate is cleared; the textual captures above are the in-repo verifiable baseline.
This repository operates under a self-imposed $0/month + no credit card constraint as a supply-chain discipline demonstration. That constraint dictates the default Ollama gemma3:4b (~3.8 GB install footprint) — small enough to run on a consumer laptop, free to use, no payment required.
Customer deployments are not bound by that constraint. Three upgrade paths are wired in on day one:
| Goal | Mechanism | Tier |
|---|---|---|
| Larger local Ollama model (private, on-prem) | OllamaProvider({ model: 'qwen2.5-coder:14b' }) |
open-source SOTA |
| Frontier quality via your own subscription | --use-claude-code flag |
Claude Sonnet / Opus |
| CI / offline / deterministic | --provider mock (default) |
none |
Within-family size↔quality is monotonic per Gemma 3 Technical Report Table 18 (4B→27B: +16.5 HumanEval pass@1) and Qwen2.5-Coder Technical Report ("positive correlation between model size and model performance"). The portfolio default is the floor of useful quality, not the ceiling. Full rationale + size budget + customer deployment recipe: docs/adr/0008-default-llm-choice-and-customer-deployment.md.
MIT. See LICENSE. Third-party attribution: LICENSE-third-party.md.
- STRIDE-GPT (https://github.com/mrwadams/stride-gpt) — STRIDE + OWASP LLM/ASI prompt templates adapted via decomposed prior art (ADR-0002)
- OpenGrep (https://github.com/opengrep/opengrep) — SAST engine, LGPL-2.1
- Bandit (https://github.com/PyCQA/bandit) — Python SAST, Apache-2.0
- OSV-Scanner (https://github.com/google/osv-scanner) — SCA, Apache-2.0
- Sigstore cosign (https://github.com/sigstore/cosign) — referenced for Phase β patch-artifact signing (verify-blob + SLSA L2 attestation); not yet wired in Phase α