Metadata-Version: 2.5
Name: valvur
Version: 0.2.0
Summary: Fully offline security scanner for AI-generated code. Your source never leaves your machine.
Author: MaverickHQ
License-Expression: Apache-2.0
License-File: LICENSE
Requires-Python: >=3.11
Provides-Extra: dev
Requires-Dist: jsonschema>=4; extra == 'dev'
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# valvur

**A fully offline security scanner for AI-generated code. Your source never leaves your machine — and you can prove it.**

> **Status: `0.2.0`** — published and installable; `v1.0.0` follows the usability gate.
> `pip install valvur` · `ghcr.io/maverickhq/valvur`

---

## Why this exists

Every serious code-security scanner sends your code — or metadata about it — to
someone else's servers. For regulated industries and data-residency jurisdictions
that is not a preference to negotiate; it ends the procurement conversation. valvur
runs entirely on your machine, with networking switched off and your source mounted
read-only.

At the same time, AI now writes a fast-growing share of production code, and it fails
in ways classic scanners were never built to catch: hallucinated dependencies that
attackers pre-register, poisoned agent instruction files, hidden Unicode directives.

**Offline scanning, built for how AI-generated code actually breaks.**

## The three claims

This is the introduction. [`docs/EVALUATING.md`](docs/EVALUATING.md) is the audit —
the measured first run, the verification commands, the three statuses, and a plain
list of what valvur does **not** claim. Read that one sceptically.

### 1. It cannot exfiltrate your code — and you can verify it

No account, no API key, no telemetry: nothing to opt out of. On the default `offline`
profile the scanners run in containers with `--network=none`, and the host process
that launches them opens no socket either. Both halves are checked by one command:

```bash
python3 scripts/verify-offline.py /path/to/your/repo
```

On Linux the OS can deny the whole process tree the network, no privileges needed:
`unshare -rn valvur scan --profile offline`. Every run records in `run.json` exactly
what left the machine — on `offline`, the word `nothing`.

### 2. Security checks built for AI-generated code

- **Hallucinated dependencies (slopsquatting).** LLMs invent package names; attackers
  register them. No advisory database can catch it — the package is *new*, not
  known-bad. valvur checks that every declared dependency exists, **offline**, against
  a local index of every name on PyPI, npm, RubyGems, Packagist and crates.io (6.3
  million names, exact, published daily and signed). On `full` it also asks how old
  each one is, and whether it is one edit from something popular.
- **Agent-config auditing.** `CLAUDE.md`, `AGENTS.md`, `.cursorrules`, `.mcp.json`,
  skills and prompt files — scanned for injected directives, hidden Unicode
  (zero-width, bidi, tag characters), unpinned `@main` MCP refs, blanket
  `autoApprove` and permission-bypass flags.
- **A small set of Opengrep rules** for model output reaching `eval`, `exec`, a shell,
  SQL or `innerHTML`, and for unpinned actions and mutable git refs.

What is covered, and what is not, is stated on every scan rather than left to infer:

| ecosystem | exists? | known CVEs? |
|---|---|---|
| Python, npm, Ruby, PHP, Rust | offline, from the index | needs `requirements*.txt`, `uv.lock`, `poetry.lock` / a lockfile |
| JVM, Go | `full` only — no offline index exists for either registry | `pom.xml`, `go.mod` on their own |

A manifest with no lockfile beside it, or a manifest nothing here reads (a lone
`Pipfile`, say), is a **coverage note**: the run reads `inconclusive` rather than
`clean`, and names why. That reporting is the part we consider non-optional.

### 3. Ten things that matter, not four hundred findings

Findings are ranked by **whether attackers are actually exploiting them** — CISA KEV
(including the ransomware-campaign flag) and FIRST EPSS — not by CVSS theatre.
Development-only dependencies are demoted; transitive vulnerabilities come with the
path and the direct package to bump.

| Finding | CVSS | EPSS | KEV | Severity-sorted | Ranked here |
|---|---|---|---|---|---|
| CVE in a dev-only test library | 9.8 CRITICAL | 0.04% | No | **#1** | #40 |
| CVE in your production web framework | 6.5 MEDIUM | 92% | **Yes** | #40 | **#1** |

## For AI coding agents — the primary way in

Add valvur to your agent's MCP configuration:

```json
{ "mcpServers": { "valvur": { "command": "uvx", "args": ["--from", "valvur", "valvur-mcp"] } } }
```

Then ask it to scan. The server is **stdio only** — no listener, no port — and every
tool it exposes is read-only: valvur can never change your code.

Add this to your project's `CLAUDE.md` or `AGENTS.md`, so the agent uses what it has:

```markdown
## Security scanning
This project uses valvur. Scan with the `valvur` MCP tools if you have them:
call `scan`, then `scan_status` until it reports DONE. Otherwise run
`valvur scan`. Results appear in `.security-scan/`: read SUMMARY.md, then
REMEDIATION.md. Never commit `.security-scan/`. Never add suppressions without
explicit human approval. Propose fixes for approval — do not apply them and
rescan autonomously.
```

## For developers

```bash
pip install valvur          # or: uv tool install valvur
valvur update               # the image, the vulnerability database and the name index, once
valvur scan                 # offline by default; --profile full adds the networked checks
```

The first `valvur update` pulls the image (about 240MB), the vulnerability database
(118MB) and the name index (34MB, one signed artifact, built daily) — a minute or
two. Later updates take seconds; run `valvur update --if-stale` from a hook or cron,
it costs one file read when current. A scan that finds the image missing pulls it
and says so — over MCP, `scan_status` reads *"pulling ghcr.io/…"* with the size —
rather than sitting silent. If the published index cannot be reached, the five
registries are walked directly instead, which takes about seven minutes once.

Results land in `.security-scan/`:

```
.security-scan/
├── SUMMARY.md          ← start here. Bounded, leads with anything that failed
├── REMEDIATION.md      ← ranked proposal, with dependency paths and upgrade targets
├── findings.json       ← complete, normalised, schema-versioned
├── results.sarif       ← SARIF 2.1.0 for your IDE
├── sbom.cdx.json       ← CycloneDX SBOM
├── run.json            ← what ran, which versions, what was skipped and why
└── raw/                ← untouched per-tool output, so you can verify us
```

The folder ignores itself, so results are never committed. Secrets are redacted in
every artifact, `raw/` included. **You decide which fixes to apply and when to
rescan** — there is no autonomous loop. Suppressions (with mandatory expiry dates)
and `[scan] exclude` paths live in a committed `.security-scan.toml`, and every
exclusion is reported with what it cost.

## What actually does the scanning

valvur builds no detection engine. Six open source scanners do that and deserve the
credit; valvur adds orchestration, one findings model, exploit-aware ranking, and the
checks under claim 2.

| Tool | Licence | Does |
|---|---|---|
| [Trivy](https://github.com/aquasecurity/trivy) | Apache-2.0 | Dependency vulnerabilities |
| [Gitleaks](https://github.com/gitleaks/gitleaks) | MIT | Secrets, including git history |
| [OSV-Scanner](https://github.com/google/osv-scanner) | Apache-2.0 | Dependencies against OSV.dev (`full`) |
| [Opengrep](https://github.com/opengrep/opengrep) | LGPL-2.1 | Static analysis |
| [Checkov](https://github.com/bridgecrewio/checkov) | Apache-2.0 | Infrastructure misconfiguration |
| [Syft](https://github.com/anchore/syft) | Apache-2.0 | SBOM, and the dependency licences read from it |

Exploit intelligence comes from CISA KEV and FIRST EPSS — public primary sources,
auditable and mirrorable. No proprietary database; nothing to lock you in.

## What it deliberately does not do

- **No reachability analysis.** We do not prove a vulnerable function is called.
- **No autonomous fixing.** You choose the fixes.
- **No penetration testing.** No DAST, no exploitation, no scanning of deployed systems.
- **No code-quality analysis.** Security only.
- **We will not out-detect commercial SAST.** We win on trust, breadth in one
  artifact, and prioritisation — not on engine depth.

## Platforms

| | |
|---|---|
| macOS, Linux — Docker or Podman | **Supported**, tested on every commit against both runtimes |
| `linux/amd64` and `linux/arm64` | Both, **from 0.2.0**. `0.1.0rc1` was published `arm64` only — a defect, not a policy |
| Windows via **WSL2** | Supported — inside WSL valvur is running on Linux |
| Native Windows | **Not claimed.** Untested, and valvur says so at startup |
| SELinux-enforcing hosts (RHEL, Fedora) | Supported, with one deliberate friction: valvur will not relabel your source tree unless you set `VALVUR_SELINUX_RELABEL=1`. Details in [EVALUATING.md](docs/EVALUATING.md#5-what-it-does-not-claim) |

**Air-gapped?** The database, the name index and KEV all live outside the image and
each has a mirror setting, measured end to end. See [`docs/AIR-GAPPED.md`](docs/AIR-GAPPED.md).

## Contributing, and reporting problems

A finding you disagree with — especially one valvur *missed* — is a bug worth
reporting. [CONTRIBUTING.md](CONTRIBUTING.md) has the setup and the short list of
things refused on principle; [SECURITY.md](SECURITY.md) is for suspected
vulnerabilities, which for a security tool include a false clean result;
[CHANGELOG.md](CHANGELOG.md) is what changed.

## Licence

Apache-2.0 — see [LICENSE](LICENSE); chosen over MIT for the explicit patent grant.
Bundled scanners keep their own licences, listed above. valvur adds no GPL or AGPL
component; the Alpine base carries GPL userland as every Linux container does, and
the published SBOM discloses all of it ([ADR-0005](docs/adr/0005-no-gpl-tools-in-the-image.md)).
