Skip to main content

kwaro

A free, open-source security scanner that finds, proves, fixes, and verifies vulnerabilities on your machine.

License: AGPL-3.0 Python: 3.10-3.12 Runtime deps: zero CI: pytest matrix Platform: cross-OS

pipx install kwaro  ·  runs on a local model (Ollama) with no API key and no internet, ever.


kwaro helps you find real vulnerabilities in your code by chatting with a local AI agent. No paid APIs, no Docker stacks, no lock-in. It runs anywhere: Windows, macOS, Linux, and WSL.

Most scanners stop at "here is a bug." kwaro goes further, all locally, all for free: it finds a vulnerability, proves it with a runnable proof-of-concept, fixes it with a codebase-aware patch, and verifies the fix by re-scanning. You watch a finding go from "reported" to "proven" to "fixed and verified" in one session, on your own hardware.

kwaro is fully built and released (v0.6.0). Architecture, research, and all design decisions live in docs/. This README is the user-facing entry point.

Why kwaro

  • 100% free by default. Runs on a local model via Ollama. No credit card, no account, fully offline. Paid models (OpenAI, Groq, OpenRouter, and any OpenAI-compatible API) are opt-in via bring-your-own-key.
  • Privacy as a feature, not a footnote. Your code never leaves your machine. With the default local provider, analysis is fully offline. There is no telemetry, no crash reporting, no analytics. This is a hard requirement for fintech, banking, and blockchain teams blocked from cloud scanners.
  • Cross-platform, zero runtime dependencies. Pure Python 3.10+, standard library only for the CLI. One codebase on Windows, macOS, Linux, and WSL.
  • Proof, not opinion. Every finding shows its evidence chain: static rule hit? model confirmation? PoC verified? Confidence is derived from the loop, not asserted by the model.
  • Any codebase, any domain. Generic scanning plus community domain profiles (fintech, blockchain, AI apps). Profiles are plain files anyone can write and submit.
  • Two interfaces, one engine. Talk to it in your terminal (kwaro chat) or in a browser UI (kwaro serve). Same agent, same findings.
  • Open source, AGPL-3.0. Free for everyone, forever. Forkers must stay open.

The loop that makes it different

Find -> Prove -> Fix -> Verify, all on your machine, for free.

  1. Find via static analyzers and model triage.
  2. Prove by generating and (optionally) running a PoC that shows the actual crash.
  3. Fix via the chat agent that edits the file with a real remediation.
  4. Verify by re-scanning the changed file to confirm the bug is gone.

The math (why you can trust it)

kwaro's differentiator is that confidence and termination are derived from the find/prove/fix/verify loop, not asserted by the model. Four primitives make this explicit and honest. All are pure stdlib Python, zero runtime deps, offline-friendly. Full detail and verified examples are in docs/math.md.

  • Bayesian confidence. Each finding starts from a low base rate (candidate flags are mostly noise, prior = 0.05). Evidence collected during prove/verify updates the posterior with Bayes rule. A finding is only reported if its posterior clears the bar. The model's own "I'm 90% sure" number is ignored.
  • Loop-variant termination. The run state has a measure V = unproven + unfixed + unverified. Each pass strictly decreases V until it reaches 0 (or hits a safety cap), so the loop provably terminates. The trace is visible in output.
  • Pipeline graph + trace validator. Find/Prove/Fix/Verify is a directed graph. A completed run must be a legal walk, so prove can never be silently skipped and every finding carries full lineage.
  • SPRT stop rule. Wald's Sequential Probability Ratio Test sets error-rate budgets (alpha = false-positive rate, beta = missed rate) and stops as soon as the accumulated log-likelihood ratio crosses a bound. This controls both error rates by design instead of relying on an arbitrary threshold.

Example from docs/math.md: a real SQLi climbs from posterior 0.05 to 0.89 as static and PoC evidence arrives; a false alarm the model also rated 0.90 collapses to 0.02 because the verify stage found no real exposure. The math decides, not the model.

Features

Feature Status Notes
Static analyzers (secrets, SQLi, XSS, path traversal, weak crypto) shipped pure-Python, zero-dep, CWE-mapped
Domain profiles (generic, fintech, blockchain, ai_app) shipped select which analyzers run
Math spine (Bayes, variant, graph, SPRT) shipped drives confidence, ranking, termination
Pipeline ranking (L3 severity bands + composite confidence) shipped static > model, PoC-verified > unverified
De-duplication by root cause (L4) shipped merges by root cause, not tool output
SARIF 2.1.0 + JSON export shipped carries the math fields under properties
Diff-aware rescan (L9) shipped analyze changed files only
Browser UI via serve extra shipped hand-written bundle, no React build
CI/CD guard (--diff + SARIF) planned GitHub code scanning output
Tree-sitter AST + intraprocedural taint shipped (kwaro[ast] extra) Rust (Solana rules) + Python/JS taint; see docs/coverage.md
Sandbox PoC execution (--execute-pocs) shipped (opt-in) no network, timeout, VERIFIED only on explicit confirm marker

Coverage and honesty

kwaro has two analysis layers. The base layer is pure-Python regex over 8 extensions (secrets, SQLi, XSS, path traversal, weak crypto): fast and zero-dependency. The kwaro[ast] extra adds tree-sitter AST depth: Rust/Solana rules (missing signer check, missing ownership check, unchecked arithmetic) and intraprocedural taint for Python and JS/TS (untrusted input reaching SQL/shell/eval/DOM sinks, with sanitizer awareness). A language is only claimed "covered" once its seeded fixture passes the eval. The per-language matrix lives in docs/coverage.md. We target free, local, Semgrep-Community-Edition-class breadth plus the find/prove/fix/verify loop, not "beat CodeQL" on deep multi-step taint in v1. Taint scope is intraprocedural only; the PoC sandbox is process-level containment, not a VM.

Install

The CLI has zero third-party dependencies and one pure-Python wheel covers Windows, macOS, and Linux. On Debian/Ubuntu and some macOS setups, system Python is externally managed (PEP 668), so pip install system-wide is blocked. Use pipx (recommended for a CLI app), pip install --user, a venv, or a package manager.

# Recommended (isolates the app, exposes the `kwaro` command globally)
pipx install kwaro

# PyPI, into a venv or user site
pip install kwaro            # in a venv, or: pip install --user kwaro
uv tool install kwaro        # if you use uv

# macOS / Linux
brew install kwaro           # after: brew tap vip-ultr/kwaro

# Windows
scoop install kwaro         # after: scoop bucket add kwaro https://github.com/vip-ultr/scoop-kwaro

kwaro init                   # detect Ollama, write config (free/offline default)

If you only need the CLI (scan/chat), the plain install above is enough. The browser UI needs the serve extra (see below).

If you used pip install --user, make sure ~/.local/bin is on your PATH (export PATH="$HOME/.local/bin:$PATH").

The browser UI is an optional extra. If you installed the plain package, add it with pipx inject kwaro fastapi uvicorn websockets (or reinstall with the extra):

pipx install "kwaro[serve]"  # or, in a venv: pip install "kwaro[serve]"
# already have plain kwaro via pipx? inject the deps instead:
pipx inject kwaro fastapi uvicorn websockets

Without the extra, kwaro serve prints an install hint instead of failing.

Providers (chat only)

Scanning is fully static and offline. The chat agent needs a model, and kwaro supports two kinds with zero extra dependencies:

  • Local (default, Ollama): free, no API key, fully offline. Install Ollama, pull a code model, then kwaro init --provider ollama.
  • Groq (free API, no model pull): if you don't want to download models, set a free Groq key and kwaro uses it. No local setup.
# Groq (free tier, no local model needed)
export GROQ_API_KEY=gsk_xxx
kwaro init --provider groq          # writes provider=groq, key from env
kwaro chat ./my-repo

# Or set it in the environment only (no config write) and run:
export GROQ_API_KEY=gsk_xxx
kwaro chat ./my-repo                # auto-resolves groq from GROQ_API_KEY

# Other OpenAI-compatible hosts work the same way (BYOK):
#   openai, openrouter, together, deepseek - set the matching *_API_KEY env var,
#   or put base_url + api_key in ~/.kwaro/config.toml.

The default model for Groq is llama-3.3-70b-versatile (a strong, free code model); override by editing config.toml (model = "qwen-2.5-coder-32b" for coding tasks). Local-first stays the documented default; Groq is the easy opt-in for users who don't want to manage local models.

For development from source:

git clone https://github.com/vip-ultr/kwaro
cd kwaro
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev,serve]"
pytest                      # runs the suite + the in-repo fixture repo

Quickstart

kwaro init                 # detect Ollama, recommend a local model
kwaro scan ./my-repo       # static-first scan, free, offline
kwaro chat ./my-repo       # conversationally find, prove, fix, verify
kwaro serve                # open the browser UI at http://127.0.0.1:8080

CLI reference

kwaro init                                  # first run: detect Ollama, write config
kwaro scan ./my-repo                        # static scan + math spine, ranked report
kwaro scan ./my-repo --profile fintech      # domain-tuned analyzers
kwaro scan ./my-repo --rescan               # diff-aware: only changed files (L9)
kwaro scan ./my-repo --format sarif         # SARIF 2.1.0 export (math in properties)
kwaro scan ./my-repo --format json          # JSON export (L7 fields + math)
kwaro scan ./my-repo --pocs                  # generate PoC stubs per finding (offline)
kwaro chat ./my-repo                        # interactive loop: model uses tools, then reports
kwaro serve [--port 8080]                   # browser UI (needs the serve extra)

kwaro chat needs a model. With no model configured it falls back to the static analyzer; point it at local Ollama (no key) or a hosted bring-your-own-key provider.

Example scan output

kwaro: scanning ./my-repo (profile: generic, 5 analyzers, math spine on)

5 unique findings (from 5 raw), 0 kept after prove/verify

loop variant trace: 5 -> 5
pipeline graph valid: True (ok)
de-duplicated: 5 raw -> 5 unique

severity | rule                 | file:line        | confidence
---------|----------------------|------------------|-----------
HIGH     | secrets.hardcoded     | app.py:1          | static
HIGH     | injection.sql-concat  | app.py:3          | static
MEDIUM   | xss.innerhtml        | static/bundle.js:5| static
MEDIUM   | traversal.open-userpath| server.go:9      | static
MEDIUM   | auth.weak-hash       | app.py:7          | static

Each finding carries its posterior, SPRT verdict, composite confidence, and PoC state, exposed in the terminal report, the browser UI, and the SARIF/JSON export.

Results (eval, L13)

The seeded fixture tests/fixtures/vuln-repo/ contains one instance of each detector. tests/test_eval.py asserts recall (every seeded rule fires) and flags unexpected rule ids as possible false positives.

Current numbers on the fixture: 100% recall on the 5 seeded rule families (secrets, SQLi, XSS, traversal, weak crypto), 0 unexpected findings. The math spine keeps 0 of 5 static candidates in the "kept" set because offline static evidence alone does not clear the posterior bar or the SPRT REAL verdict, which is the honest behavior until a PoC verifies. As the loop gains prove/verify evidence (see docs/math.md), that SQLi posterior rises to 0.89 and the finding is kept; a false alarm collapses and is dropped. Precision/recall tracking per scan is the product's headline quality metric, not raw "findings found."

Privacy and security posture

kwaro is a local scanner. The SECURITY.md policy is explicit:

  • With the default local provider (Ollama), nothing leaves your machine. No code, snippets, or findings are uploaded anywhere.
  • The only network traffic is what you opt into: a non-local provider, or an explicit version check (off by default).
  • Proof-of-concept execution is off by default. When enabled, it runs in a separate process with a temp directory, no network, and resource/time limits. It is a soft boundary, not a hard sandbox. Run untrusted PoCs in a container or VM you can discard.
  • kwaro complements, not replaces, expert review and mature SAST tools. Model output can be wrong; findings tagged model-only or unverified are lower-confidence by design.

Architecture at a glance

kwaro/
  core/         models, storage (SQLite), workspace, verify (Bayes + SPRT),
                graph (trace validator), loop (variant termination),
                rank (L3 bands + confidence), pipeline (FIND/PROVE/FIX/VERIFY),
                profiles, export (SARIF + JSON)
  analyzers/    base + registry, secrets, injection, xss, traversal, auth, prover
  providers/    base, ollama (offline default), openai-compat (BYOK)
  chat/         agent (tool-calling loop, deterministic validation)
  web/          static bundle (index.html, style.css, app.js), no React build
  serve.py      FastAPI app (lazy import; CLI stays zero-dep)

The CLI stays zero-dependency. FastAPI/uvicorn/websockets load only under kwaro serve. Detailed design and locked decisions: docs/architecture.md, docs/locked-decisions.md.

Development status

Phases 0-6 are shipped and verified (pytest green, 35/35). The CI matrix runs on ubuntu/macos/windows across Python 3.10-3.12. The only remaining pre-1.0 items are a launch demo and the optional PyPI publish step (a maintainer action). See docs/roadmap.md and BUILD.md for the phase history.

Documentation

All planning, research, and locked decisions are in docs/:

Community guidelines: CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md.

Contributing

Contributions of all kinds are welcome: code, domain profiles, docs, bug reports, and ideas. Start with CONTRIBUTING.md. The easiest high-value contribution is a domain profile (see docs/profiles.md). All commits are Developer Certificate of Origin (DCO) signed-off (git commit -s).

License

AGPL-3.0. See LICENSE. Free for everyone, forever; forkers must stay open.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

kwaro-0.7.0.tar.gz (159.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

kwaro-0.7.0-py3-none-any.whl (73.1 kB view details)

Uploaded Python 3

File details

Details for the file kwaro-0.7.0.tar.gz.

File metadata

  • Download URL: kwaro-0.7.0.tar.gz
  • Upload date:
  • Size: 159.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.14

File hashes

Hashes for kwaro-0.7.0.tar.gz
Algorithm Hash digest
SHA256 4fa23966790404516a544545554c98248909315afaf14fe8a2a2c75c8e624ddc
MD5 eba079c488cef669f2ccf5e988e18ab0
BLAKE2b-256 5a01dcc3b503902768d25b7c1fe75f68fb6ce9c3b27392b8553cd13681226fcb

See more details on using hashes here.

File details

Details for the file kwaro-0.7.0-py3-none-any.whl.

File metadata

  • Download URL: kwaro-0.7.0-py3-none-any.whl
  • Upload date:
  • Size: 73.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.14

File hashes

Hashes for kwaro-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d918cf31ae20ccaee88e194f3fdd145fa60ea055ffa6cfed18821f2a05f6778b
MD5 edf794871eb27e08b69d0a49c94247cb
BLAKE2b-256 33b5aff9c96222ae71b82378d3a18275ece795ca6d55e56545e207e6b9782d51

See more details on using hashes here.

Release history Release notifications | RSS feed

0.7.1

2 files

This release

0.7.0 This release

2 files

0.6.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page