Releases: PwnKit-Labs/foxguard
v0.7.1
foxguard v0.7.1 — Taint engine 2× faster
A performance release. Go, Python, and JavaScript taint engines were each re-walking every file's AST once per rule. We collapsed that to one walk per sanitizer group. Gin scans are 2.2× faster. Express and Flask are ~1.25× faster. Findings are byte-identical.
Full write-up: Making foxguard taint tracking 2× faster in v0.7.1
npx foxguard@latest .Benchmark (avg ms, 15 iterations, 3 warmups)
| Repo | v0.4.0 | v0.7.0 | v0.7.1 | v0.7.1 vs v0.7.0 | v0.7.1 vs v0.4.0 |
|---|---|---|---|---|---|
| express | 128 | 183 | 145 | -21% | +13% |
| flask | 127 | 233 | 187 | -20% | +47% |
| gin | 111 | 364 | 166 | -55% | +50% |
Express now lands within 13% of the pre-taint v0.4.0 baseline while doing strictly more work (cross-file taint tracking, multi-hop propagation).
What changed
Performance — the taint trilogy
- #199 — Share Go taint Pass 1 summaries across rules
- #202 — Share Python taint Pass 1 summaries across rules
- #203 — Share JavaScript taint Pass 1 summaries across rules
Same pattern across all three: group rules by sanitizer fingerprint, compute function summaries once per group, share across rules. For Go, 9 taint rules collapse from 9 AST walks per file to 2. For JavaScript, 11 rules collapse from 22 walks to 4. Findings are attributed back via a new rule_id_hint field on TaintFinding.
Distribution
- #205 — New one-line installer:
curl -fsSL https://foxguard.dev/install.sh | sh
Detects OS + arch, hits GitHub API for latest release, drops the binary in$HOME/.local/bin. ~80 lines of POSIX sh, no maintenance. - #195 — Removed Homebrew tap automation. The
brew install peaktwilight/tap/foxguardpath still works at v0.6.3 for existing users; new users should use the installer,npx, orcargo install.
Docs + infra polish
- #194 — New
TriageShowcasesection on the landing page - #196 — Unified tagline: "A security scanner as fast as a linter, written in Rust." Matches the Astral positioning pattern and the HN-validated post title. Rule count standardized on
170+. - #197 —
scripts/release.shnow auto-bumps README install refs. CI guard asserts they matchCargo.toml. - #198 — Bumped
ratatui0.29→0.30(pullslrupast GHSA-rhfx-m35p-ff5j).
Correctness
All refactors verified with:
- Byte-identical JSON diffs on express, flask, and gin between pre- and post-refactor binaries
- Dogfood — foxguard scans its own Rust source and reports the same 1,082 findings with identical severity breakdown
- Test suite — 415+ tests passing, clippy clean under
-D warnings
Upgrade
npx foxguard@latest .
# or
curl -fsSL https://foxguard.dev/install.sh | sh
# or
cargo install foxguardv0.7.1 is a drop-in replacement for v0.7.0. Same rules, same findings, faster.
Closed
v0.7.0
foxguard v0.7.0 — Interactive TUI
foxguard tui — a full-screen terminal UI for local security triage. Pick a mode, walk findings, mark/baseline/ignore, and jump to source — without leaving the terminal.
npx foxguard@latest tui .Highlights
- New
tuicommand as the first-class interactive surface (renamed fromui). - Launch picker with three modes:
Scan,Diff,Secrets. Nothing runs until you choose. - Richer detail pane — source context, cleaner snippet section, dataflow path rendering.
- Explicit Open targets —
finding/source/sink, cycled withTab, opened withEnter. - In-app triage actions (
i): mark reviewed / todo / ignore, clear review state, add to baseline, ignore rules in config. Preview before apply. - Diff and Secrets share the same UX language as Scan: same launch flow, footer, key hints.
Screenshots
Usage
npx foxguard tui . # full scan triage
npx foxguard tui --diff main . # only new findings vs target branch
npx foxguard tui --secrets . # credentials and token leaksKeys: h/l move, 1-3 jump, Tab cycle open target, Enter open, i triage, ? help, q quit.
Upgrade notes
- Use
tuiinstead ofui—uiis reserved for future web experiences. - Non-interactive scan commands are unchanged.
Also in this release
- Multi-hop cross-file taint tracking (#188)
- Cross-version benchmark harness for perf regressions (#187)
- Taint extraction optimizations and code formatting (#186)
Read more
v0.6.3
What's Changed
- feat: post findings as inline PR review comments with --github-pr (refs #164) by @peaktwilight in #166
- feat: add
foxguard diffsubcommand to show new findings vs target branch (refs #165) by @peaktwilight in #167 - Website redesign + README updates for v0.7 by @peaktwilight in #169
Full Changelog: v0.6.2...v0.6.3
v0.6.2
What's Changed
- fix: UTF-8 boundary checks in redact_match by @Darkroom4364 in #85
- fix: pass 1 max-file-size check, disable symlink following, encode SARIF URIs (refs #152, #153, #154) by @peaktwilight in #155
- fix: add --quiet flag, warn on unsupported files, fix summary format (refs #156, #157, #158) by @peaktwilight in #159
Full Changelog: v0.6.1...v0.6.2
v0.6.1
Precision & quality release
25 commits since v0.6.0 focused on precision, quality, and developer experience.
New rules & sources
- Kotlin taint rules:
kt/taint-sql-injection,kt/taint-command-injection,kt/taint-ssrfwith Ktor + Spring Boot sources - NoSQL injection:
py/taint-nosql-injection,js/taint-nosql-injection,go/taint-nosql-injectionfor MongoDB - XXE taint:
py/taint-xxe,js/taint-xxewith defusedxml sanitizer support - Log injection:
py/taint-log-injection,js/taint-log-injection,go/taint-log-injection - Deserialization:
js/no-unsafe-deserialization,go/no-unsafe-deserialization - JWT:
py/jwt-no-verify,py/jwt-hardcoded-secret,go/jwt-no-verify,go/jwt-hardcoded-secret - Java XSS, C# path traversal, Ruby SSRF + path traversal, Go path traversal taint
- Framework sources: Koa, NestJS (JS), Echo/Fiber/Chi (Go), Tornado/Bottle (Python)
Taint engine fixes
- Python walrus operator (
:=) now tracked - JS
awaitexpressions no longer break taint chain - Go type assertions preserve taint
- JS spread elements (
...arr) propagate taint - Python comprehensions propagate taint
- Ternary/conditional expressions propagate taint in Python + JS
- JS
export defaultfunctions now included in cross-file summaries
Precision improvements (false positive reduction)
- LDAP rules:
.search()/.bind()no longer matchString.search()/Function.bind() - NoSQL rules:
.find()restricted to MongoDB collection patterns, notArray.find() - Log injection:
.error()/.log()restricted toconsole.*to avoid generic method FPs - Ruby
system("literal")no longer fires - NoSQL severity downgraded from Critical → High
- Sanitizers now match fix suggestions:
shlex.quote,DOMPurify.sanitize,shellescape,html.escape,encodeURIComponentall recognized - Fix suggestions always visible (not gated behind
--explain) - Noise path exclusions:
/spec/,/__tests__/,/__snapshots__/,/generated/,/gen/,/stubs/
Refactoring
impl_rule!macro eliminates 1,665 lines of boilerplate across 174 rules- Single-pass language partition in scanner (was 3 separate filter passes)
- Integration tests organized into 12 language-grouped modules
Totals
- 174 built-in rules across 10 languages
- 337 tests all passing
- Cross-file taint for Python, JavaScript, Go
Upgrading
npx foxguard@0.6.1 .What's Changed
- feat(js): add Koa and NestJS taint sources (refs #95) by @peaktwilight in #99
- feat: add log injection taint rules for Python, JS, Go (refs #91) by @peaktwilight in #101
- feat: propagate taint through ternary/conditional expressions (refs #93) by @peaktwilight in #100
- feat: add XXE taint rules for Python and JavaScript (refs #92) by @peaktwilight in #103
- feat: add deserialization rules for JS/Go and JWT rules for Python/Go (refs #94, #98) by @peaktwilight in #106
- feat(py): propagate taint through list/dict/set comprehensions (refs #96) by @peaktwilight in #107
- feat: add NoSQL injection taint rules for Python, JS, Go (refs #97) by @peaktwilight in #108
- fix: handle walrus operator, await expressions, and Go type assertions in taint engines (refs #109, #110, #113) by @peaktwilight in #115
- feat(go): add path traversal taint rule (refs #114) by @peaktwilight in #116
- fix(js): handle export default in cross-file taint analysis (refs #112) by @peaktwilight in #117
- feat(kt): add SQL injection, command injection, and SSRF taint rules (refs #111) by @peaktwilight in #118
- fix: JS spread taint, noise path exclusions, always-visible fix suggestions (refs #119, #121, #122) by @peaktwilight in #125
- feat: add Java XSS and C# path traversal rules (refs #123, #124) by @peaktwilight in #126
- feat(rb): add SSRF and path traversal rules (refs #120) by @peaktwilight in #127
- feat(go): add Echo, Fiber, Chi framework taint sources (refs #129) by @peaktwilight in #131
- feat(py): track with-statement bindings and add Tornado/Bottle taint sources (refs #128, #130) by @peaktwilight in #132
- fix: restrict log injection matchers and downgrade NoSQL severity (refs #134, #135) by @peaktwilight in #137
- fix: restrict LDAP sink matchers to avoid String.search/Function.bind false positives (refs #133, #136) by @peaktwilight in #138
- fix: add missing sanitizers (shlex.quote, DOMPurify, etc.) to taint rules (refs #139) by @peaktwilight in #143
- fix: Ruby command injection literal check + restrict NoSQL find matcher (refs #140, #141) by @peaktwilight in #144
- refactor: single-pass language partition in scanner + fix XSS severity consistency (refs #146, #147) by @peaktwilight in #149
- refactor: add impl_rule! macro to reduce rule boilerplate (refs #145) by @peaktwilight in #150
- refactor: organize integration tests into language-grouped modules (refs #148) by @peaktwilight in #151
Full Changelog: v0.6.0...v0.6.1
v0.6.0 — Cross-file taint analysis
Cross-file taint analysis
foxguard now traces taint across file boundaries — the first open-source security scanner to do this at sub-second speed.
# views.py
from . import queries
def search(request):
name = request.GET["name"]
return queries.run_query(name) # ← py/taint-sql-injection fires here
# queries.py
def run_query(name):
cur.execute("SELECT * FROM users WHERE name = '" + name + "'")How it works
Two-pass parallel scan: pass 1 builds function-level taint summaries for every file, pass 2 resolves imported calls against those summaries. The Django shop fixture scans in 0.03s.
Supported patterns
- Python:
from .module import func,from module import func, sibling file resolution - JavaScript:
require('./services'),import { fn } from './services'with extensionless path probing (.js/.ts/.mjs/.cjs/.jsx/.tsx) - Go: same-package resolution (all .go files in a directory share exported functions)
Also in this release
- Kotlin — 10th supported language with 10 security rules
- 9 new taint rules: SSTI, XPath injection, LDAP injection for Python, JS, and Go
- Python taint improvements:
%formatting,.format(), tuple/list propagation,os.environ.get()source - Fix suggestions on all taint findings with concrete code examples
--explainflag showing source-to-sink dataflow traces--max-file-sizeflag for memory safety on large files- Lock-free parallel scanner (Mutex eliminated)
- Iterative AST walker (stack overflow prevention)
Totals
- 153 built-in rules across 10 languages
- 276 tests all passing
- Cross-file taint for Python, JavaScript, and Go
Upgrading
npx foxguard@0.6.0 .v0.5.1
What's new since v0.5.0
Kotlin — 10th supported language
Full Kotlin support via tree-sitter-kotlin-sg. 10 security rules targeting JVM APIs: SQL injection, command injection, unsafe deserialization, SSRF, path traversal, weak crypto, hardcoded secrets, XXE, CORS wildcard, and ScriptEngine eval.
9 new taint rules: SSTI, XPath, LDAP injection
Every taint language (Python, JS, Go) now covers three additional sink categories:
- Server-Side Template Injection —
py/taint-ssti,js/taint-ssti,go/taint-ssti(CWE-1336) - XPath injection —
py/taint-xpath-injection,js/taint-xpath-injection,go/taint-xpath-injection(CWE-643) - LDAP injection —
py/taint-ldap-injection,js/taint-ldap-injection,go/taint-ldap-injection(CWE-90)
Python taint improvements
%operator propagation:"SELECT %s" % user_inputnow detected.format()propagation:"SELECT {}".format(user_input)now detected- Tuple/list literal propagation:
"... %s" % (clean, tainted)now detected os.environ.get()now recognized as a taint source
Performance
- Lock-free parallel scanner: replaced
Mutex<Vec<Finding>>withpar_iter().flat_map().collect(), eliminating lock contention
Totals
- 134 built-in rules across 10 languages
- 275 tests all passing
Upgrading
npx foxguard@0.5.1 .What's Changed
- feat(py): propagate taint through % formatting, .format(), and tuple/list literals by @peaktwilight in #75
- perf: lock-free parallel scanner via flat_map/collect by @peaktwilight in #76
- feat(go): add SSTI, XPath, LDAP taint rules by @peaktwilight in #78
- feat(js): add SSTI, XPath, LDAP taint rules by @peaktwilight in #79
- feat(py): add SSTI, XPath, LDAP taint rules and os.environ.get source by @peaktwilight in #80
- feat: add Kotlin as 10th supported language with 10 security rules by @peaktwilight in #81
Full Changelog: v0.5.0...v0.5.1
v0.5.0
Highlights
--explain flag: source-to-sink dataflow traces
Taint findings now show exactly how data flows from source to sink. Pass --explain to see source and sink trace lines with file, line number, and description.
src/routes.py:42 CRITICAL py/taint-sql-injection (CWE-89)
source → line 38: flask.request.args (user-controlled input)
sink → line 42: cursor.execute (SQL execution)
Fix: use parameterized queries: cur.execute("SELECT * FROM users WHERE name = ?", (name,))
Fix suggestions for all taint findings
Every taint rule now includes a concrete fix suggestion — parameterized queries for SQL injection, shlex.quote for command injection, html.escape for XSS, URL allowlisting for SSRF, and more. Suggestions appear in terminal (--explain), JSON, and SARIF output.
New taint rules
- js/taint-sql-injection — SQL injection via string-built queries in Express/Knex/Sequelize
- js/taint-eval — eval/Function constructor with user input
- js/taint-command-injection — child_process.exec with user input
- js/taint-ssrf — fetch/axios/http.get with user-controlled URLs
- go/taint-*: closure analysis for Gin handler patterns (
r.GET("/path", func(c *gin.Context) { ... }))
MCP server (foxguard-mcp)
AI coding agents can now invoke foxguard via Model Context Protocol. Ships as a separate binary.
Claude Code hook integration
Run foxguard automatically on every file edit in Claude Code. See docs/claude-code-integration.md.
Semgrep taint YAML bridge — JS & Go support
The --semgrep-rules flag now supports taint rules targeting JavaScript and Go, in addition to Python.
Internal improvements
- Shared
AliasTableeliminates duplicate import alias logic across Python/JS/Go engines AnalysisContextstruct replaces ad-hoc parameter passing in taint engine signatures- Dead code cleanup across taint engines and scanner
Upgrading
npx foxguard@0.5.0 .
# or
cargo install foxguardUpdate GitHub Actions:
- uses: PwnKit-Labs/foxguard/action@v0.5.0Update pre-commit:
rev: v0.5.0What's Changed
- ci: deploy foxguard.dev from GitHub Actions by @peaktwilight in #49
- refactor: extract duplicated rule helpers into common module by @Darkroom4364 in #45
- tests: multi-file Django fixture pinning cross-file taint limit (refs #48) by @peaktwilight in #50
- tests: multi-file Express, Next.js, and Gin realistic fixtures (refs #48) by @peaktwilight in #51
- feat(js): js/taint-sql-injection server-side taint rule by @peaktwilight in #53
- docs: adopt PwnKit Labs umbrella tagline by @peaktwilight in #56
- docs: reduce PwnKit Labs mentions and fix stale umbrella tagline by @peaktwilight in #57
- docs: add Claude Code hook integration guide by @peaktwilight in #62
- feat: add MCP server for AI coding agent integration by @peaktwilight in #63
- feat(go): analyze closures in Go taint engine (refs #55) by @peaktwilight in #65
- feat(js): add remaining JS server-side taint rules by @peaktwilight in #64
- refactor: delete dead debug code, stale annotations, extract constants by @peaktwilight in #66
- feat: extend Semgrep taint YAML bridge to JavaScript and Go by @peaktwilight in #70
- refactor: introduce AnalysisContext struct for taint engine signatures by @peaktwilight in #71
- refactor: deduplicate import alias tables into shared AliasTable by @peaktwilight in #72
- feat: add --explain flag for source-to-sink dataflow traces by @peaktwilight in #73
- feat: add fix suggestions for taint findings (refs #61) by @peaktwilight in #74
New Contributors
- @Darkroom4364 made their first contribution in #45
Full Changelog: v0.4.0...v0.5.0
What's Changed
- ci: deploy foxguard.dev from GitHub Actions by @peaktwilight in #49
- refactor: extract duplicated rule helpers into common module by @Darkroom4364 in #45
- tests: multi-file Django fixture pinning cross-file taint limit (refs #48) by @peaktwilight in #50
- tests: multi-file Express, Next.js, and Gin realistic fixtures (refs #48) by @peaktwilight in #51
- feat(js): js/taint-sql-injection server-side taint rule by @peaktwilight in #53
- docs: adopt PwnKit Labs umbrella tagline by @peaktwilight in #56
- docs: reduce PwnKit Labs mentions and fix stale umbrella tagline by @peaktwilight in #57
- docs: add Claude Code hook integration guide by @peaktwilight in #62
- feat: add MCP server for AI coding agent integration by @peaktwilight in #63
- feat(go): analyze closures in Go taint engine (refs #55) by @peaktwilight in #65
- feat(js): add remaining JS server-side taint rules by @peaktwilight in #64
- refactor: delete dead debug code, stale annotations, extract constants by @peaktwilight in #66
- feat: extend Semgrep taint YAML bridge to JavaScript and Go by @peaktwilight in #70
- refactor: introduce AnalysisContext struct for taint engine signatures by @peaktwilight in #71
- refactor: deduplicate import alias tables into shared AliasTable by @peaktwilight in #72
- feat: add --explain flag for source-to-sink dataflow traces by @peaktwilight in #73
- feat: add fix suggestions for taint findings (refs #61) by @peaktwilight in #74
New Contributors
- @Darkroom4364 made their first contribution in #45
Full Changelog: v0.4.0...v0.5.0
v0.4.0
What's Changed
- Add inline ignore comments for code findings by @peaktwilight in #4
- Bump vite from 7.3.1 to 7.3.2 in /www by @dependabot[bot] in #6
- Bump defu from 6.1.4 to 6.1.7 in /www by @dependabot[bot] in #5
- Resolve Python import aliases in sink-matching rules (#7) by @peaktwilight in #11
- Add LoC metric and larger-corpus target to benchmarks (#8) by @peaktwilight in #12
- Add intraprocedural taint engine and first Python taint rule (refs #10) by @peaktwilight in #13
- Taint engine: wire up sanitizer support (refs #16) by @peaktwilight in #20
- Taint engine: nested subscripts and tuple destructuring (refs #15) by @peaktwilight in #21
- Add py/taint-* rules for eval, command injection, SSRF, yaml.load, SQL (refs #14) by @peaktwilight in #22
- Semgrep-compatible YAML bridge for taint rules (refs #17) by @peaktwilight in #23
- Taint engine: port to JavaScript/TypeScript with js/taint-xss-innerhtml (refs #18) by @peaktwilight in #24
- Publish per-rule precision methodology and false-positive footprint (refs #9) by @peaktwilight in #25
- Taint engine: same-file interprocedural return propagation (refs #19) by @peaktwilight in #26
- Semgrep taint YAML bridge: support pattern-either in source/sink/sanitizer blocks (refs #33) by @peaktwilight in #36
- Tooling: auto-generate www/src/data/rules.ts from Rust rule registry (refs #34) by @peaktwilight in #37
- Taint: add Django, FastAPI/Starlette, and CLI sources for Python (refs #29, #30) by @peaktwilight in #38
- Taint engine: method call propagation and f-string interpolation (refs #27, #28) by @peaktwilight in #39
- Taint: add Next.js, Hono, Fastify, SvelteKit, Deno sources to JS taint (refs #32) by @peaktwilight in #40
- Add realistic test corpus under tests/fixtures/realistic/ (refs #35) by @peaktwilight in #41
- Taint engine: propagate taint through binary + on strings (refs #42) by @peaktwilight in #43
- Taint engine: port to Go with go/taint-* rules (refs #31) by @peaktwilight in #44
New Contributors
- @dependabot[bot] made their first contribution in #6
Full Changelog: v0.3.3...v0.4.0
v0.3.3
Full Changelog: v0.3.2...v0.3.3


