Outputs
fstab-decode — 5,480 bytes of x86 ELF from a Linux distribution — exports as a SARIF log carrying 26 results. Twenty-three are CVEs — twenty-one against glibc, two against the minimum kernel ABI the binary declares in its .note.ABI-tag — and every one is marked floor_only: unconfirmable. Two are build-posture facts about the whole image. Exactly one names an address inside the program's own code.
{
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
"version": "2.1.0",
"runs": [{
"tool": { "driver": {
"name": "openbinary",
"informationUri": "https://openbinary.ai",
"version": "0.11.0", "semanticVersion": "0.11.0",
"rules": [ /* 26 entries — one per rule that fired, and nothing else */ ]
}},
"artifacts": [{
"location": { "uri": "002556361f8e06ec…" },
"roles": ["analysisTarget"],
"hashes": { "sha-256": "002556361f8e06ec5ff0761777bbe5283030eaab4ccee4c5ea6994147ff765fe" }
}],
"results": [
{
"ruleId": "command-exec.execvp",
"level": "warning",
"message": { "text": "Calls execvp(): PATH-search exec with array args. Same PATH-traversal exposure as execlp(); attacker control of PATH yields code execution." },
"locations": [{ "physicalLocation": {
"address": { "absoluteAddress": 1232, "kind": "function", "fullyQualifiedName": "sub_540" },
"artifactLocation": { "uri": "002556361f8e06ec…" }
}}],
"properties": { "cwe_id": 78, "confidence": "medium",
"detector": "static-rule", "reachable_from_main": false }
},
{
"ruleId": "CVE-2025-8058",
"level": "warning",
"message": { "text": "The regcomp function in the GNU C library version from 2.4 to 2.41 is subject to a double free if some previous allocation fails. …" },
"locations": [{
"logicalLocations": [{ "kind": "module", "name": "glibc 2.3.4" }],
"physicalLocation": { "artifactLocation": { "uri": "002556361f8e06ec…" } }
}],
"properties": {
"match_confidence": "floor_only", "kev": false,
"cvss": { "version": "4.0", "base_score": 5.900000095367432,
"vector": "CVSS:4.0/AV:L/AC:H/AT:P/PR:L/UI:P/VC:L/VI:L/VA:H/SC:L/SI:L/SA:H" }
}
}
],
"properties": { "tool_version": "0.11.0", "schema": "2.1.0", "arch": "x86" }
}]
}
absoluteAddress is 1232 — 0x4d0 — while fullyQualifiedName is sub_540. They are unrelated addresses: 0x4d0 sits below .text, the PLT stub for execvp and the call target this detector family records, while sub_540 is the function that made the call, rendered sub_<hex> because it carries no symbol. A consumer that reads the name as a label for the address will mis-anchor, and what the address field means differs per family. glibc 2.3.4 is not the glibc this binary ran against either; it is the symbol-version floor recovered from the ELF's version requirements, and every CVE hanging off it is named-as-affected, not confirmed-as-vulnerable.
Every output is a projection of the stored analysis through one of three surfaces: a REST API, 48 MCP tools, and a SARIF 2.1.0 log. That analysis is not one row: the binary record, the CWE-findings slot, the threat assessment, the strings sidecar, and the functions and call-graph blobs are separate per-sha stores, fused at read time by whichever surface is asking.
| Section | Answers |
|---|---|
| The SARIF document | what a log contains, and which finding families map to which severity levels |
| Result locations | how a virtual address becomes a uri a code scanner will accept |
| Limits and truncation | what is dropped past 25,000 results, and how the drop is recorded |
| Result confidence | how sure a CVE match, a reachability flag, or a threat row actually is |
| Output surfaces | which of REST, MCP, and SARIF carries what, and how each is gated |
| The query DSL | which fields are queryable, with which operators |
| Per-binary outputs | the field inventory for one analysed binary, marked by surface |
| Per-container outputs | what a firmware image, package, or archive yields instead |
| Corpus-wide outputs | the seven outputs spanning the whole corpus or a fleet |
The SARIF document
Every export is a single run with a single tool.driver named openbinary, whose version and semanticVersion are the exporter's own build — 0.11.0 above, the same string that stamps run.properties.tool_version, not the version of the engine that produced the analysis. The engine version sits on the stored record and is emitted nowhere in the log, so a binary analysed two engine releases ago still exports 0.11.0. SARIF 2.1.0 is what GitHub code scanning and DefectDojo already ingest, so an export lands in an existing queue rather than a new dashboard.
The analysed binary is a run.artifacts entry, never a result. The driver.rules catalog is derived from the results that survived, not from the rule set that could have fired, so every ruleId in the document resolves to a rule and no orphan rule is shipped.
Finding families and anchors
Five finding families project into that one run, each anchoring to a different kind of location:
| Family | ruleId | Location it anchors to | result.taxa |
|---|---|---|---|
| CWE findings, binary and source | the detector rule id (command-exec.execvp) | an address, or a path:line:col region | never — projected with an empty technique list |
| Embedded secrets | the secret rule id | the byte offset the scanner matched at | the technique ids the rule carries |
| CVE / SBOM matches | the CVE id | one logicalLocation per affected component | never |
| Behavior capabilities | the capability rule id | the attributed function, when one resolved | the technique ids the rule carries |
| Threat-ledger indicators | threat/ + the indicator rule id | the row's first evidence anchor carrying a VA | the technique ids the rule carries |
Rule id collisions
Rules dedup on the pair (family, rule id), not on the id alone. A CWE rule literally named threat/something therefore does not silently merge into the threat namespace: whichever of the two colliding keys is emitted second takes a suffix naming its own family — #cwe here — and every result referencing it is rewritten to match. Inputs with no collision emit no suffix at all.
Severity levels
Each family maps onto SARIF's four levels — none, note, warning, error — differently:
| Input | error | warning | note |
|---|---|---|---|
| CWE / CVE severity | critical, high | medium | low, info, none, unknown |
| Secret kind | private-key and symmetric-key material — PEM and DER private keys, PKCS#12, OpenPGP session keys, AES and SM4 key schedules, leaked firmware keys | every other kind: tokens, JWTs, certificates, password hashes | — |
| Threat-indicator evidence strength | Definitive, Proven | Suggestive | Presence, and any unrecognised band |
| Capabilities | — | — | all of them |
Capabilities take note unconditionally: they record what the code can do, and grading them by severity would launder a capability into a verdict.
ATT&CK taxonomy
MITRE ATT&CK rides as a single run.taxonomies component, built from the union of techniques the surviving results referenced, so every result.taxa reference resolves. When nothing referenced a technique the component is omitted rather than emitted empty — the state of every log whose findings are all CWE and CVE, since neither family emits result.taxa even where the CWE registry entry has an ATT&CK mapping.
Result locations
Every result carries an artifactLocation.uri, because GitHub code scanning drops any result without one and a virtual address is not a path.
Synthetic uris for binary results
Every binary-anchored result on the per-binary route carries the sha256 as its uri, for every format. The builder prefers the basename — stripped of any / or \ prefix and percent-encoded down to a single RFC 3986 path segment, because a control byte or a CJK character in a display name fails the schema's uri-reference check and gets the whole log rejected — then falls back to the sha256, then to the literal binary. The name seed never resolves here: the record the exporter reads carries no top-level name, size, or format, all three nested under the fingerprint.
That same absence is why the log above has such a thin artifacts entry and run.properties:
| Field | What it would carry | On the binary route |
|---|---|---|
artifacts[0].location.uri | the basename | always the sha256 |
artifacts[0].length | the file size in bytes | omitted |
artifacts[0].properties.format | ELF / PE / Mach-O | omitted |
run.properties.format, run.properties.display_name | the same two, run-level | omitted |
run.properties.arch | x86 | present — arch is a top-level record field |
The basename-and-percent-encode path does run on the two surfaces whose uris are real paths: source findings and package member paths.
What the address field means
absoluteAddress carries whatever value the producing detector recorded, and that is not one kind of thing:
| Result | absoluteAddress | fullyQualifiedName |
|---|---|---|
| CWE finding, address-bearing | the VA the detector recorded — the call target for the command-exec family, the call site for most others, the enclosing function's entry for whole-function detectors like excessive-alloc.stack | the calling function's name; falls back to location.symbol when the caller has no name |
| CWE finding with no VA | the caller's VA, when one was recorded | as above |
| Embedded secret | a file byte offset, not a virtual address | the secret's kind (rsa_private_key_der, gcp_service_account, leaked_firmware_key, …) |
| Capability | the attributed function's VA | that function's name |
| Threat-ledger row | the row's first evidence anchor carrying a VA | the resolved symbol, when one exists |
A tool that maps absoluteAddress through a section table mis-places every secret result, because a byte offset and a VA differ by the section's load bias; the only in-band signal is that the secret family omits kind: "function" on the address object. On a stripped binary with no recovered call graph, a CWE result's fullyQualifiedName falls through to the callee primitive — the name reads strcpy or system, neither the enclosing function nor a function defined in this binary.
Source file paths
Source findings carry the real relative path plus a region, treated as strictly repo-relative because that path arrives from a symbol table or an archive entry name, both attacker-controlled. Every .., ., and empty segment is dropped and each survivor percent-encoded.
| Input path | What the uri becomes |
|---|---|
../../etc/passwd | cannot escape — the .. segments are dropped |
/etc/shadow | cannot escape — the empty segment is dropped |
café/app.c | caf%C3%A9/app.c — a two-segment path with the non-ASCII bytes percent-encoded |
| nothing safe remains | the binary's synthetic artifact location, rather than a hostile uri |
Taint witnesses
A finding carrying a taint witness also emits codeFlows — one threadFlow whose locations are the proven Source → Propagate → Sink steps in order, each an address with the step's role and symbol in its message. The primary location stays the sink. That is the same shape CodeQL and Semgrep ship, so a compliance buyer can diff them.
The witness is not SARIF-only: the same ordered address / role / symbol chain rides the finding on GET /binary/{sha} and the findings tool, so a consumer that never exports SARIF still gets the proof. Only taint detectors that retain a source anchor populate it — interprocedural summary detectors prove parameter-index reachability without recording a per-operation witness, and static-rule findings carry none at all.
Threat-ledger evidence
A threat-ledger row's detail text and matched-string evidence ride the free-text message and properties fields only — never a uri or helpUri — because they are baked from bytes the adversary chose. From such a row only the numeric virtual address, and a symbol name if one resolved, is lifted into a location.
Limits and truncation
Six limits bound an export, and each records separately what it dropped:
| Limit | Value | What happens past it |
|---|---|---|
| Results per run | 25,000 | Lowest-ranked results evicted; run.properties.truncated and omitted_results record it |
| Rules per run | 25,000 | Catalog truncated after sort by emitted id |
| Locations per result | 1,000 | Caps the CVE multi-component fan-out |
| Members per package | 1,024 | Excess members are not projected; truncated_members and members_omitted record it |
| Surface bytes per member | 32 MiB | Member skipped, counted as members_oversized. Bounds projection CPU, not memory: one corrupt findings array cannot monopolise a request every other member shares |
| Concurrent package exports | 3, process-wide | 429. Not a rate limit: each in-flight export holds a blocking thread plus one member's surfaces and a bounded result set |
GitHub's twenty-runs-per-file limit is satisfied by construction — every export is exactly one run. The result cap is GitHub's too, and it is enforced continuously rather than at the end: the accumulator is a bounded keep-top-N heap holding at most 25,000 results at any moment, evicting the current worst on every push past the cap. Peak memory is a function of the cap, not of how many findings the binary produced. The eviction order is total and therefore reproducible — level descending, then rule id ascending, then insertion sequence — so two exports of the same analysis truncate to byte-identical documents.
A firmware image exports as one merged run with one artifacts entry per member and a global keep-top-N rather than a per-member share, so results compete on severity across the whole image instead of being rationed. Member results carry artifactLocation.index, the SARIF-standard disambiguator, so two members sharing an in-image path stay attributable; the single-binary path never sets it, which keeps its output byte-identical.
Selection into the 1,024 cap is a weaker guarantee than the eviction that follows it. Members are ranked on cheap signals only — the stored verdict label and the CVE rollup, no findings read — because reading every member's findings to decide which members to read is the cost the cap exists to avoid. A member with a quiet verdict and no CVEs but a large findings array is cut before its findings are ever looked at.
A package log's run.properties names both axes: package (sha256, name, kind), members_total, members_analyzed, members_projected, members_unanalyzed, members_omitted, members_failed, members_oversized, truncated_members. Members omitted by the 1,024 cap never fold into omitted_results, which counts evicted results only; truncated and omitted_results appear only when truncation actually happened.
Result confidence
Every CVE result carries its match_confidence, and the five tiers are ranked:
| Tier | Means |
|---|---|
range_matched | The advisory's affected range explicitly contains the resolved component version |
exact | The version equals an enumerated affected version from OSV |
range_unparseable | A range was present but did not parse as semver, CPE, or calver — an operator audit item |
floor_only | Only a lower bound is known — from an ELF symbol-version requirement, a .note.ABI-tag minimum, or a signature-mined build. The advisory names the component; the range cannot be confirmed |
name_only | No version at all; the advisory matched on component name |
floor_only is not a defect being hidden — it is the alternative to fabricating a range_matched from a minimum. It is also the majority case for a distro ELF: 23 of fstab-decode's 26 results are floor_only, all inherited from a glibc 2.3.4 floor that the real deployed glibc almost certainly exceeds.
Reachability flags, capabilities, and threat rows each carry their own convention for what a missing or ungraded value means:
| Signal | Property | Emitted when | What absence or fallback means |
|---|---|---|---|
| Reachability from entry | reachable_from_main | the analysis recorded a definite true or false | absent means unknown, never flattened to false |
| Pre-auth reachability | reachable_before_auth | the same rule | absent when no network-listener roots resolved at all, the normal state for a command-line tool |
| Capability classifier | confidence — sound or heuristic — plus superset, superset_kind, low_confidence, category and the evidence, all verbatim in result.properties | every capability | The classifier's third tier, superset, has no confidence value of its own: it serializes as sound with superset: true and a superset_kind naming the leaf (taint-flow, reachability, resolved-argument, dynamic-resolution, XPC). scoring_eligible: false marks a real capability that deliberately carries no verdict weight |
| Threat-row evidence band | the SARIF level | every threat-ledger row | a missing, mistyped, or unrecognised band maps to note, not warning |
Threat-ledger rows fail low, not high: defaulting an ungraded row upward would rank unproven evidence above a recognised-but-weak band.
Output surfaces
The same analysis leaves through three surfaces, and every named output is a projection of the stored analysis record through one of them, not a route of its own:
| Surface | Shape | Carries |
|---|---|---|
| REST | 31 artifact-scoped routes under /api/v1/binary/{sha256}, 7 under /api/v1/package/{sha256}, 2 under /api/v1/source/{sha256} | the full field inventory below |
| MCP | 48 tools | the same stored record, addressed by tool call |
| SARIF | one 2.1.0 log per binary, per source file, and per package | the five finding families |
The SARIF projector runs no analysis, opens no binary, and computes nothing new. It reads already-resolved, already-gated JSON.
Access control
The projector performs zero access control of its own; gating is inherited by composing the same read facades every other surface uses. Each route denies differently:
| Route | Denial | What the caller gets |
|---|---|---|
| Binary SARIF | no static-audit grant | a valid log with the CWE and secret families absent — not an error, not a partial document, not a 403 |
| Binary SARIF | binary not visible | the same not-found every other read returns |
| Package SARIF | caller is not Pro | 403 for the whole document. The gate runs after the visibility check, so an unseen package still 404s and the 403 never leaks existence |
/api/v1/source/{sha256}/sarif | any denial, a missing static-audit grant included | 404 |
Static audit is grant-only: no plan tier clears it, Pro included. It sits with raw binary download, whole-binary reconstruction, corpus-wide statistics, and curator annotation in the set reachable only through a per-user grant an operator issues by hand. Two of the five SARIF families — CWE findings and secrets — are therefore absent from a paying Pro customer's log by default, and the CWE result opening this brief is reachable only by a grant-holding caller. One carve-out: on a deployment with no auth provider configured, the operator running the binary is the data owner, so every grant-only feature is on.
Projector dependencies
The projector depends on serde, serde_json, and thiserror — no analysis code, no storage, no typed finding model — and that last omission is load-bearing. The identifying fields of every finding family, rule ids and summaries and ATT&CK id lists, are interned strings that serialize out but round-trip back to empty through the analysis types. Re-reading the JSON through those types would silently blank every rule id in the export. Having no access to them makes that mistake unavailable.
The source SARIF route
/api/v1/source/{sha256}/sarif collapses every denial into one 404, a missing static-audit grant included, because on that route the gate result is itself the existence signal. Since shas are content-addressable, an attacker computes them from files they already hold; a 200-with-empty-log would answer "was this exact file uploaded and scanned by someone" at one request per candidate — a corpus-wide file-membership oracle. The uniform 404 closes it.
The query DSL
The corpus answers a compositional DSL whose identifiers are a hand-maintained subset of the indexed fields, not the display fields and not the index itself.
| Field kind | Identifiers |
|---|---|
| Tokenized text | frameworks, framework_paths, entitlements, imported_symbols, endpoints, apis_called, static_libs, bsd_syscalls, mach_traps, dlsym_targets, xpc_services, indicator_rule_names, mitre_technique_ids, name |
| Numeric | including file_size, function_count, known_vuln_count, max_section_entropy, text_entropy |
| Facets | platform, arch, binary_type, mach_o_type, signing_status, package (curated provenance, independent of the byte-derived platform), component, indicator_categories, indicator_rules, attack_techniques, consistency_kinds, vuln_severity, has_kev, hardening_feature, hardening_severity, is_apple_signed, and the eight action_* booleans |
| Indexed, but absent from the DSL | verdict, capabilities, analyze_status, linkage, named_coverage, named_bp, cwe, cwe_rule, finding_severity |
Those last nine are real indexed fields — most counted by the corpus-statistics endpoint, named_bp a sortable numeric, verdict additionally filterable through the search endpoint's own verdict parameter. They are absent because the DSL's field table is a separate hand-maintained list, not a view of the schema.
The queryable field set is versioned — schema 12 today — and the version is a wipe boundary, not a migration: adding a field bumps it and every existing index must be rebuilt before the new identifier answers anything.
Each field kind fixes its legal operators:
| Field kind | Operators | Cannot express |
|---|---|---|
| Flat facets and hashes | ==, != | — |
| Multi-value facets | ==, !=, ANY, ALL, CONTAINS | — |
| Numeric | the comparisons | — |
| Tokenized text | ==, ANY, CONTAINS — all three the same term match at token granularity | != is rejected outright, so there is no way to ask which binaries lack a syscall |
file_size > 100000 and platform == "FreeBSD" → 67 binaries
The same expression rides the search endpoint's filter= parameter.
Per-binary outputs
One analysed binary yields the fields below. api marks a REST endpoint, mcp an MCP tool; an entry carrying neither is computed and consumed internally.
Identity and status
| Output | What it carries | Surfaces |
|---|---|---|
| Name | best sighting filename, else the embedded name | mcp |
| Visual fingerprint | byte-map and bigram renderings of the raw bytes, for visual triage and look-alike search | api |
| Perceptual hash | a 64-bit dHash of the bigram image, ranked by Hamming distance. The documented bands — 5 for two recompiles of the same source, 15 for the same family, past 25 for unrelated — are an assertion, not a measurement. What is measured, on synthetic inputs, is the separation: a 16-byte perturbation moves at most 8 of the 64 bits, two structurally unrelated inputs at least 10 | api |
| Analysis status | engine version, analysis timestamp, live reanalysis stage, queue position, terminal status | mcp api |
Description and provenance
| Output | What it carries | Surfaces |
|---|---|---|
| Generated description | a one-line title and prose summary of what the binary is | mcp api |
| Fingerprint identity | platform, architecture (all slices for fat binaries), binary type and product-role label (daemon / kext / cli-tool / …), signing verdict, min OS, ABI, SDK, source version, file / image / header sizes, symbol-table state, signing authority, Team ID, bundle id, UUID or build id, interpreter, source path, designated requirement | mcp api |
| Build provenance | inferred compiler, optimization level, and toolchain. On the pairwise-compare route and the command-line report, not the binary route; its statically-linked library list also falls back into the DNA fingerprint and the static_libs search field when no library profile resolved | api |
| History and sightings | first seen, last seen, total sighting count, every filename it has been observed under | mcp api |
| Membership | which curated packages and firmware images contain this binary, and at what path | api |
| App Store enrichment | live store listing for the bundle id: display name, publisher, version, category, rating, price, release dates, size, languages, what's-new, description, screenshots | api |
Structure
| Output | What it carries | Surfaces |
|---|---|---|
| Program headers | entry point, the segment and program-header table, the section-containment tree, load-command count | mcp api |
| Sections | per-section name, kind, flags, address, offset, size, entropy, content hash, embedded-format count | mcp api |
| Build hardening | each compile-time mitigation as on / off / not-applicable: PIE, stack canaries, ARC, hardened runtime, CFI, PAC, RELRO, NX, ASLR, DEP, CFG, CET, BTI, library validation | mcp api |
| Objective-C summary | class, category, and protocol name lists with counts | mcp api |
| Fat slices | per-slice arch, encryption, signing status, Team ID, entitlement count, cdhash, identity-mismatch flag | mcp api |
| Load commands | header flags, dyld environment, install name, dynamic linker, linker options, sub-clients, debug object files | mcp api |
| ELF dynamic section | interpreter, SONAME, needed libraries, RPATH and RUNPATH, bind-now versus lazy, PIE, flags | mcp api |
| PE image header | subsystem, image base, entry, alignments, sizes, stack and heap reserves, link and compile timestamps, data directories | mcp api |
| PE version info | company, product, and version strings plus the resource summary. On a stripped PE with no readable strings, this is the field that names the tool | mcp api |
| PE debug info | embedded PDB path, build GUID and age, symbol-server id | mcp api |
| CLR/.NET metadata | runtime, flags, assembly identity (name, version, culture, public-key token), MVID, referenced assemblies, and how many CIL method bodies were recovered into control-flow graphs | mcp api |
| Imports by library | imported-symbol counts grouped by library | mcp api |
System access
| Output | What it carries | Surfaces |
|---|---|---|
| Entitlements | declared entitlement claims, key and value | mcp api |
| Capabilities | the ATT&CK-mapped behavior inventory: per-capability summary, confidence, technique links, per-function attribution. See Capabilities | mcp api |
| Behavioral aspects | an aspect-taxonomy summary of what the binary does | mcp api |
| Action profile | eight booleans: opens files, opens network, spawns processes, loads code dynamically, sends Mach messages, direct syscalls, computed syscalls, likely packed. Each is also a query facet | mcp api |
| System surface and call signatures | resolved syscalls and library calls with their resolved arguments: domain, name, per-call count, argument values | mcp api |
| XPC services | per-service mach name, role, kind, call sites, sends, auth state | mcp api |
| Bundle identity | bundle id, name, executable, version, min system version, privileged helpers, authorized clients, URL schemes | mcp api |
| Consistency | declared-versus-actual divergences (unused or undeclared entitlements, private-entitlement abuse, bundle-id impersonation) with affected keys and ATT&CK links. Part of the static-audit surface, so grant-only — the key is omitted, never nulled, for callers without it | api |
Network and libraries
| Output | What it carries | Surfaces |
|---|---|---|
| Network surface | networking frameworks, static endpoint literals, resolved outbound targets (host, port, protocol, caller), DNS lookups, inbound listeners, raw-socket count | mcp api |
| Libraries and SBOM | detected libraries with name, version, supplier, function count, and evidence; the statically-linked rollup; CycloneDX 1.6 and SPDX 3.0 export | mcp api |
Functions
| Output | What it carries | Surfaces |
|---|---|---|
| Function list | every recovered function with name, address, origin (user / library / runtime / glue), library attribution, rarity marker | mcp api |
| Imports and exports | imported and exported symbols with address, and library for imports | mcp api |
| Function origins | user, runtime, library, glue, and total counts plus top libraries | api |
| Function-starts count | entries recovered from LC_FUNCTION_STARTS | api |
| Decompiled code | per-function output in any of six stages: raw disassembly, lifted IL, control-flow-graph edge list, SSA-form IL with phi nodes, structured pseudocode, typed C — with pipeline confidence | mcp api |
| Per-function context | recovered locals and arguments, one-hop callers and callees, referenced strings | api |
| Annotations | human and auto-filled names, comments, prototypes, and data names overlaid per address | mcp api |
| Annotation autofill | suggested names and comments for one function or the whole binary | mcp api |
| Rust reconstruction | the whole binary emitted as a Rust crate tarball. Whether it compiles is a measured property, reported alongside class coverage, method coverage, lift ratio, and a count of // UNKNOWN markers — not a guarantee. Gated, job-based | api |
Strings
| Output | What it carries | Surfaces |
|---|---|---|
| Interesting strings | grouped by category: identifiers, file and config paths, URLs and endpoints, IOKit constants, telemetry, format templates | mcp api |
| Full strings table | every recovered string with file offset, virtual address, encoding, and the functions referencing it; filterable, paginated, exportable. Pro | mcp api |
Threats
| Output | What it carries | Surfaces |
|---|---|---|
| Verdict | benign / suspicious / likely-malicious / malicious, with a plain-English rationale and the rule-set version | mcp api |
| Family attribution | attributed malware family names with nearest-neighbour confidence | mcp api |
| Threat ledger | total score and, per scored signal, the indicator label, category, ATT&CK links, match count, points, and evidence anchors | mcp api |
| Heuristic indicators | rule matches over strings, imports, and sections, with address, rule, and technique | mcp api |
| Threat reports | known IOC writeups this binary appears in, with cross-check links | api |
Vulnerabilities
| Output | What it carries | Surfaces |
|---|---|---|
| Known CVEs | matched CVEs with severity, CVSS, KEV, EPSS, match confidence, affected components, and CWE. See CVEs | mcp api |
| CWE code findings | static-audit findings with CWE id, severity, occurrence count, and per-site caller with inline decompile; plus reachability triage (reachable-from-entry, reachable-before-auth), the entry surfaces an attacker reaches through, a privilege-ceiling estimate, and the taint witness where one was proven. Grant-only. See Findings | mcp api |
| Secrets | detected credential and key material by kind: PEM and DER private keys, OpenSSH and PuTTY keys, PKCS#8, PKCS#12, PKCS#7 signed data, X.509 certificates, OpenPGP private and session keys, JWTs, AWS / GitHub / Slack tokens, GCP service accounts, crypt(3) and LDAP password hashes, AES and SM4 key schedules recovered from their key-expansion structure, and known-leaked firmware signing keys. Each carries a two-tier confidence — magic-only versus header-valid — location, ATT&CK links, and a redacted snippet. Grant-only, alongside the code findings | mcp api |
| Source findings | CWE findings for an extracted source file. Grant-only | api |
| SARIF export | the SARIF 2.1.0 log, per binary (mcp api), per source file, and per package (api only, Pro) | mcp api |
Lineage
| Output | What it carries | Surfaces |
|---|---|---|
| Hashes | SHA-256, SHA-1, MD5, UUID, cdhash, ssdeep, plus structural hashes over code, imports, frameworks, classes, entitlements, symbols, C++ classes, and hardening | mcp api |
| DNA fingerprint | the capability vector, structural hashes, and binary kind used for similarity | mcp api |
| Similar binaries | nearest-neighbour matches scored by Jaccard and cosine | mcp api |
| Variant cluster | a neighbourhood graph, each node carrying verdict, type, status, and similarity | mcp api |
| Visual variants | perceptual look-alikes ranked by dHash distance | api |
| Combined verdict | the malware tier fused with the similarity-neighbourhood label, with rationale | api |
| Unusual functions | functions rare relative to the reference corpus | mcp api |
| Fleet hosts | per-host inventory of where a binary is observed, with first and last seen. Fleet-gated | mcp api |
Operations and lookups
| Output | What it carries | Surfaces |
|---|---|---|
| Resolve name to sha | the first visible binary matching a name | mcp api |
| Pairwise compare | a full diff of two binaries: shared and differing hashes, symbols, strings, endpoints, similarity scores | mcp api |
| Raw download | the original uploaded bytes by sha. Grant-only | mcp |
| On-demand re-analysis | re-run the current engine over a binary you can already see, reusing the stored bytes. Pro | api |
| Call-graph navigation | god nodes (top hubs), graph summary, node search, local subgraph, path existence, reachability with a witness path, attack-surface ranking. One bounded call answers "what calls this" or "does A reach B" instead of hand-walking edges; the local subgraph around one address is also a REST route | mcp api |
| Batch host verify | up to 1,000 sha-256 hashes classified in one request into eight statuses: apple_baseline, apple_other_build, known_good, known, suspicious, known_bad, unknown, invalid. Each row carries the evidence behind it — verdict label, title, team id, sources, and the OS builds whose baseline packages contain the hash — so the client is not trusting an opaque answer. Anonymous callers classify against the public corpus; a token widens the visible set. A larger batch is a 413 naming the cap and the count received; the 8 MB body ceiling sits high enough that the entry count is always what rejects a batch | mcp api |
| Triage playbook | the bundled tool catalog and query-DSL patterns, served as an MCP resource | mcp |
| Type prediction and anomaly score | a learned product-role prediction (label, confidence, top-k) and a per-type anomaly score (percentile, flagged), reachable through search facets and the anomalies feed | api |
Fields computed but never serialized
These products are computed during analysis and reach no surface:
| Computed product | Where it goes instead |
|---|---|
| The IL abstract domains — value ranges, points-to sets, guard and dispatch reasoning | consumed by analysis passes and discarded |
| Full Objective-C, Swift, C++, Go, DWARF, and PDB metadata | only the names-only summaries and CLR identity surface; the type and structure metadata stays internal |
| Per-function machine-learning feature vectors | only the derived rarity output surfaces |
| The raw syscall, dynamic-load, and aspect surfaces | they feed the summaries above |
| IOKit surface, carved Mach traps, fileset entries, and data-in-code ranges | — |
| The completeness report card — analysis status, linkage, function-naming coverage in basis points with a by-source breakdown, and edge-resolution coverage (total call-graph edges versus those whose target is indirect or computed) | no per-binary surface; two internal consumers. The search index turns status, linkage, and naming coverage into the analyze_status and linkage facets and a five-bucket coverage histogram in corpus statistics. The pipeline reads it to declare an analysis insufficient — status empty becomes "no functions discovered", a below-floor function count with almost nothing nameable becomes "stripped/opaque". Its decode-coverage, lift-ratio, and undecoded-opcode fields are always empty: nothing measures them yet |
| The per-pass error report naming which analysis passes failed on this binary | logged only |
Per-container outputs
A firmware image, package, or archive is a distinct record family: it is never one binary, and its outputs describe a tree.
Package detail
| Output | What it carries | Surfaces |
|---|---|---|
| Identity | sha, filename, kind (firmware / app / dmg / zip / pkg / deb), size, upload time, status | mcp api |
| Observed-as identities | every distinct upload of this package: uploader, name, source, visibility, time | api |
| Unpack status | live stage, progress, queue position | api |
| Members | the extracted binaries with path, format, size, kind, language, format chain, analysis status | mcp api |
| Member progress | total, analyzed, pending, failed, not-analyzed | mcp api |
| Counts | binaries extracted, files extracted, total CVEs | api |
| Malicious rollup | worst-case verdict across members with readable findings | mcp api |
| CVE buckets | counts by severity plus the top CVEs (id, severity, EPSS, component, binaries affected) | mcp api |
| Composition and contents | artifact-kind breakdown and the per-file inventory: path, kind, size, sha, format chain | mcp api |
| Container secrets | secrets found anywhere in the container, by kind and severity, with where each was seen | api |
| SBOM | components with name, version, category, supplier, evidence, affected binaries, and CVEs; firmware CVEs; stats. CycloneDX 1.6 and SPDX 3.0 | mcp api |
Firmware readout
One readout is computed over the unpack result and carried whole by the package-description tool. See Unpacking.
| Output | What it carries | Surfaces |
|---|---|---|
| Extraction counts | written, skipped, identified-only, warnings | api |
| Container-version CVEs | CVEs implied by the firmware version banner | api |
| Format breakdown | the leaf carrier formats present | api |
| Format chains | the outer-to-inner nesting paths | api |
| Largest payloads | the biggest extracted files | api |
| Arch distribution | the CPU architectures present | api |
| Interesting findings | notable file classes: CGI, remote-access daemons, HTTP servers, shadow files, IPsec, init scripts, keys and certificates | api |
| CVE path heuristics | CVEs inferred from file-path patterns | api |
| Skipped breakdown | why entries were not extracted: symlink, traversal, encrypted, and the rest | api |
| Identified-only breakdown | formats found but not extracted, split by reason: no extractor, low confidence, depth or budget exhausted | api |
| Embedded URLs | phone-home, update, and NTP endpoints found in scripts and configs | api |
| Warnings breakdown | non-fatal integrity anomalies by kind | api |
| Vendor carriers | which vendor a carrier format implies | api |
| Embedded filesystems | the mounted filesystems present and their byte totals | api |
| Kernel envelope | Linux kernel presence, location, version | api |
| Byte coverage | how much of the raw image was identified, versus padding, versus unknown | api |
| Region map | the located byte regions of the raw image | api |
| Carved file types | what the leaf payloads are, by magic | api |
Extraction ledgers
Every extraction decision is recorded, including the negative ones.
| Output | What it carries | Surfaces |
|---|---|---|
| Written files | each extracted file's path, original path, size, format chain, source offset | api |
| Skip ledger | each rejected entry and why | api |
| Identified-only ledger | each detected-but-not-extracted format with offset, depth, and reason | api |
| Warnings ledger | each integrity anomaly and its detail | api |
| Unpack stats | total files, total bytes, max nesting depth, identified-only count | api |
| Progress stream | live extraction events: firmware version, CVEs, components, summary, completion, per-file progress | api |
The per-entry confidence scores and probe internals behind the identified-only ledger are collapsed into reason labels, and the full per-file extraction trace beyond the written and skip ledgers stays inside the unpack pipeline.
Curated packages and comparison
| Output | What it carries | Surfaces |
|---|---|---|
| Package row | id, kind (linux-distro / bsd / macos / windows / ipsw / docker / vendor), name, version, vendor, release date, source, arch, member count, baseline-versus-additive | api |
| Members preview | each member with sha, name, version, path, status, size, format, verdict | api |
| Processing rollup | done, running, queued, failed, not-analyzed, queue position | api |
| Curated SBOM rollup | components aggregated across members | api |
| Curated malicious and CVE rollup | worst-case verdict and CVE buckets across members | api |
| Reverse rollup | which packages contain a given binary | api |
| Similar packages | related packages by shared components, CVEs, format chains, findings, and dominant arch, with a score | api |
| Package diff | added, removed, and changed files between two packages, with optional per-path verdict, CVE, and component enrichment, plus counts | api |
| Operations | re-run the full unpack, analyze a single extracted child, detect Docker/OCI image and image arch | api |
Corpus-wide outputs
Seven outputs operate over the whole corpus or a fleet rather than one artifact.
| Output | What it carries | Surfaces |
|---|---|---|
| Search | full-text and faceted search, scoped to the caller's visibility | mcp api |
| Query | the DSL above, also available as the search endpoint's filter= parameter | mcp api |
| Corpus statistics | facet distributions, arch-by-platform and package-by-CWE matrices, coverage, and signature-database supply versus demand — scoped to the caller's visibility, with the three CWE facets (cwe, cwe_rule, finding_severity) stripped from callers without the static-audit grant | mcp api |
| Whole-corpus statistics | the same aggregates computed across the entire corpus rather than the caller's visible slice. Grant-only, and it 404s rather than 403s so the route's existence never leaks | mcp api |
| Discovery and anomalies | a curated feed of recent uploads and the anomaly-flagged listing | api |
| Corpus rename | rename a function across the entire public corpus. Curator-only | mcp |
| Fleet CVE and host rollup | cross-binary CVE rollup and per-host inventory over the visible corpus | api |
Reference
- SARIF 2.1.0 — the OASIS specification.
- SARIF support for code scanning — GitHub's ingest rules and the hard limits enforced above.
- Model Context Protocol — the tool-calling protocol behind the MCP surface.
- CycloneDX and SPDX — the two SBOM formats exported.