Killer
A Rust security testing framework with a custom language for writing vulnerability attacks and code-analysis rules.
"The software that tries to destroy your software before attackers do."
Killer does four things:
-
Static analysis (
killer scan) — walks a project, detects its languages, and runs security/quality rules over every file: hardcoded secrets, dangerous command execution, oversized files,TODO/FIXMEmarkers, and duplicate code. Prints a color-coded report with a 0–100 health score. -
A security test framework (
killer test) — runs.klrfiles written in the Killer Rule Language, a real DSL withsuite/testblocks,repeatloops, andmutatefuzz-generators. Tests run in parallel across worker threads, with Jest-like output, built-in suites (--suite web), and JSON/HTML reports. -
Project intelligence (
killer history) — Killer remembers. Every scan is recorded, so it can show how a project's security score moves over time ("Improved +16, fixed 23 findings"). -
Workflow integration (
killer review/killer ci) — reviews only the lines a change touched (including concurrency/transaction bugs like an unguardedbalance -= amount), and provides a single CI gate you can drop into GitHub Actions.
Status: v1.1.0 — available now. Builds and passes its full test suite; install from source (a crates.io release is coming). The static engine, the
.klrtest framework, project intelligence, code review, and the CI gate are all implemented and tested; see Roadmap for what's intentionally deferred, docs/ for guides, and CHANGELOG.md for what shipped.
Features
- Zero-config scanning — run
killer scan .and get results immediately. - Language detection — Rust, JavaScript, TypeScript, Python, Go, Ruby, Java, C/C++, and Shell (by file extension).
- Smart directory pruning — automatically skips
.git,node_modules,target,dist,build, virtualenvs, caches, and other noise. - Extensible rule engine — every rule implements a single
Ruletrait. - Security rules
- Hardcoded secrets — API keys, passwords, tokens, plus known provider
formats (AWS keys, GitHub PATs, Slack tokens, private-key blocks). Ignores
placeholders and
process.env/std::envlookups to keep noise down. - Dangerous commands —
os.system,subprocess(..., shell=True),eval/exec, RustCommand::new, JSchild_process/execSync, etc.
- Hardcoded secrets — API keys, passwords, tokens, plus known provider
formats (AWS keys, GitHub PATs, Slack tokens, private-key blocks). Ignores
placeholders and
- Quality rules
- Large files — flags files over a configurable line threshold.
- TODO / FIXME tracker — surfaces
TODO,FIXME,HACK,XXX. - Duplicate code — detects repeated blocks of consecutive lines.
- Health score — a single 0–100 number, weighted by severity.
- Configurable — a
.killer.tomlfile tunes ignores, thresholds, and which rules run.
Installation
From source (the supported path today):
Once a release is published to crates.io this will also work:
Requires a stable Rust toolchain (1.74+). On Windows with the GNU toolchain,
see the build note under Development if dlltool errors appear.
Usage
scan flags
| Flag | Description |
|---|---|
--quiet |
Print a single summary line instead of the full report. |
--fail-on-issues |
Exit non-zero if any critical or high issue is found (CI). |
test flags
| Flag | Description |
|---|---|
--suite <NAME> |
Run a built-in suite (web, api, authentication, database, crypto, filesystem) instead of files. |
--url <URL> |
Base URL relative attack targets resolve against. |
--parallel [N] |
Run tests across N worker threads (omit N to auto-size to the CPU). |
--format <FMT> |
terminal (default) or json (for CI). |
--project <DIR> |
Project directory to run static .klr rules over (default .). |
--no-save |
Do not write results to .killer/results/. |
--fail-on-issues |
Exit non-zero if any vulnerability is found (CI). |
Example
Scanning a project that contains a couple of deliberately vulnerable files:
$ killer scan tests/vulnerable_project
====================================================
KILLER REPORT
Project: vulnerable_project
Files scanned: 2
Lines of code: 27
Languages: JavaScript, Python
Issues found: 8
Security
❌ Hardcoded secret detected bad.py:15
AWS access key id found in source. Move it to an environment variable or secret manager.
❌ Hardcoded secret detected secret.js:4
OpenAI-style secret key found in source. Move it to an environment variable or secret manager.
❌ Hardcoded secret detected secret.js:5
Hardcoded credential found in source. Move it to an environment variable or secret manager.
⚠ Dangerous command execution bad.py:7
os.system() shell execution detected. Validate/sanitize inputs and avoid passing user data to a shell.
⚠ Dangerous command execution bad.py:8
subprocess call detected. Validate/sanitize inputs and avoid passing user data to a shell.
⚠ Dangerous command execution bad.py:12
eval() of dynamic input detected. Validate/sanitize inputs and avoid passing user data to a shell.
Quality
⚠ FIXME comment bad.py:6
FIXME: this passes untrusted input straight to the shell
• TODO comment secret.js:8
TODO: move these into environment variables
Summary
Critical: 3
High: 3
Warning: 1
Info: 1
Score: 0/100
====================================================
A clean project scores 100:
$ killer scan tests/clean_project
KILLER REPORT
Project: clean_project
Files scanned: 1
Lines of code: 22
Languages: Rust
Issues found: 0
No issues found. Clean scan!
...
Score: 100/100
The Killer Rule Language (.klr)
.klr is a small domain-specific language for describing attacks (dynamic
security tests) and rules (static code checks). The compiler pipeline is a
classic one:
.klr text ──▶ lexer ──▶ tokens ──▶ parser ──▶ Program (AST)
│
┌────────────────────────┴───────────────┐
▼ ▼
interpreter (attacks) rule_engine (static rules)
│ │
▼ ▼
AttackOutcome RuleFinding
Attacks
An attack describes how a secure system should behave. If every expectation
holds, the system defended itself (PASSED). If any expectation fails, a
vulnerability is indicated (FAILED).
project "MyApplication"
attack authentication {
target "/api/login"
send {
username = "' OR 1=1"
password = "anything"
}
expect {
status != 200
response does_not_contain "token"
}
severity critical
message: "SQL injection vulnerability detected"
}
Run it against a target:
$ killer test examples/auth_security.klr --url http://localhost:8080
╭────────────────────────────────────────────╮
│ │
│ K I L L E R │
│ Software Security Engine │
│ │
╰────────────────────────────────────────────╯
====================================================
KILLER TEST REPORT
Project: MyApplication
Sources: examples/auth_security.klr
Tests
✗ authentication
✗ status != 200 observed status 200
✗ response does_not_contain "token" "token" leaked in response
→ killer explain KLR-SQLI
✗ api_rate_limit
✗ blocked_after 10 no rate limiting after 100 requests
→ killer explain KLR-RATE-LIMIT
Tests:
0 passed
2 failed
2 total
Time: 0.05s
2 vulnerabilities found
====================================================
More attack shapes
Both brace-blocks and colon-forms are accepted:
# Rate limiting: the endpoint should block abusive clients.
attack api_rate_limit {
request: POST "/login"
repeat: 1000 times
expect: blocked_after 10
severity medium
}
# Path traversal: an uploaded "../../etc/passwd" must not be exposed.
attack upload {
endpoint "/upload"
payload: "../../etc/passwd"
expect { file_not_exposed true }
}
# Session reuse: a stolen cookie must be rejected on reuse.
attack session {
target "/account"
login user "test"
steal cookie
attempt reuse
expect { session_invalidated true }
}
Static rules
A rule runs against your source code (via --project), not a live server:
rule "unsafe database query"
when function contains "query"
and input reaches query
without sanitization
severity high
report: "User input reaches database directly"
$ killer test examples/database_rules.klr --project .
Static Rule Findings
• unsafe database query dao.py:4
User input reaches database directly
The static-rule engine uses line-level heuristics (does the line hit the sink, read input or build a dynamic string, and skip sanitization?). It is a pragmatic first pass — full dataflow analysis via Tree-sitter is a later phase.
Language reference
| Construct | Forms | Notes |
|---|---|---|
project "Name" |
— | Optional display name. |
suite "Name" { … } |
— | Groups attack/test/repeat blocks. |
attack <name> { … } / test <name> { … } |
— | A dynamic test (test is a friendly alias). |
target / endpoint / url |
target "/path" |
Relative to --url, or an absolute http://…. |
method / request |
method POST · request: POST "/x" |
Defaults to POST when a body is present, else GET. |
send { k = v … } |
— | JSON request body. |
header { k = v … } |
— | Extra request headers. |
payload "…" |
payload: "…" |
Raw request body (e.g. traversal string). |
repeat N times |
repeat: N |
Repeats the request within one test. |
repeat N { … } |
— | Loop: repeats the contained tests N times. |
check <name> |
check authentication |
Expands to built-in expectations (authentication, injection, rate_limit). |
mutate <field> { … } |
— | Fuzzing: expands the test into one variant per generator value. |
fuzz <field> |
fuzz amount |
Shorthand for mutate with a broad default generator set. |
expect { … } |
expect: <cond> |
One or more conditions. |
severity |
critical/high/medium/low |
— |
message: "…" |
message "…" |
Shown when the attack fails. |
Expectations: status <op> <n> (== != < > <= >=),
response contains "…", response does_not_contain "…", blocked_after <n>,
and named booleans file_not_exposed true / session_invalidated true.
Mutation generators: negative_numbers, huge_values, decimals, zero,
empty, sql_injection, xss, null_bytes, long_strings, unicode. An
unknown generator injects its own name as the value.
Comments start with # or //. Parse errors carry a stable code (KLR001…)
plus File / Line / Expected / Found fields.
Test framework: suites, fuzzing & parallelism
suite "Payment Security" {
attack duplicate_payment {
request POST "/payment"
send { amount = 100 }
mutate amount {
negative_numbers # -1, -999999
huge_values # 999999999999999, ...
decimals # 0.0001, 3.14159
}
expect { status != 200 }
}
}
That one mutate block expands into six concrete tests. Run built-in suites or
your own, across worker threads, with a Jest-like report:
$ killer test --suite api --url http://localhost:8090 --parallel 8
KILLER TEST REPORT
Sources: builtin:api
Workers: 8
API
✓ input_injection_fuzz [id=sql_injection]
✓ input_injection_fuzz [id=negative_numbers#1]
...
✗ rate_limit
✗ blocked_after 30 no rate limiting after 100 requests
→ killer explain KLR-RATE-LIMIT
Tests:
6 passed
1 failed
7 total
Time: 0.06s
- Built-in suites:
--suite web | api | authentication | database | crypto | filesystem(embedded in the binary). - Parallel:
--parallel [N]runs across scoped worker threads (omit N to auto-size). - Reports:
--format jsonfor CI, orkiller report --htmlfor a self-contained dashboard (killer-report.html).
Issue ids & explain
Every attack is tagged with a stable issue id — KLR-SQLI,
KLR-PATH-TRAVERSAL, KLR-RATE-LIMIT, KLR-SESSION, KLR-GENERIC. Look one up:
$ killer explain KLR-SQLI
KLR-SQLI SQL Injection
What it is
Untrusted input is incorporated into a SQL query, letting an attacker alter
the query's logic (e.g. ' OR 1=1).
Impact
Authentication bypass, reading or modifying arbitrary data, ...
How to fix it
Use parameterized queries / prepared statements. ...
Results storage
Unless --no-save is passed, each run is written to
.killer/results/run-<timestamp>.json for later review or diffing.
Transport note: the built-in HTTP client speaks plain
http://with zero dependencies, behind anHttpClienttrait. TLS (https://) is a planned drop-in backend; until then, point--urlat anhttp://target (e.g. a local dev server or a proxy).
Project intelligence, review & CI
killer history — Killer remembers
Every killer scan records a snapshot under .killer/history/. Over time,
killer history shows the trend:
$ killer history .
KILLER SCORE
Security: 100/100
Current: 0 findings (0 critical, 0 high)
Since first scan (2 snapshots)
Change: +39
Fixed: 4 findings
Trend: ▅█
Storage is plain JSON (.killer/project.json + .killer/history/*.json) — no
database engine, so it stays portable and diffable. Pass --no-record to
scan to skip recording.
killer review — review only what changed
killer review diffs the working tree (or --staged, or --base <ref>) and
reviews only the added lines. It runs the existing rules plus review-specific
heuristics — including concurrency/transaction safety:
$ killer review .
KILLER CODE REVIEW
Reviewed: 1 changed file
❌ Hardcoded secret detected payment.rs:3
OpenAI-style secret key found in source. ...
→ Load secrets from the environment or a secrets vault.
⚠ Possible race condition payment.rs:2
`balance` is updated with a non-atomic read-modify-write and no visible lock/transaction.
→ Guard the update with a Mutex, use an atomic type (fetch_add/fetch_sub), or a DB transaction.
✗ REVIEW FAILED — 1 blocking issue(s)
killer ci + killer github enable — the gate
killer ci runs the whole gate (scan + static .klr rules + review) and exits
non-zero on any blocking issue:
$ killer ci .
▶ Killer CI gate
✗ scan — score 75/100, 1 critical, 0 high
✓ klr rules — 0 finding(s)
✗ review — 2 finding(s), 1 blocking
✗ Killer gate FAILED # exit code 1
killer github enable writes .github/workflows/killer.yml so the gate runs on
every push and pull request.
Scope of this Phase 4 increment. The full Phase 4 spec describes a platform (software-graph/data-flow engine, a networked rule & attack marketplace, a full multi-language IR compiler, a web dashboard, live GitHub/GitLab/Jenkins apps, and enterprise features). This increment ships the local-first, testable core of that vision — persistent intelligence, code review, and a CI gate. The hosted/UI/networked pieces are intentionally deferred rather than stubbed. Note also that Phase 3 (fuzzing / chaos testing) is not yet implemented.
Configuration
Generate a starter config with killer init, then edit .killer.toml:
[]
# Display name for reports. Defaults to the directory name.
= "my-app"
[]
# Extra paths to ignore, on top of the built-in defaults.
= ["tests", "vendor"]
# Files longer than this many lines are flagged as "large".
= 1000
[]
# Toggle individual rules on or off.
= true
= true
= true
= true
= true
[]
# Languages to focus on (informational in this phase).
= true
= true
= true
[]
# Security posture: relaxed | standard | strict.
= "standard"
[]
# Where `killer test` looks for .klr files when no path is given.
= "./security-tests"
# Base URL that relative attack targets resolve against.
= "http://127.0.0.1:8080"
All fields are optional — a project with no config file still gets a full scan.
How the health score works
Each finding deducts points from a starting score of 100:
| Severity | Points |
|---|---|
| Critical | 25 |
| High | 10 |
| Warning | 3 |
| Info | 1 |
The score is clamped to the 0–100 range. --fail-on-issues fails the run when
any critical or high finding is present, which makes Killer easy to drop into CI.
Architecture
Killer is split into a reusable library crate (src/lib.rs) and a thin CLI
binary (src/main.rs).
src/
├── main.rs # CLI entry point (scan / test / explain / init / version)
├── lib.rs # public library surface
├── cli.rs # clap command/flag definitions
├── scanner.rs # directory walk, language detection, file loading
├── analyzer.rs # Rule trait, Finding/Severity types, the Analyzer
├── config.rs # .killer.toml loading
├── report.rs # scan/attack/review/history rendering
├── results.rs # TestRun/AttackOutcome types + JSON storage
├── explain.rs # knowledge base behind `killer explain`
├── intelligence.rs # persistent snapshots + score trends (Phase 4)
├── git.rs # git-diff parsing for the review engine
├── review.rs # code review over changed lines (+ concurrency heuristics)
├── ci.rs # CI gate helpers + GitHub Actions workflow
├── rules/ # Phase 1 static rules
│ ├── security.rs # hardcoded secrets, dangerous commands
│ ├── quality.rs # large files, TODO/FIXME, duplicate code
│ └── dependencies.rs # reserved for a future phase
├── suites.rs # built-in test suites (embedded .klr)
├── klr/ # the Killer Rule Language
│ ├── lexer.rs # text → tokens
│ ├── parser.rs # tokens → AST (recursive descent, coded errors)
│ ├── ast.rs # AST types (suite/attack/mutate/…)
│ ├── interpreter.rs # executes one attack → AttackOutcome
│ ├── runner.rs # check/mutate expansion + parallel execution
│ └── rule_engine.rs # executes static .klr rules → RuleFinding
├── attacks/ # attack executors
│ ├── http.rs # zero-dep HTTP client behind an HttpClient trait
│ ├── filesystem.rs # path-traversal / file-exposure helpers
│ └── database.rs # SQLi signatures + rule heuristics
└── suites/ # embedded .klr suites (web, api, authentication, database, crypto, filesystem)
Note on layout: the Phase 2 spec sketches a
core/ scanner/ analyzer/ …multi-directory reorg. For a single crate that would be churn without benefit, so the Phase 1 modules stayed put and Phase 2 added theklr/andattacks/module trees alongside them.
Adding a rule
-
Implement the
Ruletrait:use ; use FileData; ; -
Register it in
src/rules/mod.rsdefault_rules().
That's the whole extension surface — the scanner, analyzer, and report layers
need no changes. Adding a .klr expectation or attack transport is similarly
localized: extend the AST + parser and add an arm in the interpreter, or
implement the HttpClient trait for a new transport.
Development
Tests live alongside each module (#[cfg(test)]) plus two integration suites:
tests/integration.rs (static analysis over the
vulnerable_project / clean_project fixtures) and
tests/klr_e2e.rs, which stands up a real TCP server and
drives the actual HTTP client end-to-end.
Note (Windows GNU toolchain): the default
x86_64-pc-windows-gnutoolchain needsdlltool+asonPATHto build crates that usewindows-sys. If you hit adlltool/CreateProcesserror, add a MinGW-w64bindirectory (e.g. from WinLibs or MSYS2) to yourPATH, or switch to thex86_64-pc-windows-msvctoolchain with the Visual Studio Build Tools.
Roadmap
- Phase 1 ✅ — static analysis engine: scanner, language detection, rule engine, security/quality rules, scored terminal report.
- Phase 2 ✅ — the Killer Rule Language (
.klr): lexer, parser, AST, interpreter, HTTP/filesystem/database attack executors,killer test/killer explain, result storage, attack reports. - Phase 3 ⏳ — deeper analysis: AST parsing with Tree-sitter, a dependency graph, TLS transport for attacks, fuzzing, and chaos testing. (not started)
- Phase 4 🟡 (partial) — platform features. Done: project
intelligence (
history), code review (review), CI gate (ci/github enable). Deferred: software-graph/data-flow engine, networked rule & attack marketplace, full multi-language IR, live GitHub/GitLab/Jenkins apps, and enterprise features. - Phase 5 🟡 (partial) — the test-framework upgrade. Done:
.klras a real language (suite/test/repeat/mutate, coded errors), a parallel runner, built-in suites, JSON/HTML reports, and the CLI banner. Deferred: the interactiveratatuiTUI (killer ui),watchmode, a package manager (killer add/ imports), and YAML config. - Beyond — a distributed, cloud-runner security lab.
Documentation & community
- docs/ — introduction,
installation, quickstart,
CLI reference,
.klrguide, writing tests, architecture, and examples. - CONTRIBUTING.md — how to build, test, and extend Killer.
- SECURITY.md — how to report a vulnerability in Killer itself.
- CHANGELOG.md — release history.
- Website: killer.blinkdev.me
License
Licensed under the Apache License, Version 2.0.