flawed
flawed is a static-analysis engine for Python web applications that builds a framework-aware model of an app's routes, request inputs, state effects, call graph, and value flow, exposing it both as a queryable Python library (open_repo(), load_findings()) and a CLI scanner. It runs pluggable rule modules (@detector contract) across three layers — L1 index, L2 semantic model, L3 rules — and outputs findings as JSON/SARIF with baseline diffing support for CI use.
What it is
flawed (Apache 2.0) is both a Python library and a CLI. The top-level flawed package is the analysis API — the same surface the bundled rules use — so you can drive the model from a REPL: open_repo() builds/loads a repo model, load_findings() reads back a prior --json/--sarif scan. Both return immutable, chainable collections (group_by / count_by / tabulate / | / slicing).
How it works
A scan runs three layers, named explicitly: L1 index + L2 semantic + L3 rules. L1 (extraction) is cached after the first run; later scans reuse it. Findings go to stdout; progress and diagnostics go to stderr. The route-query idiom the bundled auth-guard rule embodies is exposed directly, e.g.:
for route in repo.routes.accepting(POST):
if route.reachable.effects() and not route.full_stack.checks():
print(route)Preconditions / Affected
Project tooling is via mise (task entry points) and uv (Python deps), pinned in mise.toml. New frameworks aren't automatic — teaching the engine one requires writing a provider (docs/, "Provider authoring") so rules see that framework's routes, inputs, and effects.
What to do
Setup and gate:
mise install # Python + uv, pinned in mise.toml
mise deps # uv sync --all-extras
mise run install-hooks # version-controlled pre-commit gate
mise run check # lockfile + lint + format + mypy + layers + testsScanning and custom rules:
flawed scan path/to/repo # L1 + L2 + L3
flawed scan . --rules-dir ./my_rules # your rules instead of built-insNote on the pre-commit hook: edit the tracked file at tools/hooks/pre-commit and reinstall — never edit the copy under .git/hooks/.
Caveats
Local observability is on by default. Every scan appends records — a runs.jsonl and a per-repo scan_metrics.jsonl under the data dir. The page states these are local-only (no network, no telemetry), but if you don't want them, set observability_enabled: false in ~/.config/flawed/config.yaml. Same file controls cache location (data_dir, repo_local; per-repo layout <data_dir>/owner__name/) and an opt-in, experimental mypy oracle (type_enrichment.enable_mypy_batch: false). Bare flawed prints an orientation dashboard and does not scan — use flawed . or flawed scan.
Related in AppSec
Flowise MCP Security Bypass Enables RCE
Inside an Oracle Database SQL Injection Attack | Huntress
OASIS
