English · 简体中文
A fast, multi-language Static Application Security Testing (SAST) analyzer for CI/CD gates.
Godzilla lowers many languages into one language-neutral SSA IR — gIR — and runs a single inter-procedural taint engine over it. Write a detection rule once; it applies across every supported language.
flowchart LR
GO[Go] --> FE
PY[Python] --> FE
JS[JavaScript] --> FE
JV[Java] --> FE
RS[Rust] --> FE
RB[Ruby] --> FE
CC["C / C++"] --> FE
FE["Language<br/>frontends"] --> IR["gIR<br/>language-neutral SSA"]
IR --> ENG["Taint engine<br/>+ YAML rules"]
ENG --> FD["Findings<br/>with confidence"]
FD --> OUT["Report · JSON · SARIF<br/>severity-gated exit code"]
FD -. optional .-> LLM["LLM review"]
LLM -.-> OUT
Status: usable and tested, but young. See Status & limitations.
- Inter-procedural taint tracking. Follows untrusted data across function calls (source → sanitizer → sink). Each finding carries a confidence: High intra-procedural, Medium cross-function.
- YAML rules, sink-argument aware. Sources / sinks / sanitizers / propagators
are canonical-name globs. A sink can pin its injection-point argument
(
"go:*database/sql*.Query#0"), so a parameterizeddb.Query("... = ?", x)is not a false positive. See docs/writing-rules.md. - Batteries included. Built-in packs for the classes in the detection matrix, plus non-dataflow checks for weak crypto and hardcoded secrets.
- CI-friendly output. Human-readable findings, a single-file HTML report (filterable and sortable, with taint-flow snippets, syntax highlighting and a scan-diagnostics panel), JSON and SARIF 2.1.0 (for GitHub code scanning), and a severity-gated exit code.
- Optional LLM review. A pluggable, off-by-default stage sends findings at or below medium confidence to Claude to trim false positives; High-confidence findings are never reviewed, and the stage fails open.
- Single self-contained binary. Go/JS parsing is pure Go; Python, Ruby, Java,
and Rust shell out to a toolchain on
PATHand degrade gracefully when absent.
go install github.com/bytevet/godzilla/cmd/godzilla@latest # or, from a clone:
go build -o godzilla ./cmd/godzillaRequires Go 1.26.5+. Scanning Python, Ruby, Java, or Rust also needs that
language's toolchain (python3, ruby, a JDK 24+ java, rustc) on PATH,
each degrading gracefully when absent. Or skip install and
run with Docker.
Both commands above produce a binary that reports its version as dev. Use
make build for one stamped with the current tag (godzilla version).
# Scan a directory (or a single source file) with the built-in rules
godzilla scan ./path/to/project
# Write an HTML report and fail the build only on high+ severity
godzilla scan --html report.html --fail-on high ./path/to/project
# Machine-readable output: JSON for tooling, SARIF for GitHub code scanning
godzilla scan --sarif results.sarif --json results.json ./path/to/project
# Add your own rules on top of the built-ins
godzilla scan --rules myrules.yaml ./path/to/project
# Triage medium/low-confidence findings with an LLM (needs ANTHROPIC_API_KEY).
# A scan whose findings are all High reports "0 reviewed" — that is the gate
# working, not a failure.
godzilla scan --llm-review ./path/to/project
# Changed-files mode: gate only what a commit touched (one process, one gate)
git diff --name-only --cached | godzilla scan -files -Pre-commit hook (.git/hooks/pre-commit) — gate a commit on only its staged
files, so a docs-only commit passes cleanly:
#!/bin/sh
git diff --name-only --cached --diff-filter=d | godzilla scan -files - --fail-on highExit codes: 0 clean · 1 error · 2 bad usage · 3 findings at/above
--fail-on (default: medium). Use the exit code as your CI gate.
When stderr is a terminal, a scan shows its progress. Each phase is a row that
carries its own completeness, so a language's coverage is read where the phase
is; a running phase keeps a spinner and a clock, and gains its took and
coverage only when it lands. The footer is a fixed 80 columns — a 40-cell bar
segmented by phase group, then percent, elapsed and the running phase.
Frontend warnings stream into a pane above the bar, each naming the language and the file it came from. It is a preview: every captured line is written out in full when the scan finishes.
✓ walk +0.02s @0.02s
! javascript parse & lower 87 files 86/87 +0.09s @0.11s
✓ ruby parse & lower 47 files 47/47 +0.18s @0.20s
⠹ java parse & lower @1.20s
6 warnings · last 2
rust cannot find module or crate `reqwest` in this scope
→ test/rust/ssrf_reqwest/src/lib.rs:12
rust unresolved import `rouille`
→ test/rust/web_rouille/src/lib.rs:7
██████████████████████████████░░░░░░░░░░ 76% 1.20s go parse & typecheck
Three outcomes stay distinct without colour — ✓ complete, ! partial, ✗
failed — and under NO_COLOR the status widens to [+] [!] [x] [*] and the bar
collapses to a single [====----] run. Below 80 columns the took column goes
first, then volume; status, name, coverage and the elapsed clock survive.
At rest the bar completes, a legend names what each group cost, and a closing block states the outcome with its exit code and its reason:
███████████████████████████████████████████████████ 100% 1.47s
▪ frontends 2.10s ▪ go 1.26s ▪ analysis 0.10s
201 findings 76 critical 80 high 26 medium 19 low
incomplete python, javascript, java, rust — cpp failed
exit 3 findings at or above -fail-on=medium
The bar draws on stderr only, and findings stay on stdout, so redirecting
either one on its own still works. Findings are coloured by severity when stdout
is a terminal — asked separately, so godzilla scan > report.txt writes a plain
file. All of it turns itself off when stderr is not a terminal, under -quiet,
and when CI is set.
$ godzilla scan ./test/go/sql_injection
coverage: go=ok
[high] go-sql-injection (CWE-89, confidence: medium)
Untrusted input flows into a database/sql query without parameterized arguments...
sink: .../main.go:40:20 -> go:(*database/sql.DB).QueryRow
source: .../main.go:43:6
in: go:(*.../sql_injection.User).GetByID
[high] go-sql-injection (CWE-89, confidence: high)
Untrusted input flows into a database/sql query without parameterized arguments...
sink: .../main.go:62:24 -> go:(*database/sql.DB).Query
source: .../main.go:58:27
in: go:.../sql_injection.main$1
2 finding(s); 2 at/above "medium"; 0 suppressed.
The Go frontend lowers dependency bodies, so taint flows through library code
instead of stopping at it — but a big repo's transitive closure runs to millions of
lines, and loading all of it can exhaust the machine before any analysis starts.
-dep-budget caps how much third-party Go source is promoted to full analysis:
| Value | Effect |
|---|---|
auto (default) |
Size the cap from the memory available to the process — a container's cgroup limit counts, so a smaller CI runner gets a smaller cap. |
off |
No cap: load the whole dependency closure, whatever it costs. |
A byte count — 32M, 256M |
Use exactly this cap (suffixes K/M/G, powers of 1024). |
The cap is spent on the dependencies nearest your own code first, so what it drops is the fringe of the closure. Those are analyzed as signatures only — the same treatment the standard library always gets. The scan still runs to completion and still reports findings; what it loses is taint that would have been carried through one of those dependencies' bodies. Such a scan is degraded, not failed:
coverage: go=DEGRADED
-strict still passes on it, because the frontend ran — -strict fails only when a
detected language went un-analyzed. The HTML report's scan-diagnostics panel names
how much of the closure was dropped.
A rule matches a canonical name and pins its injection point by logical
argument index (go:*gorm*.DB*.Raw#0); neither is visible in the source, and a
wrong #<n> fails silently — it selects a real argument, just not the intended
one (docs/writing-rules.md). godzilla-playground is a
second binary that lowers a target once and serves a local web UI for exploring
the gIR and debugging rules against it; the scan pipeline is unchanged.
Three columns — file tree · source · gIR — with the source and gIR sides kept in
step, so clicking either highlights the other. Every call shows its canonical
name, and each argument's logical index is a badge you click to get the pattern;
a statically-resolved method call's receiver is drawn as recv and never
numbered, which is the off-by-one removed. A drawer at the bottom tests a
canonical pattern against the loaded module and reports how many calls it matches
and which argument each #<n> pins. Sink/source badges and the tester both run
through the real internal/rules matcher server-side, so the UI shows the
engine's own verdict rather than a second implementation of it. A file the walk
found but no frontend lowered is listed and flagged — such a file is invisible to
every rule.
go run ./cmd/godzilla-playground <path> # or: godzilla-playground <path>
-rules <path> additional YAML rule file — or directory of rulepacks — to load alongside the built-in rules
-addr <host:port> listen address (default 127.0.0.1:0 — an ephemeral port)
-open=false do not open a browser
-allow-build allow running the scanned project's build tool (Maven/Gradle/Cargo)
-parse-timeout <dur> deadline per per-file parse/dump subprocess
-build-timeout <dur> deadline for a whole-project build under -allow-buildIt binds loopback only and lowers once per invocation — no watching, no
re-lowering. make build and go build ./... build both binaries, and both
Docker images ship them (Run with Docker).
Everything routine is a CLI flag (godzilla scan -h); the environment only
carries operator concerns:
| Variable | Effect |
|---|---|
GODZILLA_ALLOW_BUILD=1 |
Same opt-in as -allow-build: lets a scan run the project's build tool (Maven/Gradle/Cargo). |
GODZILLA_RUSTC, GODZILLA_CARGO |
Paths to the Rust toolchain binaries (default: rustc, cargo on PATH). |
GODZILLA_CC, GODZILLA_CXX |
C/C++ compilers for the opt-in LLVM backend (default: clang, clang++). |
GODZILLA_LLM_MODEL |
Override the -llm-review model (default: claude-haiku-4-5 for Anthropic, gpt-4o-mini for OpenAI). |
GODZILLA_LLM_PROVIDER=openai, GODZILLA_LLM_BASE_URL |
Select an OpenAI-compatible endpoint for -llm-review (e.g. a local model). |
ANTHROPIC_API_KEY / OPENAI_API_KEY |
Credentials for -llm-review (Anthropic also honors an ant auth profile). |
GOMEMLIMIT |
Respected as-is: setting it disables Godzilla's automatic soft memory limit. |
GODZILLA_PROGRESS |
Force the scan's progress display on (1) or off (0). By default it runs only when stderr is a terminal and CI is unset. |
Subprocess deadlines are flags, not environment: -parse-timeout (default
2m0s, each per-file parse/dump) and -build-timeout (default 10m0s, a
whole-project build under -allow-build).
Prebuilt images ship with the toolchains a scan needs, so you can gate a repo without installing anything. They live on GHCR in two variants:
| Image | Size | Scans |
|---|---|---|
ghcr.io/bytevet/godzilla (:latest) |
~600–700 MB | Go · JavaScript/TS · Python · Ruby · secrets |
ghcr.io/bytevet/godzilla:full |
~1.5–2 GB | everything in slim + Java + Rust |
The entrypoint is godzilla and the default command is scan ., so mounting a
repo at /src scans it immediately.
# Scan the current directory (exit 3 on a finding at/above --fail-on)
docker run --rm -v "$PWD:/src" ghcr.io/bytevet/godzilla
# Any arguments override the default `scan .`
docker run --rm -v "$PWD:/src" ghcr.io/bytevet/godzilla \
scan --sarif /src/results.sarif --fail-on high /src
# Java/Rust need the full image
docker run --rm -v "$PWD:/src" ghcr.io/bytevet/godzilla:full
# The playground is the image's other binary. Bind 0.0.0.0 — the 127.0.0.1
# default is the container's own loopback, which no port publish reaches — and
# open it as localhost, since it serves loopback Host headers only.
docker run --rm -p 7391:7391 -v "$PWD:/src" \
--entrypoint godzilla-playground ghcr.io/bytevet/godzilla \
-addr 0.0.0.0:7391 -open=false /srcThe slim image skips Java and Rust with a coverage warning rather than
failing. Tags: X.Y.Z/X.Y/latest (slim) and X.Y.Z-full/full (full) track
releases; edge/edge-full track main. Multi-arch (amd64 + arm64).
| Go | Python | JavaScript | Java | Rust | Ruby | |
|---|---|---|---|---|---|---|
| Parser | golang.org/x/tools SSA |
python3 ast |
esbuild AST (pure Go); TS/JSX/ESM natively; Flow blanked in place; .vue/.svelte SFCs |
JVM bytecode (java.lang.classfile) |
rustc MIR | ruby Ripper; .erb templates |
| SQL injection | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Command injection | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Path traversal | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| SSRF | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Reflected XSS | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Open redirect | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| DOM XSS (client-side navigation) | — | — | ✅ | — | — | — |
| Insecure deserialization | — | ✅ | ✅ | ✅ | — | ✅ |
Code injection (eval) |
— | ✅ | ✅ | — | — | ✅ |
| Server-side template injection | — | ✅ | — | — | — | — |
| LDAP / XPath injection | — | ✅ | — | — | — | — |
| Zip slip | — | ✅ | — | — | — | — |
| Insecure framework config | — | ✅ | — | — | — | — |
| Weak crypto | ✅ | — | — | ✅ | — | — |
Hardcoded secrets (CWE-798) are detected in all languages by
kind: secretrules — regexps run over gIR string constants and over config files no frontend parses (.env, compose, CI YAML), independent of the taint engine. Add your own credential format with--rules.
- JavaScript also scans Vue (
.vue) and Svelte (.svelte) single-file components: untrusted data reachingv-html/:hrefor{@html}is flagged as template-injection XSS (CWE-79). Pure Go, no Node. - JavaScript also flags client-side navigation (
location.href = x,location.assign/replace,window.open) as XSS, not just an open redirect. A server'sLocation:header is safe from this — browsers refuse to follow one to ajavascript:URL — but assigning that same string tolocationin the page executes it, so encoding the value does not help and only a scheme allowlist does. - Ruby also scans ERB templates (
.erb), where a Rails view puts request input on the page. Rails auto-escapes<%= %>, so only the escape-bypassing forms —<%== %>,raw,.html_safe— are treated as XSS sinks. - Java analyzes JVM bytecode (so it scans
.class/.jartoo); needs a JDK 24+java. Maven/Gradle projects are built first so third-party deps are on the classpath. - Rust analyzes rustc MIR and ships in the default binary; only
rustcis needed. ACargo.tomlproject is built so web-framework request accessors are recognized as sources. - C / C++ are analyzed via LLVM IR — an opt-in cgo build
(
make build-llvm, needs libLLVM + clang), not in the default binary. Adds command injection, path traversal, format string, SQL injection, and buffer-overflow checks.
Full frontend details are in ARCHITECTURE.md.
A rule is a source→sink taint spec (or a non-dataflow dangerous-call check)
matched against canonical <lang>:module.Type.member names. Adding a detection is
usually a few lines of YAML in rulepacks/; pass your own with
--rules. See the rule-authoring guide.
flowchart TD
CLI["cmd/godzilla<br/>scan CLI · exit code"] --> CONV["converters/*<br/>frontends → gIR"]
CONV --> IRp["pkg/ir/v1<br/>gIR (generated from proto/)"]
IRp --> AN["internal/analysis<br/>call graph · taint · secrets"]
RULES["internal/rules<br/>YAML rule packs"] --> AN
AN --> REP["internal/report<br/>HTML · JSON · SARIF"]
AN --> REV["internal/llm<br/>optional review"]
REV --> REP
ARCHITECTURE.md has the design and the reasoning behind it.
Godzilla is functional and covered by tests, but deliberately scoped. Taint is
inter-procedural but context-insensitive, dynamic dispatch resolves by
class-hierarchy analysis, and pointer analysis is approximated (value-flow + CHA)
rather than a full points-to. Python, JS and Ruby build a real CFG, but
exceptions and break/continue stay approximate. SSRF findings are suppressed
only when the taint is confined to the path or query of a proven fixed host, so
the reduction never costs a true positive.
Details, and the per-component status table, are in ARCHITECTURE.md.
scripts/pr-quality-gate.sh measures every PR against its base on four axes — LOC
changed (excluding tests), corpus TP/FP/FN, rule churn, and scan performance. CI
posts the report as a PR comment, and precision/recall/perf regressions block the
merge. Run it yourself with scripts/pr-quality-gate.sh origin/main. See
docs/quality-gate.md.
Contributions welcome — see CONTRIBUTING.md. Good first areas: new built-in rules (often just YAML — guide), a new language frontend, or better frontend fidelity.
Found a vulnerability in Godzilla itself? Please report it privately through GitHub's private vulnerability reporting rather than a public issue. A missed or false detection is not a vulnerability — that is an ordinary issue, and a very welcome one.
MIT © 2026 Byte.Vet