A security auditor where the tool owns no model — the calling agent is the LLM.
Static analysis tools fall into two camps, both broken for agent-first use:
-
Heavyweight SAST (Semgrep, CodeQL, Strix): powerful, but they own the whole pipeline — the rules, the triage, sometimes the LLM call that filters false positives. They need a server, a database, a Docker sandbox, or an API key. An agent that already is an LLM ends up paying for a second LLM inside the tool, or fighting the tool's opinions about what matters.
-
Grep-in-a-loop: cheap, but no structure — no stable finding IDs, no persisted verdicts, no CI integration, no severity taxonomy. Every run starts from zero; the agent re-triages the same false positives forever.
The tool owns the deterministic part. The agent owns the judgment.
rules.json (the moat) --> secure binary (the engine) --> JSONL / SARIF findings
|
calling agent reads + reasons (its own LLM)
|
secure verdict <id> keep|drop --> persisted store
secure is a regex engine with stable finding IDs and a persisted verdict
store. It never calls an LLM, never holds an API key, never makes a network
call to a model provider. The agent driving secure (Devin, Claude Code, any
coding agent) already has a model — it reads the findings, reasons with its own
context, and writes back a verdict that survives across scans. Same BYOK split
as grepapi's /v1/brief: the tool returns structured data, the operator's
own LLM completes the reasoning.
| Decision | Consequence |
|---|---|
| No LLM client in the tool | No API key, no budget, no network call. The agent's own model is the filter. |
rules.json is data, not code |
Add detections without recompiling. The rule pack is the actual product. |
Stable finding IDs (sha256(rule|file|line)) |
Verdicts persist across scans, across machines, across agents. |
| SARIF 2.1.0 output | Drops into GitHub Code Scanning, VS Code, any SARIF consumer. CI-native. |
--diff / --diff-base |
Scans only changed files — 0.23s on a 10k-file repo with 8 changed files. The actual fix for CI latency. |
| No Docker, no browser, no sandbox | Read-only static analysis. Never executes target code, never mutates the repo. |
- Not an agent loop. No tool-calling LLM, no multi-step reasoning inside the
tool. The agent that runs
securedoes the reasoning;securedoes the scanning. - Not a report generator.
--hartprints guidance for the agent to author and publish its own HTML report with its own model. The tool doesn't bake prose-generation judgment into a binary that otherwise owns none. - Not an index or database. The filesystem is scanned fresh every time — freshness over sophistication. The one piece of state that persists is the verdict store, and only because it's the agent's memory, not a cache the tool invents.
- Not AST-based (yet). Regex over lines is v0 — deterministic, fast, and good enough for the patterns that matter most (secrets, injection, weak crypto, TLS bypass). Tree-sitter precision is a possible v2, but it breaks the single-binary KISS model and the agent-verdict filter already handles false positives. KISS first; complexity only when it earns its keep.
The scanner is ~500 lines of MFL. rules.json is the actual product. The
detection quality lives in the rule pack — the patterns, the CWE tags, the
severity calibration, the language coverage. The engine is interchangeable; the
rules are the accumulated knowledge. This is why the rules are plain data read
fresh from disk every run: the moat must be editable without recompiling the
engine.
- v0 — deterministic regex engine: JSONL findings, stable IDs, exit codes.
- v1 — agent-managed verdicts:
secure verdict keep|drop, persisted store,--show-all. - v2 — performance:
--diff/--diff-basefor CI,--workers Nfor parallel full scans. - v3 — CI integration: SARIF 2.1.0 output, reusable GitHub Action,
security-events: write. - v4 — rule pack expansion: 84 CWE-tagged detections across 16 languages.
- v5 — IaC + frameworks + more CWE: 119 rules across 17 languages — Terraform/Kubernetes/Docker IaC misconfigurations, Django/Flask/FastAPI/Spring framework rules, CSRF/mass-assignment/ReDoS/log-injection/weak-TLS.
- v6 — CI/CD + deps/config + auth/session: 147 rules across 24 languages — GitHub Actions pipeline security (pull_request_target, write-all, secrets in run, script injection), dependency hardening (unpinned requirements/package.json/Cargo.toml/Gemfile, config file secrets), auth/session (JWT alg:none in 4 languages, unsalted hashes, bcrypt low rounds, session fixation, plaintext password scheme, weak password length, non-crypto random for secrets).
- v7 — API injection + crypto + info disclosure: 188 rules — GraphQL (introspection, depth/complexity limits), WebSocket origin, prototype pollution, NoSQL/LDAP/template injection (SSTI), SSRF expanded (urlopen, axios, fetch, file_get_contents), crypto misuse (ECB, hardcoded IV/salt, short AES/RSA keys, PKCS1v1.5 padding, deprecated crypto.random, MD5/SHA1 for passwords in 5 languages), PHP injection patterns (SQL/eval/include/unserialize with $_GET/$_POST), info disclosure (debug code, hardcoded IPs, HTTP URLs, verbose errors, file upload).
- v8 — language deep dives + mobile + cloud: 235 rules across 25 languages — C/C++ (format strings, scanf, alloca, strtok, atoi), Rust (transmute, from_raw, raw pointers, ptr::read/write), Go (unsafe.Pointer, cgo, Sprintf-SQL, no-TLS), Java (JNDI/Log4Shell, SpEL, XPath, SSRF, TransformerFactory), Android (debuggable, cleartext, backup, WebView, exported), iOS (ATS, plist secrets), Rails (CSRF skip, render SSTI, attr_accessible), Laravel (DB::raw, Blade {!!}, env()), AWS (S3 public-read, IAM wildcard, hardcoded creds), database (Redis EVAL, ES query_string, ORDER BY), network (FTP/Telnet/SMTP cleartext), supply chain (GHA @main, npm postinstall, pip git).
- Baseline mode: snapshot current findings as accepted state; future runs surface only new findings. Pairs with
--diff— the path to usable static analysis in real codebases (you can't fix 500 legacy issues at once). - Community rule packs: split
rules.jsoninto per-language or per-framework packs that can be contributed independently of the engine. - Tree-sitter precision (maybe): AST-based matching for fewer false positives. High effort, breaks single-binary KISS — only if the agent-verdict filter proves insufficient in practice.
- KISS. The 15-lines-of-bash lesson: a deterministic regex engine over the filesystem finds most of what a heavyweight agent finds, with none of the infrastructure, cost, or attack surface. Complexity only when it earns its keep.
- Agent-first. Output is JSONL/SARIF, not prose. No TUI, no dashboard. The tool is shaped for an agent to pipe, parse, and act on — not for a human to stare at.
- BYOK. The tool never duplicates the LLM the operator already has. No model client, no API key, no network call to a provider. The agent is the LLM.
- Data over code. The rules are the moat; the engine is interchangeable. Edit detections without recompiling.
- Freshness over sophistication. No index, no database, no run journal. Scan the filesystem every time. The one persistent state is the agent's own verdict memory.