Reports are claims. Verdis runs them.
Every verdict traceable to a run · every rule written down and tested · no model decides an outcome
Quickstart · How it works · The verdicts · The benchmark · Threat model
Triage starts with its most expensive question
Verdis is an open-source tool that reproduces a security report before a human reads it. It builds your project at the claimed version, runs the proof of concept in a rootless sandbox with no network, and returns a verdict together with a hashed evidence bundle. It ships as a Python CLI and a GitHub Action.
A maintainer who opens a report needs five answers before anything can be decided. The first, does this reproduce?, takes the longest, and every answer that comes before it is a guess. Verdis answers all five in one pass, starting with that one.
| Question | What Verdis does | |
|---|---|---|
| 01 | Does it reproduce? | Builds the project at the claimed ref and runs the proof of concept in a sandbox with no network |
| 02 | Is it the bug the report describes? | Oracles compare what happened with the claim, and the reasoning is shown when they disagree |
| 03 | Is it in scope? | Reads your project's own policy and cites the clause that applies |
| 04 | Have we seen it before? | Two-pass dedupe against the local store and published advisories |
| 05 | Is the proof of concept safe to run? | Inspects attachments statically, quarantines risky ones, and executes only inside the sandbox |
A real run. The report and target are in examples/demo.
Start here
Run itInstall it, point it at a report and a checkout, then read the verdict card and the evidence bundle. |
Understand itHow a verdict is reached, what each of the seven verdicts means, and what the oracles do and do not establish. |
Adopt itWrite a policy for your project, add a build recipe, and run in shadow mode on your own reports before Verdis says anything to a reporter. Policy · Recipes · The Action |
Install
pipx install verdis
verdis doctor
To try what is on main before it is released:
pipx install "verdis @ git+https://github.com/usualdork/verdis"
Or from a checkout:
git clone https://github.com/usualdork/verdis.git
cd verdis && pip install .
verdis doctor
doctor checks the sandbox backend, the images and the permissions.
Verdis needs Python 3.12+ and Podman or Docker. A proof of concept is attacker-controlled code, so when no container runtime is available Verdis stops instead of running it on the host. The host-process backend (--sandbox local) exists for development, is not a security boundary, and has to be requested by name.
Pull the sandbox image once. It is built for linux/amd64; on Apple Silicon, make sandbox builds it for your machine instead:
docker pull ghcr.io/usualdork/verdis/base:latest # or: podman pull
Images are never pulled at run time: the bits that run are the ones you pinned. If your runtime is a VM or a remote daemon, point it at a directory it can mount:
export VERDIS_WORK_DIR=~/.cache/verdis
One triage
verdis triage report.md --repo ./libfoo --ref v2.3.1
Verdis verdict: REPRODUCED (Tier 1) report VDS-0042 target libfoo@v2.3.1
Why: the sanitizer oracle fired and matched the claim; the claimed impact
(memory corruption) was demonstrated
Evidence:
1. run 1: AddressSanitizer reported a diagnostic
2. run 1: sanitizer class heap-buffer-overflow
3. run 1: frame #1 parse_header (src/hdr.c:212)
Policy:
- SECURITY.md Supported versions: version 2.3.1 matches supported pattern 2.x
Duplicates: none above threshold.
Suggested next action: confirm the severity and decide on a fix
Bundle: ~/.local/share/verdis/evidence/reports/VDS-0042/bundle (run ./rerun.sh to reproduce)
Not established: exploitability beyond what the evidence shows; behaviour on
configurations other than the one tested.
The card states what was shown and what was not. A reproduction is evidence about one claim at one ref in one configuration; severity, exploitability and the reply to the reporter stay with you.
Useful flags: --compare <ref> checks whether a fix removes the behaviour, --output json|sarif feeds scripts and code scanning, --bundle-out <path> writes the evidence bundle where you want it, and --model-provider none|openai|anthropic|ollama selects the model, or none.
How it works
|
The model drafts. The sandbox runs. A language model can read the prose of a report and draft a run plan. It never decides whether a bug reproduced, and it never sees the inside of the sandbox. |
Oracles decide. Sanitizer output, crashes, uncaught exceptions, canary files, differential runs, property scripts and expected output are compared with the claim. A crash in an unrelated function does not confirm a report. |
|
Every verdict carries its proof. The bundle holds the run, the environment, the image digest and the isolation that was used, so a verdict can be re-checked instead of taken on trust. |
A person decides what happens next. Verdis drafts the reply and leaves the decision to the maintainer. In the Action's comment or label mode it posts a comment or applies the label you chose, and nothing is ever closed, rejected or banned. |
The seven verdicts
| Verdict | Meaning |
|---|---|
REPRODUCED |
An oracle fired at the claimed ref and matches the claim. Tier 1: the impact was demonstrated. Tier 2: the behaviour was demonstrated and the impact was not. |
NOT_REPRODUCED |
The build and the proof of concept ran, and the claimed behaviour did not occur. |
INCONCLUSIVE |
The build failed, the run timed out, the result was flaky, or the environment could not answer. This is the default under doubt. |
OUT_OF_SCOPE |
It reproduces, and your policy excludes it. The clause is cited with its file, heading and lines. |
DUPLICATE |
It matches a prior report or published advisory above the threshold. A suggestion, never a merge. |
NEEDS_INFO |
Required evidence is missing. The policy clause is quoted and a checklist is drafted. |
UNSAFE_ATTACHMENT |
Preflight quarantined the attachment. Observations are stated as behaviour, not as a judgement of the reporter. |
The rules run in a fixed order and the first one that applies decides. They are written in src/verdis/judge/rules.py and explained in the verdicts page.
What you get
| Capability | How it is delivered |
|---|---|
| A reproducible triage pipeline | Intake, claim extraction, policy check, dedupe, attachment preflight, plan, build, run, oracles, judgement, evidence |
| Four intake sources | Local files, GitHub private reports, GitHub issues and IMAP |
| Builds for four ecosystems | C and C++ with ASan and UBSan, Go with the race detector, Node and Python, plus a Dockerfile mode |
| Seven oracle types | Crash, sanitizer, exception, canary, differential, property and output |
| A cited project policy | .verdis/policy.yml, with hints derived from SECURITY.md and THREAT_MODEL.md when it is absent |
| Two-pass dedupe | Against the local store and against published advisories, with a threshold you set |
| Attachment preflight | Static inspection and quarantine, with an optional REA plugin for native binaries |
| A model-free path | Reports that carry an RRS block are triaged with no model at all, at zero cost |
| Evidence bundles | A hashed manifest, a rerun.sh, and verdis bundle verify |
| A GitHub Action | Shadow, comment and label modes, with the token kept out of the sandbox |
| Plugins at the edges | Intake sources, model providers, sandboxes, oracles and emitters register through entry points |
The evidence bundle
Every verdict ships with a bundle: the report as it arrived, the claim, the plan, the policy matches, the build and run logs, the oracle results, the environment, the verdict in JSON and Markdown, a draft reply, and a script that reproduces the run on another machine.
bundle/
├── manifest.json versions, hashes, the environment
├── original/report.md the report as it arrived, unmodified
├── claim.json what was understood, with source spans
├── plan.json · plan.txt the plan, in full and in readable form
├── policy_matches.json the clauses that matched, with citations
├── build.json · build.log
├── run.json · run.log every run, with exit codes and timings
├── oracles.json what each oracle saw, and whether it matched
├── fs_changes.json files the run created or changed
├── blocked_network.json outbound attempts the sandbox refused
├── environment.json image digests, kernel, tool versions
├── verdict.json · verdict.md
├── draft_reply.md a reply you can approve, edit or discard
└── rerun.sh reproduces the run elsewhere
verdis bundle verify bundles/VDS-0042/ # re-hash every file against the manifest
cd bundles/VDS-0042 && ./rerun.sh # run it again, next to the original
A bundle holds no secrets, because the orchestrator's tokens never enter a sandbox, and no exploit material beyond the reporter's own proof of concept.
What the benchmark shows
VerdisBench is a corpus of 673 cases from 25 independent target programs written in C, Python, Node and Go. Each case is a report, a target and a statement of what the verdict should be, and each program is checked in a real container before a case is written from it. The corpus includes 41 adversarial reports that try to steer the tool, 40 of them compared against a clean twin, and three upstream CVEs (libxml2 and libarchive) taken at the vulnerable commit with the proof of concept the fix brought.
Because the 539 negatives share 25 programs, the cases are not independent draws, and the upper 95% bound on the false-REPRODUCED rate is 12% when each program counts once. It is 0.71% if every case were independent. Both figures, with the reasoning, are in the benchmark reference. The corpus found nine real defects while it was being built, and each one is now a regression test.
make corpus && make corpus-check # build the cases, prove each program behaves as claimed
make benchmark-sandbox # run the whole corpus inside a container
Validate the repository
git clone https://github.com/usualdork/verdis.git
cd verdis
python3 -m venv .venv && source .venv/bin/activate
python3 -m pip install -e ".[dev]"
pytest
CI holds the core to 100% line coverage. The suite includes a sandbox conformance run that asserts the isolation is really there: no network, no writes outside the work directory, no privilege gain, and resource overruns stopped. It runs against a real container runtime in CI on every push.
The security model
Everything in a report is treated as hostile input, including the text a model reads and the commands a plan proposes.
| Layer | What applies |
|---|---|
| Runtime | Rootless Podman or Docker, a non-root user, user namespaces on |
| Privileges | All capabilities dropped, no-new-privileges, the default seccomp profile, no devices |
| Network | None in the run stage. The build stage may reach a mirror you configure, and nothing else |
| Filesystem | The source is mounted read-only, with no other host mounts |
| Limits | CPU, memory, processes, disk, open files and wall-clock time, with the process group killed on timeout |
| Secrets | The orchestrator's tokens never enter a sandbox, and the environment is scrubbed to the plan's own variables |
| Plan language | A restricted step language. A shell command string, a path that leaves the work directory, or a program off the allowlist is refused before anything runs |
No sandbox is perfect. Run Verdis on a dedicated, ephemeral runner that holds no other secrets, and start in shadow mode. The full model, including what is out of scope, is in the threat model.
Scope
Verdis is built for maintainers triaging reports about their own projects. It reproduces the report it is given. It does not find vulnerabilities, audit a codebase, or create, improve or weaponise a proof of concept, and it does not score or detect the authorship of reporters: it judges evidence, not people. A REPRODUCED verdict is evidence about one claim, at one ref, in one configuration, and it is not a statement about a project's security as a whole.
Status
| Area | State | Notes |
|---|---|---|
| Triage pipeline, CLI and evidence bundles | Alpha | Every verdict as expected across the 673-case corpus |
| Container sandbox (rootless Podman, Docker) | Alpha | Conformance suite green on Podman and Docker (macOS) and on Docker (Linux CI). No independent review yet |
| GitHub Action | Alpha | Begin in shadow mode |
| Model-assisted claim extraction and planning | Experimental | The benchmark runs model-free, so this path has no measured accuracy yet |
| Observation integrity | Known gap | Oracles read what the sandboxed process prints, so a proof of concept can print a convincing diagnostic. Read the matched output in the card, and run --compare against the fixed ref. The threat model has the detail |
| Upstream CVE corpus | Growing | 3 admitted, 40 planned |
| Maintainer pilots | Planned | No published case studies yet |
The Reproducible Report Spec
A small YAML block a reporter can add to a report. Reports that carry one are triaged without a model and deduplicate reliably. verdis preflight checks a proof of concept in a clean environment and writes the block for you.
rrs: 0.1
target:
repo: https://github.com/example/libfoo
ref: v2.3.1
claim:
class: CWE-122
component: src/hdr.c:parse_header
impact: memory corruption leading to crash
steps:
- build: default
- run: ./build/parse_demo poc.bin
inputs:
- path: poc.bin
sha256: <hash>
expect:
oracle: sanitizer
signature: heap-buffer-overflow in parse_header
config:
default: true
The specification is in the concepts page.
The GitHub Action
- uses: usualdork/verdis@v1
with:
mode: shadow
sandbox: default
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
It runs as a composite action, so the proof of concept runs in the runner's own container runtime. It asks for the minimum permissions and passes the token only to the step that posts a comment or applies a label, after the verdict exists. Run it in shadow mode for a month before you let it comment.
Documentation
| Concepts | How it works · Verdicts · The plan language · RRS |
| Guides | Quickstart · Policy · Recipes · Plugins · The Action |
| Reference | CLI · Configuration · Bundles · Benchmark · Threat model · Schemas |
Project leadership
|
Manish Tripathy — Maintainer |
Contributing
Read CONTRIBUTING.md, the Code of Conduct and the security policy before opening an issue.
Issues, corrections, recipes, oracles and adapters are welcome. Verdis gets better when people who maintain software tell us what their reports look like, and the benchmark gets better when they send the cases that break it.
Use synthetic, consented, anonymised or non-sensitive public examples. Do not submit personal data, live-case material, victim information, target lists, credentials or harmful operational detail.
License
Copyright © 2026 the Verdis contributors.
Licensed under the Apache License 2.0. Contributions are accepted under the same licence.
Verdis reproduces reports. It does not find, write or improve exploits.
Every verdict is a statement about evidence, and every decision stays with a person.
Metadata
Release files for verdis 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| verdis-0.1.1.tar.gz | 440.5 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| verdis-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 616.8 kB
Release files / verdis-0.1.1.tar.gz
| Download URL | verdis-0.1.1.tar.gz |
|---|---|
| Size | 440.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f8a5e1e0f1f63005658ca5b88b39683ae1044c01a7f189f1eae4e21894583c07
|
|
BLAKE2b-256 checksum How to use checksums |
ca863c423f3ae245165edbc16e22397f6f32162ad33e24d1d7a83030e8bf9220
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logRelease files / verdis-0.1.1-py3-none-any.whl
| Download URL | verdis-0.1.1-py3-none-any.whl |
|---|---|
| Size | 176.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8f47ccf62222d777ac4452b2b449d2af3a911a0e1e5487ef17913f70f132ce49
|
|
BLAKE2b-256 checksum How to use checksums |
2609d8a484f96b9a16c670037455cc3ee9d5b695896d5df4db8fd9104093ad33
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logSecurity for verdis 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for verdis-0.1.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.