An agentic vulnerability analyzer that uses a team of AI agents to investigate source code and binaries for security vulnerabilities. Unlike traditional static analysis tools that rely on fixed rules, skwaq's agents reason about code like experienced security researchers β tracing data flows, mapping attack surfaces, and debating exploitability.
What makes it unique: skwaq improves itself. A built-in benchmark harness (Skwaq Gym) measures detection accuracy against industry benchmarks, and a self-improvement loop uses AI agents to analyze their own failures and propose better investigation strategies.
From spec to self-improving system
Can AI agents bootstrap themselves into a competitive vulnerability detection system?
Started with a brief specification for a "vulnerability investigation copilot":
The question: Can a short spec + AI bootstrapping produce a competitive detection system from scratch?
Built a code property graph in LadybugDB β skwaq's own analysis code extracts and stores:
Graph capabilities unlock:
Agents query this graph with specialized tools β enabling analysis that static patterns alone cannot achieve.
Started with a small set of agents. The self-improvement loop was given instructions to experiment with new agentic workflows and agent types β and evolved the pipeline organically as it learned what worked:
Powered by RustyClawd β a Rust-based agentic LLM framework
Integrated agent-kgpacks β portable knowledge graph packages that encode:
Purpose: Agents load domain-specific knowledge packs to bootstrap expertise without re-learning from scratch on every run. Accumulated institutional knowledge becomes portable and reusable across runs and agent instances.
Added durable agent memory (adapted from amplihack-memory-lib's cognitive memory model) so investigation insights persist across cycles:
Built the gym improve command β agents started teaching themselves:
Four proposal types (in preference order): AGENT_PROMPT β TAINT_RULE β CWE_MAPPING β NEW_PATTERN
It became critical to ensure improvement loops don't "build to the benchmark" β overfitting to specific test cases rather than improving general detection.
Response: built a specialized overfitting-reviewer agent with explicit rejection criteria:
Each vulnerability investigation passes through five layers, from fast pattern matching to deep semantic reasoning:
Why five layers? Fast layers (1-2) handle high-volume pattern detection. Slow layers (4-5) apply expensive LLM reasoning only where needed. Context validation (3) eliminates noise before agents see it.
Cost control: Pattern-only mode skips layers 4-5, enabling large-scale batch evaluation. Agentic mode activates all layers for high-confidence findings.
All analysis operates on a code property graph stored in LadybugDB β functions, call edges, taint flows, data sources, data sinks, and symbols. Agents query this graph with specialized tools.
get_taint_paths β source-to-sink flow tracesget_cross_file_calls β cross-module call edgesget_data_sources β user-controlled inputsget_imports β dependency analysisbuild_analysis_context() β 2-hop call graph + string refsAll 18 agents and the pipelines they participate in:
| Type | Effect |
|---|---|
| AGENT_PROMPT | Teaches agents new investigation strategies |
| TAINT_RULE | Adds source/sink definitions to dataflow engine |
| CWE_MAPPING | Fixes scoring / classification |
| NEW_PATTERN | Regex detection (last resort only) |
| Suite | Source | Cases | Languages | Focus |
|---|---|---|---|---|
| Juliet | NIST | 54,488 | C/C++ | 116 CWEs, synthetic variants |
| OWASP Benchmark | OWASP Foundation | 2,740 | Java | Web app vulns (XSS, SQLi, crypto) |
| CyberSecEval | Meta | 578 | C/C++, Python | Real-world vuln patterns |
| CGC | DARPA | 300 | C | Real challenge binaries (patched/unpatched) |
| CyberGym | UC Berkeley | 3,014 | C/C++ | Real OSS-Fuzz CVEs from 188 projects |
| Fixtures | skwaq team | 99 | Mixed | Regression suite |
Why multiple suites? Juliet tests synthetic breadth (116 CWEs). OWASP tests Java web semantics. CSE tests real-world C patterns. CyberGym tests real CVEs. No single suite validates the full capability envelope.
| Suite | Cases | F1 | Precision | Recall | TP | FP | FN | TN |
|---|---|---|---|---|---|---|---|---|
| Fixtures | 99 | 93.7% | 98.1% | 89.3% | 103 | 2 | 12 | 11 |
| CSE | 578 | 91.8% | 100% | 84.8% | 434 | 0 | 78 | 0 |
| Juliet | 1,000 | 88.8% | 100% | 79.9% | 798 | 0 | 201 | 1 |
| OWASP | 500 | 93.8% | 100% | 88.3% | 228 | 0 | 30 | 242 |
| CGC | 226 | 89.8% | 100% | 81.5% | 388 | 0 | 88 | 0 |
| CyberGym | 100 | 71.9% | 100% | 56.1% | 64 | 0 | 50 | 0 |
| CWE | Before | After | Method |
|---|---|---|---|
| CWE-401 memory leak | 0% | 99% | Agent-recommended callocβResourceLeak mapping |
| CWE-614 secure cookie | 0% | 100% | setSecure(false) + semantic class fix |
| CWE-78 cmd injection | 37% | 70% | spawn family (agent-identified taint rules) |
| CWE-22 path traversal | 41% | 65% | java.io.File* qualified name fix |
| CWE-79 XSS | 46% | 63% | getWriter().format/append (agentic cycle) |
| Race conditions | 33% | 100% | signal handler + thread (agentic cycle) |
Pattern: The loop finds the specific taint source/sink or agent reasoning gap that caused misses, proposes a targeted fix, and the overfitting-reviewer validates it generalizes beyond the specific test case.
| Tool | Approach | Benchmark | Precision | Recall | F1 |
|---|---|---|---|---|---|
| Skwaq (agentic) | Multi-agent + graph | Juliet 20 | 100% | 95% | 97.3% |
| VulBinLLM | LLM decompile+reason | Juliet stripped | ~85% | ~100% | ~92% |
| PSΒ³ | Binary pattern | Curated dataset | 82% | 97% | 89% |
| LATTE | Taint + LLM | Juliet | ~70% | ~85% | ~77% |
Note: Benchmarks differ across tools β direct comparison is approximate. See IRIS paper for methodology.
| PR | Change | Impact |
|---|---|---|
| #29 | Gym benchmark harness | Baseline F1=50% |
| #35β39 | Agentic pipeline, multi-agent validation | First agent-driven detection |
| #49 | Dual-judge breakthrough | Precision: 15%β100% |
| #57 | All 4 industry benchmark adapters | Juliet, OWASP, CGC, CSE |
| #217β243 | 34 semantic classes, 109/109 Juliet CWEs | Full CWE coverage |
| #298 | Industry expansion | Juliet 1K, OWASP 1K, CSE 400 |
| #302 | CyberGym + results-skeptic agent | 3,014 real CVEs added |
| #303 | 3-layer improvement + 4 agentic cycles | +7.6% Juliet, +8.0% OWASP |
| #312β313 | Agentic eval tuning | Juliet F1=97.3% |
Suites like Juliet, OWASP, and CyberGym label specific synthesized or known vulnerabilities. They do not exhaustively label every real vulnerability in the code. A finding the benchmark does not expect may still be a genuine bug β the label set is a lower bound on truth, not an upper bound.
When skwaq reports a finding that the benchmark marks as a negative, that disagreement has three possible explanations: (a) a genuine false positive, (b) a real vulnerability the benchmark didn't label, or (c) a variant of a labeled vulnerability that falls outside the benchmark's expected form. Only (a) is a true FP. Conflating all three causes the tool to suppress valid detections.
Skwaq's 100% benchmark precision is real β but it reflects agreement with the benchmark's label set, not proof that every suppressed finding was wrong. Investigator review of "FP" cases on CyberGym and Juliet has surfaced genuine vulnerability variants outside the expected answer key. Improvement cycles must distinguish benchmark-label mismatches from actual detection errors.
# Full agentic analysis
skwaq analyze /path/to/repo
# Quick pattern-only scan
skwaq analyze path/to/src.c --quick
# Analyze compiled binary
skwaq analyze path/to/binary --binary
# Stripped binary with renaming
skwaq analyze path/to/binary \
--binary --decompile-rename
# Agentic eval
skwaq gym eval \
--suites juliet,owasp,cyberseceval
# Pattern-only eval (fast)
skwaq gym eval --suites juliet \
--quick --max-cases 200
# Run improvement cycle
skwaq gym improve juliet \
--max-cases 30 --max-improvements 5
# Target specific CWE
skwaq gym improve juliet \
--cwe CWE-78 --max-cases 20
Supports Azure AI Foundry (GPT-5.4), Anthropic Claude (via Azure MaaS), and GitHub Copilot (Claude Opus 4.6) backends β configured in skwaq.toml.