CLI and API reference¶
Command line¶
$ vitrine --help
usage: vitrine [-h] [--version] {gen-corpus,train,analyze,scan,serve,demo} ...
VITRINE command-line interface (static analysis only; nothing is ever
executed).
positional arguments:
{gen-corpus,train,analyze,scan,serve,demo}
gen-corpus write synthetic inert PE corpus
train train the linear demo model on the synthetic corpus
analyze statically analyze a PE file
scan triage every PE in a directory (JSON lines)
serve run the FastAPI service + triage UI
demo end-to-end demo on synthetic samples
options:
-h, --help show this help message and exit
--version show program's version number and exit
Each subcommand has its own --help. analyze and scan only read files; nothing is executed. Files over the
parser's 128 MiB cap are skipped before they are read (scan reports them as too large). Analysis keeps the
file and several derived copies (strings, byte histograms, feature blocks) in memory, so peak memory is a large
multiple of the file size; scan very large installers one at a time.
HTTP API (vitrine serve)¶
| Method | Path | Body | Returns |
|---|---|---|---|
| GET | /health |
none | status, version, model type, benign corpus size |
| GET | /api/model |
none | model type, calibrated thresholds, metadata, known families |
| POST | /api/analyze |
raw PE bytes (application/octet-stream, at most 32 MB) |
full triage result (verdict, score, SHAP drivers, capabilities, YARA rule) |
| POST | /api/yara/validate |
{"text": "<rule>"} |
benign hits and specificity against the configured benign corpus |
The OpenAPI schema is at /openapi.json. The interactive /docs page (Swagger UI, loaded from a CDN) is served
only with vitrine serve --enable-docs; /redoc is off. At most two uploads are buffered and analysed at once
(further requests wait unread), which bounds the service's memory. A static, server-less copy of the UI is on the
demo page.
Python API¶
vitrine.triage
¶
End-to-end static triage pipeline: dissect -> tag -> featurize -> score/attribute -> YARA -> route.
Works with either verdict model:
- :class:
vitrine.gbdt.GBDTVerdictModel-- XGBoost trained on EMBER 2018 (real data), with validation-calibrated thresholds: MALICIOUS at the 0.1 %-FPR point, SUSPICIOUS at the 1 %-FPR point. - :class:
vitrine.model.VerdictModel-- the dependency-free linear model trained on the synthetic corpus (demo / CI), with fixed 0.7 / 0.3 thresholds.
analyze(data, model, benign=None, siblings=None)
¶
Statically triage one PE file (bytes are parsed, never executed).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
bytes
|
raw file bytes. |
required |
model
|
VerdictModel | GBDTVerdictModel
|
a verdict model from :func: |
required |
benign
|
list[bytes] | None
|
optional benign corpus; when given, non-benign verdicts get a candidate YARA rule validated for specificity against it. |
None
|
siblings
|
list[bytes] | None
|
optional suspected same-family samples, used for rule coverage. |
None
|
Returns:
| Type | Description |
|---|---|
TriageResult
|
TriageResult with verdict, score, SHAP attributions, capabilities, notes and rules. |
Raises:
| Type | Description |
|---|---|
PEParseError
|
the bytes are not a parseable PE file. |
load_model(path)
¶
Load either model format (EMBER XGBoost vitrine-gbdt-1 or linear demo) by sniffing the file.
train_default(n_per_family=20, seed=0)
¶
Linear model on the synthetic inert corpus (fast, dependency-free; for demos and CI).
vitrine.dissect
¶
Pure-stdlib static PE dissector. Reads bytes only; never loads, maps or executes anything.
Supports PE32 and PE32+ (x64): headers, sections, imports (incl. ordinals and delay-load),
exports, resource types, data directories, overlay, strings, entropy and imphash. Every read
is bounds-checked; malformed input raises :class:PEParseError instead of crashing.
vitrine.gbdt.GBDTVerdictModel
dataclass
¶
contributions(X)
¶
TreeSHAP values, shape (n, d+1); last column is the bias (expected margin).
vitrine.yara_synth
¶
YARA rule synthesis + specificity/coverage validation.
Rules are generated in a restricted subset of YARA (text strings, N of them, MZ check)
so VITRINE can evaluate them natively without yara-python. If yara-python is installed,
compile_check also verifies the rule compiles with the real engine.
vitrine.capabilities
¶
capa-inspired capability tagger: small declarative rules over static artifacts.
Rules match on imported API names (case-insensitive, A/W suffix-agnostic) and, when the
actual strings are available, on string substrings. When only EMBER-style string statistics
are available (no raw strings), string rules fall back to the urls/registry/paths
counters. Each rule maps to a MITRE ATT&CK technique and carries the evidence that fired it.
norm_api(name)
¶
Canonical API name: case-folded, ANSI/Unicode A/W suffix dropped, Zw folded to Nt.
tag_imports(import_names, strings=None, string_stats=None, rules=RULES, ignore_strings=())
¶
Tag capabilities from imported API names plus either raw strings or EMBER string stats.
ignore_strings (e.g. strings from the certificate table) never count as URL evidence; neither do
XML-namespace and certificate-infrastructure (CRL/OCSP/AIA) URLs.
tag_raw(raw, rules=RULES)
¶
Tag an EMBER raw-feature dict (no raw strings: string rules use the counters).