capecli
CLI and Python library for the CAPE v2 malware sandbox REST API
Overview
capecli is a Python toolkit for driving a CAPE v2 sandbox from the command line or from code. It covers the /apiv2/ endpoints for token authentication, submission, task management, reports, IOCs, artifact downloads, sample downloads, machines, and host status.
Key Features
| Feature | Description |
|---|---|
| Endpoint coverage | Every /apiv2/ endpoint plus CAPE's distributed node API: authentication, submission, tasks (including the worker-cleanup bulk delete and the multitenancy visibility toggle), reports, IOCs, artifacts, the live-VM file stream, YARA-rule upload, samples, machines, host status, and distributed node management |
| CLI + Library | Every endpoint reachable both as a subcommand and as a typed method |
| Four output formats | Readable tables by default, plus JSON, TOON for LLM prompts, and SARIF 2.1.0 for code scanning |
| Typed | Ships py.typed; the package is mypy --strict clean with no suppressions |
| Streaming downloads | Large artifacts stream to disk instead of being buffered whole |
| Crash-safe writes | A download is written beside its destination and moved onto it only once complete, so a dropped connection or a Ctrl-C leaves neither a partial file nor a damaged earlier one |
| Credential hygiene | Passwords are read from the environment, never from argv |
| Two runtime dependencies | httpx and prettytable, nothing else |
| Cross-platform | Windows, Linux, and macOS on x64 and ARM |
| 100% line and branch coverage | Enforced by the suite, with no mocks anywhere |
Behaviour
Results Rendered as a table by default; -F selects json, toon, or sarif
Downloads Written to a path you choose; the path is printed on success
Errors CAPE error envelopes and HTTP errors raise ApiError
Exit codes 0 on success, 1 on API or configuration errors, 2 on usage
errors, 141 when the reader closed the pipe (as `| head` does)
Installation
capecli is not published on PyPI; install it from source.
git clone https://github.com/seifreed/capecli.git
cd capecli
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install .
For Development
All dependencies, runtime and development alike, live in pyproject.toml: the runtime dependency under [project] and the toolchain under [dependency-groups].
pip install -e . --group dev
Configuration
Settings resolve in this order, first match wins:
- CLI flags:
--url,--token,--timeout,--dist-url - Environment variables:
CAPECLI_URL,CAPECLI_TOKEN,CAPECLI_TIMEOUT,CAPECLI_DIST_URL - Config file:
./capecli.toml, then~/.config/capecli/config.toml
Example capecli.toml:
url = "https://cape.example.tld"
token = "YOUR_API_TOKEN"
timeout = 60
# Only needed for `dist` commands: CAPE's distributed service (default port 9003,
# a separate host and no token). Leave it out if you do not run distributed CAPE.
dist_url = "http://cape-dist.example.tld:9003"
CAPECLI_PASSWORD is read only by capecli token, and never from the command line.
Each setting resolves on its own, so a capecli.toml in the working directory can supply the
URL while the token still comes from the environment. That is worth knowing for this tool in
particular: analysis often means working inside a directory of files somebody else chose, and a
capecli.toml dropped there would point your token at the host it names. Run from a directory
you control, or pass --url explicitly, when the working directory holds untrusted files.
Quick Start
# Obtain an API token (password comes from the environment, not argv)
read -rs CAPECLI_PASSWORD && export CAPECLI_PASSWORD
capecli token --username <user>
# Submit a sample and read the report
capecli submit file /path/to/sample --machine VM-Name
capecli get report 123
# Pull the network capture
capecli get pcap 123 -o task_123.pcap
Usage
Main Commands
| Command | Description |
|---|---|
capecli token |
Exchange a username and password for an API token |
capecli submit |
Submit files, URLs, or hashes for analysis |
capecli task |
Inspect and manage tasks |
capecli get |
Fetch reports and analysis artifacts |
capecli sample |
Look up and download samples |
capecli machine |
Inspect analysis machines |
capecli status / stats / taskstats / exitnodes |
Host status and statistics |
Global Options
| Option | Description |
|---|---|
--url <url> |
CAPE base URL, overriding env and config file |
--token <token> |
API token, overriding env and config file |
--timeout <seconds> |
HTTP timeout |
--dist-url <url> |
CAPE distributed service URL, for dist commands (e.g. http://host:9003) |
-F, --output-format |
table (default), json, toon, or sarif |
--version |
Print the version and exit |
-o, --output <file> sets the destination for a command that downloads a file. It belongs to
those commands rather than to the program, so it follows the subcommand: capecli get pcap 123 -o dump.pcap.
Output Formats
Global options come before the subcommand: capecli -F toon task list 10 0.
| Format | Use it for |
|---|---|
table |
Reading at a terminal. Collections become columns; anything else becomes key/value rows with dotted paths. Columns that are empty in every row are dropped, and long values are truncated — use json when you need every byte. |
json |
Scripting and jq. Pretty-printed and key-sorted. |
toon |
Feeding results to an LLM. TOON is a lossless JSON encoding that spends far fewer tokens on syntax. |
sarif |
Pipelines and GitHub Code Scanning. SARIF 2.1.0 describes findings, so it applies only to get report (CAPE signatures) and get iocs; any other command rejects it rather than emit an empty run. |
capecli task list 10 0 # table
capecli -F json task view 123 | jq .data.status
capecli -F toon get report 123 # compact enough to paste into a prompt
capecli -F sarif get report 123 > results.sarif
Redirect to save SARIF: on get report, -o and --zip mean "save the report as a file",
which is the opposite of rendering its findings, so combining either with -F sarif is refused.
--format selects what CAPE serves: json, maec5 and litereport come back as JSON, and
every other format is a document that needs -o.
Downloads print the destination path in table, json and toon, so capecli get pcap 123
stays pipeable.
Submission
# --argument sets any top-level CAPE submission field: package, timeout, priority,
# memory, enforce_timeout, unique, clock, tags, route, platform, tlp, custom, ...
# --option adds an analyzer option to CAPE's "key=value,key=value" options string
capecli submit file /path/to/sample --machine VM-Name --argument timeout=120
capecli submit file /path/to/sample --option procdump=1 --option route=tor
capecli submit file /path/to/capture.pcap --pcap
capecli submit static /path/to/sample
capecli submit url "http://somebadness.tld"
capecli submit dlnexec "https://somebadness.tld/malware.exe"
capecli submit download-services <hash> --apikey <API_KEY> # VirusTotal or MalwareBazaar
Tasks
capecli task view 123
capecli task status 123
capecli task machine 123
capecli task list 50 0 --status reported
capecli task list 50 0 --window 60 --category file --ids
capecli task search sha256 <hash>
capecli task extsearch domain evil.tld --limit 200
capecli task latest 24
capecli task reschedule 123
capecli task reprocess 123
capecli task delete 123
capecli task delete 100-110 --status failed_analysis
capecli task delete-many 100-110 # bulk delete (CAPE's worker-cleanup path)
capecli task delete-many 100-110 --keep-reports # ... but keep the stored reports
capecli task visibility 123 public # multitenancy; needs CAPE's UI auth
Reports and Artifacts
capecli get report 123 # table to stdout
capecli get report 123 -o report.json # -o saves any format CAPE serves
capecli get report 123 --format html -o report.html # documents need -o
capecli get report 123 --zip -o report.zip
capecli get iocs 123 --detailed
capecli get config 123 --family Emotet
capecli get screenshots 123
capecli get dropped 123 --max-size 100
capecli get pcap 123
capecli get pcap 123 --variant sorted
capecli get tlspcap 123
capecli get evtx 123
capecli get keys 123 tls
capecli get etw 123 all
capecli get bulkzip 123 CAPE
capecli get selfextracted 123 --tool unpacker
capecli get surifile 123
capecli get procmemory 123 --pid 1234
capecli get fullmemory 123
capecli get payloads 123
capecli get procdumps 123
capecli get mitmdump 123
capecli get stream 123 C:/analysis/output.log # a file off the running VM (open-ended)
Samples and Infrastructure
capecli sample view sha256 <hash>
capecli sample download task 123 -o sample.bin
capecli sample download sha256 <hash> --encrypted
capecli machine list
capecli machine view VM-Name
capecli status
capecli stats 7
capecli taskstats
capecli exitnodes
YARA Rules
# Categories: binaries, urls, memory, CAPE, macro, monitor
capecli yara upload rule.yar --category CAPE
Distributed
CAPE's distributed layer is a separate service (default port 9003, no token), so
these commands use --dist-url (or CAPECLI_DIST_URL, or dist_url in the config file)
instead of the main --url.
capecli --dist-url http://cape-dist:9003 dist nodes
capecli --dist-url http://cape-dist:9003 dist status
capecli --dist-url http://cape-dist:9003 dist task 123
capecli --dist-url http://cape-dist:9003 dist node view worker1
capecli --dist-url http://cape-dist:9003 dist node add worker1 --url http://worker1:8090 --apikey <token> --enabled
capecli --dist-url http://cape-dist:9003 dist node update worker1 --disable
capecli --dist-url http://cape-dist:9003 dist node delete worker1
Caveats.
get streamis open-ended — CAPE keeps streaming the file while the guest runs, so the command returns when the guest stops or the server closes the stream.task visibilityis gated behind CAPE's web-UI session auth and needs multitenancy enabled, so a token-authenticated client usually gets 401/403 there.
Python Library
Basic Usage
from pathlib import Path
from capecli import CapeClient, load_config
config = load_config() # or Config(url="https://cape.example.tld", token="...")
with CapeClient(config) as cape:
task = cape.submit_file(Path("sample.exe"), machine="VM-Name")
report = cape.task_report(task["data"]["task_ids"][0])
cape.task_pcap(123, Path("task_123.pcap"))
All JSON endpoints return dict; download endpoints write to a Path you provide and return it.
to_toon, report_to_sarif, and iocs_to_sarif are exported for rendering results yourself.
DistClient (constructed the same way, from a Config carrying dist_url) drives CAPE's
distributed node API.
Obtaining a Token
obtain_token is the one call made through a client built without a token, since it is what produces one.
from capecli import CapeClient, Config
with CapeClient(Config(url="https://cape.example.tld")) as cape:
token = cape.obtain_token("username", "password")
Error Handling
from capecli import ApiError, CapeError, ConfigError
try:
...
except ApiError as error: # CAPE error envelope or HTTP failure
print(error, error.status_code)
except ConfigError as error: # missing or invalid configuration
print(error)
# both derive from CapeError
Development
Quality and security gates, all of which must pass with no errors, warnings, or suppressions:
black --check .
ruff check .
mypy .
bandit -c pyproject.toml -r .
pip-audit
pytest # 100% line and branch coverage; warnings fail the run
Tests run against a real in-process HTTP server that emulates the CAPE API. There are no mocks, stubs, or patched objects anywhere in the suite.
Requirements
- Python 3.14+
httpxandprettytable(the only runtime dependencies)- See pyproject.toml for the development toolchain
Contributing
Contributions are welcome.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Every gate listed under Development must pass before a pull request is merged.
Support the Project
If this project is useful in your workflows, you can support development:
Attribution
- Author: Marc Rivero López | @seifreed
- Repository: github.com/seifreed/capecli
- License: MIT
Built for practical malware analysis workflows and sandbox automation
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 capecli-0.1.0.tar.gz.
File metadata
- Download URL: capecli-0.1.0.tar.gz
- Upload date:
- Size: 70.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 |
b4b908d966c3ee36e9d9105172ebf02348640bcbe0b20ce556863c6dec58de97
|
|
| MD5 |
df14f5a6e3fcc6a3b1dcf45bc89eb1a6
|
|
| BLAKE2b-256 |
e83419a0e157f4bae1d31331afb00728353ab6bf4fb41ba7eef1f2e3d6b2efa5
|
Provenance
The following attestation bundles were made for capecli-0.1.0.tar.gz:
Publisher:
publish.yml on seifreed/capecli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
capecli-0.1.0.tar.gz -
Subject digest:
b4b908d966c3ee36e9d9105172ebf02348640bcbe0b20ce556863c6dec58de97 - Sigstore transparency entry: 2420645835
- Sigstore integration time:
-
Permalink:
seifreed/capecli@a1cce65a6d7dab5be6ba9187b1f71c5a04ed2eaa -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/seifreed
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a1cce65a6d7dab5be6ba9187b1f71c5a04ed2eaa -
Trigger Event:
push
-
Statement type:
File details
Details for the file capecli-0.1.0-py3-none-any.whl.
File metadata
- Download URL: capecli-0.1.0-py3-none-any.whl
- Upload date:
- Size: 41.4 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 |
ad14d9bcf1b4e2585d69bcf4ab1d58ffd94b588e95ab83cface0e2de532faaa1
|
|
| MD5 |
23dde089a856499251a332f789386533
|
|
| BLAKE2b-256 |
f8a3ddf6e139e8ea9782916086321f001bd83c1cd3e9e3228148f0e48b2ff7ed
|
Provenance
The following attestation bundles were made for capecli-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on seifreed/capecli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
capecli-0.1.0-py3-none-any.whl -
Subject digest:
ad14d9bcf1b4e2585d69bcf4ab1d58ffd94b588e95ab83cface0e2de532faaa1 - Sigstore transparency entry: 2420645882
- Sigstore integration time:
-
Permalink:
seifreed/capecli@a1cce65a6d7dab5be6ba9187b1f71c5a04ed2eaa -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/seifreed
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a1cce65a6d7dab5be6ba9187b1f71c5a04ed2eaa -
Trigger Event:
push
-
Statement type: