CapaGap
Compare static capa matches with dynamic runs of the same sample. CapaGap reports which capabilities were observed, which were missing, and which appeared in only some runs.
Rules without a supported dynamic scope are excluded from coverage. Results include ranked findings, static match locations, and optional Ghidra, IDA Pro, and Binary Ninja annotations. CapaGap reads result documents; it does not run samples or contact a sandbox.
Install
Python 3.10 or newer. No runtime dependencies.
Install from PyPI:
python -m pip install capagap
capagap --help
Or install from a source checkout:
python -m pip install .
If your shell cannot find capagap, use python -m capagap instead.
Try it
The examples below use files from the source repository. Download the source and run these commands from its root directory; pip install does not copy the examples into your working directory.
The inputs are synthetic. You do not need capa or a malware sample to run them.
capagap compare examples/static.json examples/dynamic.json
Excerpt from the result:
Confidence: high
Coverage: 50.0% (2/4 comparable static capabilities observed)
Unobserved: 2
Static-only: 1 (excluded from coverage)
For your own inputs, export the static and dynamic results with capa -j. Use the same sample and ruleset for both analyses. Plain JSON and gzip-compressed JSON are accepted.
Compare runs
capagap matrix examples/static.json \
--run baseline=examples/dynamic.json \
--run interactive=examples/dynamic-interactive.json \
--condition baseline:interaction=off \
--condition interactive:interaction=on \
--format html --output reports/matrix.html
The first run is the baseline. Conditions are supplied by the analyst, not inferred from the reports. CapaGap warns when a comparison changes zero or multiple declared conditions.
The example has 75% combined coverage: three of four comparable static capabilities appeared in at least one run. Open reports/matrix.html locally to compare runs, expand evidence, and copy addresses. Search covers rule names, namespaces, ATT&CK IDs, and locations. Runtime-only and excluded matches have separate views.
HTML reports are self-contained: no server, external assets, or network requests. They include JSON export and remain readable with JavaScript disabled. Text, Markdown, and JSON output are also available.
Multiline examples use POSIX shell continuations. In PowerShell, put the command on one line or replace each trailing \ with a backtick.
Check the ruleset
capagap manifest path/to/capa-rules --output reports/ruleset.json
capagap compare static.json dynamic.json --ruleset-manifest reports/ruleset.json
Build the manifest from the rule directory used for analysis. Comparable static rules with missing or different source definitions are reported as ruleset-unverified and removed from the coverage denominator. A dynamic match that disagrees with the manifest stops the comparison.
Export annotations
capagap handoff examples/static.json \
--run baseline=examples/dynamic.json \
--output reports/handoff
The output directory contains the findings, an editable triage worksheet, import scripts, and tool-specific instructions. Static match locations are exported as RVAs and rebased against the open database.
After editing reports/handoff/capagap-triage.json, create a reviewed copy:
capagap triage apply reports/handoff/capagap-handoff.json \
reports/handoff/capagap-triage.json \
--output reports/handoff/reviewed.json
Re-importing updates the corresponding CapaGap annotations. See the command reference for selection options, review fields, and exit codes.
Limits
- A missing dynamic match is not proof that code did not execute. Trace loss, absent stimuli, packing, and extractor differences can all affect coverage.
- A ruleset manifest checks source consistency. It cannot prove that every listed rule was loaded during a historical run.
- Scores set an investigation order; they are not probabilities or severity ratings. Evidence hotspots group exact RVAs, not functions or call-graph edges.
- Absolute addresses require the static report's image base for handoff. Other address types remain in the bundle as unmappable evidence.
Analysis reports can contain sensitive paths and strings. There is no automatic redaction; review them before sharing.
Development
python -m pip install -e ".[dev]"
python -m unittest discover -s tests -v
python scripts/release_check.py
The release check runs lint, formatting checks, tests, package builds, metadata validation, and a clean-environment install test. It does not publish anything. The output directory must be empty; use --outdir path/to/empty-directory to keep an existing build.
The version is defined in src/capagap/__init__.py. Release tags use v followed by that version.
More detail: command reference, comparison model, experiment setup, and example data.
License
MIT. Copyright 2026 Sawyer Shoemaker.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file capagap-0.1.0.tar.gz.
File metadata
- Download URL: capagap-0.1.0.tar.gz
- Upload date:
- Size: 108.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cab680c093041178e62ebcc9a3e58480b1c9fa091f0edfc828384e78edb186f3
|
|
| MD5 |
cc8c37b11599ff22ff0dc127f79e13cf
|
|
| BLAKE2b-256 |
589ff748d7f4ff092268bf60ea7660d2aa80609beab252680862057432e6c8da
|
Provenance
The following attestation bundles were made for capagap-0.1.0.tar.gz:
Publisher:
publish.yml on sawyershoemaker/capagap
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
capagap-0.1.0.tar.gz -
Subject digest:
cab680c093041178e62ebcc9a3e58480b1c9fa091f0edfc828384e78edb186f3 - Sigstore transparency entry: 2753826788
- Sigstore integration time:
-
Permalink:
sawyershoemaker/capagap@b402b361253e7859215490fb0425668a4c0a5169 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/sawyershoemaker
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b402b361253e7859215490fb0425668a4c0a5169 -
Trigger Event:
release
-
Statement type:
File details
Details for the file capagap-0.1.0-py3-none-any.whl.
File metadata
- Download URL: capagap-0.1.0-py3-none-any.whl
- Upload date:
- Size: 56.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ac395a972f2b5e6f52a2e8b6c52f6c72cf3d0c3c4c00809efe4a1e22ed6f9ff
|
|
| MD5 |
6ffc2c6449529ab6aa8adf151e502b8f
|
|
| BLAKE2b-256 |
b9ba816194d00f8506cae06ce9657e9cdc5724027dcdfe8a619dfbe5a1e292a5
|
Provenance
The following attestation bundles were made for capagap-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on sawyershoemaker/capagap
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
capagap-0.1.0-py3-none-any.whl -
Subject digest:
9ac395a972f2b5e6f52a2e8b6c52f6c72cf3d0c3c4c00809efe4a1e22ed6f9ff - Sigstore transparency entry: 2753826794
- Sigstore integration time:
-
Permalink:
sawyershoemaker/capagap@b402b361253e7859215490fb0425668a4c0a5169 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/sawyershoemaker
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b402b361253e7859215490fb0425668a4c0a5169 -
Trigger Event:
release
-
Statement type: