CLI and client library for the filescan.io API
Project description
filescancli
Complete Python client and CLI for the filescan.io malware analysis API
Overview
filescancli is a Python toolkit to submit samples and URLs to filescan.io, poll for reports, and query reputation and threat intelligence. It covers every endpoint the published OpenAPI document declares, as a typed library and as a CLI, with machine-readable output including JSON, TOON, and SARIF 2.1.0.
Key Features
| Feature | Description |
|---|---|
| Full API coverage | One method per endpoint, verified against the published OpenAPI document |
| Library + CLI | filescanio package and the filescan command, same capabilities |
| Namespaced groups | scan, reports, reputation, threatintel, system, and five more |
| Settled-report polling | --wait waits for post-processing, not just the allFinished flag |
| SARIF 2.1.0 | Scan reports as code-scanning findings, validated against the schema |
| TOON | Compact, token-efficient encoding for LLM prompts |
| Typed errors | ApiError with status and detail, ConfigError, TransportError |
| Cross-platform | Windows, Linux and macOS, x64 and ARM, all covered by CI |
Supported Outputs
Structured data JSON (pretty or one-line), TOON
Findings SARIF 2.1.0 from scan reports
Terminal Column tables, automatic when stdout is a terminal
Raw passthrough Binary (logos, avatars) and text (feeds, sitemap)
Installation
From Source
git clone https://github.com/seifreed/filescancli.git
cd filescancli
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -e .
Installing the package pulls in httpx and prettytable only.
Quick Start
# Authenticate
export FILESCANIO=your-api-key
# Submit a URL and wait for the settled report
filescan scan url https://example.com --wait
# Look up a hash reputation
filescan reputation hash e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
# Export a scan report as SARIF
filescan report <report_id> <sha256> --filter allSignalGroups --format sarif -o out.sarif
# Read a scan report as a human
filescan report <report_id> <sha256> --filter f:all --format report
Authentication
The API key is resolved in this order:
--api-keyCLI flag /FileScanClient(api_key=...)FILESCANIOenvironment variable~/.filescanio.tomlconfig file (api_key, optionalbase_url)
The base URL resolves independently, in the same order: --base-url,
FILESCANIO_BASE_URL, the config file, then https://www.filescan.io.
filescan config init --api-key your-api-key
filescan config show
On POSIX the config file is created 0600 and a symlink at that path is
refused rather than followed. Windows expresses neither, so there the key
relies on the access control list its directory already carries — keep it
under your user profile.
Usage
Command Line Interface
filescan scan file sample.bin --tags "malware|test" --wait --wait-timeout 1800
filescan scan url https://example.com --wait
filescan scan report <flow_id>
filescan report <report_id> <sha256> --filter general --filter allSignalGroups
filescan search "mirai" --verdict malicious --filetype peexe --age 7 # --filetype validates against the platform's type list
filescan reports public --page 1
filescan reports matches <report_id> --filter verdict=malicious
filescan report-download <report_id> --as pdf -o report.pdf # misp|stix|html|pdf
filescan files availability <sha256> [<sha256> ...]
filescan files download <sha256> --password infected -o sample.zip
filescan similarity <sha256> --min-similarity 80 # endpoint deprecated upstream
filescan reputation hash <sha256> [<sha256> ...] # bulk when several
filescan reputation ioc domain evil.example [more...] # domain|ip|url
filescan threatintel prevalence --domain example.com --days 30
filescan threatintel similars --imphash <hash>
filescan feed reports
filescan system version|info|config|features|mitre|mbc|news|...
filescan system yara -o rules.json
filescan system logo --theme dark -o logo.svg
filescan users avatar <account_id> -o avatar.png
filescan users tags|ioc-stats|interesting
filescan misc openapi|sitemap
Command Groups
| Command | Description |
|---|---|
filescan scan |
Submit files or URLs, fetch and poll flow reports |
filescan report |
A single report by report ID and file hash |
filescan search / reports |
Search by text or by field (verdict, hashes, IOCs, YARA rule, age...), public listings, match filters |
filescan files |
Hash availability, sample download |
filescan reputation |
Hash and IOC reputation, bulk when several values are given |
filescan threatintel |
IOC prevalence and special-hash similarity |
filescan system |
Platform info, config, YARA, MITRE/MBC reference data |
filescan users / feed / misc |
Account data, public feeds, OpenAPI and sitemap |
filescan config |
Write and inspect the local credentials file |
Output Formats
--format picks how a response is rendered. Without it the CLI shows a
table when stdout is a terminal and JSON when it is piped or
redirected, so filescan system info is readable and
filescan system info | jq still works.
| Format | Notes |
|---|---|
table |
Column layout via prettytable. Default on a terminal. |
json |
Pretty-printed; --raw puts it on one line. Default when piped or with -o. |
toon |
TOON — compact, token-efficient, for LLM prompts. Encoding only, comma delimiter, two-space indent. |
sarif |
SARIF 2.1.0 for scan reports, so findings can be uploaded to code-scanning tools. |
report |
A readable digest of a scan report: verdict, tags, signal groups, per-family file details (PE/ELF/PDF/Office/LNK/Mbox), emulation, IOCs, disassembly, YARA, notable strings, extracted files, OSINT and geolocation. Sections with nothing to say are omitted. |
--format report adds colour when stdout is a terminal, honouring the
NO_COLOR / FORCE_COLOR conventions; pipes,
redirections and -o files always receive plain text. scan file and
scan url show a spinner on stderr under the same rules, so scripts stay
completely silent.
A format that cannot express a response falls back to JSON and says so on
stderr — asking for a table of a nested scan report, or SARIF of
system languages, never fails the command. Binary responses
(system logo, users avatar) and text ones (feed reports,
misc sitemap) always pass through untouched.
Reports return only basic data unless you ask for sections with --filter
(general, finalVerdict, allTags, allSignalGroups, f:all, fd:all,
dr:all, ...). --wait polls until the scan flow is fully settled, not
merely until allFinished turns true: the API sets that flag while
post-processing is still running and the report is still missing sections
such as allSignalGroups.
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Other client-side failure |
| 2 | Usage error (argparse) |
| 3 | Missing or invalid configuration |
| 4 | API returned 4xx |
| 5 | API returned 5xx |
| 6 | Network or protocol failure |
Python Library
Basic Usage
from filescanio import FileScanClient
with FileScanClient() as client:
scan = client.scan.url("https://example.com")
report = client.scan.wait_for_report(scan["flow_id"])
results = client.reports.search("mirai", page_size=10)
reputation = client.reputation.file_hash("e3b0c44298fc1c...")
prevalence = client.threatintel.prevalence(domain=["example.com"])
Groups: scan, reports, files, similarity, system, users, feed,
misc, reputation, threatintel — one method per API endpoint.
Error Handling
from filescanio import ApiError, ConfigError, FileScanClient, RequestTimeout
try:
with FileScanClient() as client:
client.reports.get("missing", "hash")
except ApiError as exc:
print(exc.status_code, exc.detail, exc.retry_after)
except RequestTimeout:
print("the API did not answer in time")
except ConfigError as exc:
print("no usable credentials:", exc)
Every failure is a FileScanError subclass, so one except FileScanError
catches the lot.
Rendering
from filescanio.render import Format, Unrepresentable, render
try:
print(render(report, Format.SARIF))
except Unrepresentable as exc:
print("that shape has no SARIF form:", exc)
SARIF and Code Scanning
SARIF is built from scan reports: one result per entry in allSignalGroups,
falling back to the report's finalVerdict, with the sample recorded as a
SARIF artifact carrying its SHA-256. Ask for the sections you want first:
filescan report <report_id> <sha256> --filter allSignalGroups --format sarif -o results.sarif
Uploading those findings to GitHub Code Scanning:
- name: Scan a sample and export SARIF
env:
FILESCANIO: ${{ secrets.FILESCANIO }}
run: filescan scan file sample.bin --wait --format sarif -o results.sarif
- name: Upload SARIF to GitHub Code Scanning
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
Requirements
- Python 3.14+
- Windows, Linux or macOS, x64 or ARM
- See pyproject.toml for dependencies and the
devextra
Development
All dependencies live in pyproject.toml; the development toolchain is the
dev extra in the same file:
python -m venv venv && venv/bin/pip install -e ".[dev]"
Quality and security gates, all of which must pass without a single warning and without any suppression:
black --check . && ruff check . && mypy .
bandit -c pyproject.toml -r . && pip-audit --skip-editable
pytest # enforces 100% coverage, tests hit a real in-process HTTP server
Nothing in tests/ is skipped or needs credentials. Two directories sit
outside the coverage scope, because a test that cannot run everywhere would
count as uncovered wherever it is skipped:
-
posix/runs with the suite but is not measured. It holds the checks for permission bits, symlink refusal, FIFO handling and broken-pipe behaviour, none of which Windows can express. -
smoke/talks to the real API, so it is not collected at all. Besides the request smoke checks it verifies that every path the published OpenAPI document declares is implemented, fetching that document rather than keeping a copy of it in the repository:FILESCANIO=your-key pytest smoke --no-cov
CI runs every gate on Ubuntu, macOS and Windows. The coverage gate is enforced once, on Ubuntu, because the CLI's broken-pipe handling needs POSIX pipe semantics: demanding 100% on Windows would fail over code Windows cannot reach rather than over a gap in the suite. Every platform still runs every test.
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 above must pass before a change is accepted, and suppressing a warning to make one pass is not an option.
Support the Project
If this project is useful in your workflows, you can support development:
License
This project is licensed under the MIT license. See LICENSE.
Attribution
- Author: Marc Rivero López | @seifreed
- Repository: github.com/seifreed/filescancli
Built for practical malware analysis workflows and security automation
Project details
Release history Release notifications | RSS feed
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 filescancli-0.1.0.tar.gz.
File metadata
- Download URL: filescancli-0.1.0.tar.gz
- Upload date:
- Size: 78.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7752f4ddb5bd4a1e79cd859428b88b257f61d13f9d7799e36c946bc6a8ba44c7
|
|
| MD5 |
6d8c90434003657ff411c2d3ccaddf5b
|
|
| BLAKE2b-256 |
1200431c7b27cecd24aec808b8ddd2bb6a488654a1cbcb5f19337220c950bd31
|
Provenance
The following attestation bundles were made for filescancli-0.1.0.tar.gz:
Publisher:
release.yml on seifreed/filescancli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
filescancli-0.1.0.tar.gz -
Subject digest:
7752f4ddb5bd4a1e79cd859428b88b257f61d13f9d7799e36c946bc6a8ba44c7 - Sigstore transparency entry: 2340652980
- Sigstore integration time:
-
Permalink:
seifreed/filescancli@c15a5e1ee66b171334caa8949214f20c116ee33c -
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:
release.yml@c15a5e1ee66b171334caa8949214f20c116ee33c -
Trigger Event:
release
-
Statement type:
File details
Details for the file filescancli-0.1.0-py3-none-any.whl.
File metadata
- Download URL: filescancli-0.1.0-py3-none-any.whl
- Upload date:
- Size: 55.0 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 |
f919a8b6063c525a161f40ee8d99770503e87dcba12a0be8f14e945353e4173a
|
|
| MD5 |
105324b15ff8723e80f0132ecc1a80bf
|
|
| BLAKE2b-256 |
b391506eee1b8076128cd9fb53c634939edd7db9f20e25eb8fe3afb2cde5906c
|
Provenance
The following attestation bundles were made for filescancli-0.1.0-py3-none-any.whl:
Publisher:
release.yml on seifreed/filescancli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
filescancli-0.1.0-py3-none-any.whl -
Subject digest:
f919a8b6063c525a161f40ee8d99770503e87dcba12a0be8f14e945353e4173a - Sigstore transparency entry: 2340652992
- Sigstore integration time:
-
Permalink:
seifreed/filescancli@c15a5e1ee66b171334caa8949214f20c116ee33c -
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:
release.yml@c15a5e1ee66b171334caa8949214f20c116ee33c -
Trigger Event:
release
-
Statement type: