SIS CLI
Asset Graph & Asset-Path Queries
Every scanned file is modelled as a nested asset graph: the root document plus every artefact it
contains — archive entries, email parts, OOXML/OLE members, PDF embedded files and carved blobs, and
polyglot interpretations — as a single recursive tree. The engine always emits one (a bare root for a
flat document), so report.asset_graph is the source of truth for structure-aware querying.
You address nodes in that tree with an asset-path selector, an abbreviated-XPath grammar over the containment structure.
The asset-path grammar
An asset path is /-separated segments of <tag>:<name>, where <tag> is the container grammar tag
(zip, pdf, ole2, mht, iso, eml) and <name> is the member label. Wildcards and predicates
scope the match:
pdf:root— the root node.zip:root/zip:doc.pdf— a named entry.*— any single segment;**— any depth (recursive descent).[field=value]— a predicate on a node’s typed fields (see below).- A trailing
/<terminal>projects something about the matched nodes (findings, digest, text, …).
sis query "**" bundle.zip # every asset node
sis query "zip:root/*" bundle.zip # direct members of the root
sis query "**/[format=pe]" bundle.zip # any PE anywhere in the tree
sis query "**[polyglot=true]" doc.pdf # nodes in a polyglot
Output envelope: { "result": { "type": "asset_select", "source": "asset_graph", "matched_count", "nodes": [...] } }. A selector runs live (sis query "<selector>" <file>) or over a
saved report without rescanning (sis query --from-report report.json "<selector>").
Predicates
[field=value] filters matched nodes on typed asset fields. Supported fields:
format— the sniffed format (pe,pdf,zip,png,js, …). This is what routing keys on.shape— the structural identity (container.zip,markup.html,script.js, …): the source-qualified structural authority’s answer to “what is this artefact?”. Distinct fromformat(the coarse sniff) —[shape=…]matches only where a shape probe structurally resolved that format. One answer per node, even on a polyglot.candidate_shape— membership over every accepted shape candidate. Where[shape=…]asks what the artefact is,[candidate_shape=…]asks whether a format was accepted at all, so a polyglot’s non-selected views stay queryable: a PDF carrying a script-shaped member matches both[shape=document.pdf]and[candidate_shape=script.vbs].extent— the honest validation depth of any shape candidate (magic,header,header_and_footer,index,physical_layout,whole_structure, …).[extent=header]= shallow-validated members.origin—root/email_part/archive_entry/embedded_object/carved.depth— nesting depth (numeric:>=,=,>,<,<=).complete—true/false(a truncated/over-budget member isfalse).analysis_state,declared_type,sniffed_type.finding— membership:[finding=<kind>]matches nodes carrying that finding kind.sha256/blake3— the decoded-content digest (the content address is blake3).format,observed_format,format_family— a member the scanner has not identified as a known format (plain text,unknown, an unrecognised sniff) is unknown for all three: a format literal is canonicalised (format=oleisformat=ole2,format=sevenzipisformat=7z) and compared with the canonical tag the graph records. The graph’sformatisunknownfor plain text and for an unidentified member alike;format_state(below) is what tells them apart. So[format=pe]and[format!=pe]both exclude and count such a member, Aformat,observed_formatorformat_familyliteral must be an identified value: the canonical tag or an alias of it (case is ignored;format=PEisformat=pe), or one of the registry’s family names. Anything else (exe,pe32,gzip, a typo,unknown,text) is a parse error, exit 2, because such a rule could never equal a recorded value (a deny would pass the executable).[format=unknown]cannot select the unidentified population and no predicate for it exists yet.sniffed_typeis the sniffer’s raw string and takes any text. The family is derived from the canonical tag as well, so a sniff the graph cannot record as a format (such asgzip) has an unknownformat_familyin the graph.format_state— why a node has, or lacks, an identified format:identified,text(the sniffer found text and no marker in the first 512 bytes: a member with an ASCII head and a binary tail readstext),empty(zero bytes, content address present),directory,unrecognised_binary(a complete read that nothing identified),not_sniffed(the bytes were never available),truncated(a prefix identified nothing). A recorded state is known for=and!=. A node from a report written before the field existed has no state and is unknown for both operators;[format_state=not_recorded]is a parse error.format_statechanges nothing aboutformat,observed_formatorformat_family: they stay unknown for every unidentified member. To let plain text through a deny-style rule, compose the exemption explicitly:**/[format_family=executable][format_state!=text]is No for a text member and still unknown for an unrecognised, truncated, empty or unsampled one. Only compose it for a format the sniffer identifies by magic bytes, and accept thattextis a judgement of the head (pe,elf,pdf,zip, …): a markerless batch or PowerShell file sniffs astextand is still a script, so never exempttextfrom a rule on a script or markup format.polyglot—trueiff the node is a polyglot container or sits inside one. Only the positive is known: no node records that alternate-view detection ran (it does not on a scan without--deep) or that no view was dropped by the interpretation budget or a failed child scan, so[polyglot=false]is unknown for every node.polyglot_views— interpretation count on a polyglot node (numeric). It is a lower bound:>=/>and the cases a bound settles (=or<below the recorded count) are known, the rest are unknown. An ordinary node has no known count.view— scope to one polyglot interpretation, e.g.[view=zip].
A predicate on a field the node never recorded is unknown for both = and !=: [declared_type!=pdf]
selects nodes whose declared type is recorded and is not pdf, never nodes that declared nothing. Fields
that can be absent (sniffed_type literals are case-insensitive and reduce an identified format to its canonical tag, so PE is pe and ole is ole2; it has no fixed vocabulary, so other literals are not validated): role, declared_type, sniffed_type, verdict, blake3, sha256 (never
computed, so always unknown), view and shape (outside a polyglot / without a shape assessment), the
metadata fields producer, creator, toolchain, identity, binding and coverage (none recorded),
candidate_shape and extent (no shape assessment). An empty metadata category is unknown, not a known empty set: a node carries metadata whenever
any one category was recovered, so a node with only an identity says nothing about producers, bindings
or coverage. The only exceptions are the identity and binding categories, whose extraction lanes
record a completed marker. The rule is the same as for findings: a value that was found stands
(= true, != false), while “not found” (= false, != true) is known only when the lane completed;
otherwise it is unknown, even when the category has other recorded entries. producer, creator,
toolchain, coverage, candidate_shape and extent have no completion marker, so their “not found”
is always unknown.
[finding=x] and [withheld=x] are true when the finding was found, whatever the node’s state. Their
absence is only known for a node whose own analysis is complete; for a node that was not attempted,
partial, failed or refused, [finding!=x] and a not-found [finding=x] are unknown (a truncated subtree,
complete=false, does not change this: it concerns the node’s descendants, not its own findings).
Predicate values are checked when the selector is parsed: depth and polyglot_views take integers,
complete and polyglot take true/false, and ordered operators apply to numeric fields only. sis query leaves unknown nodes out and reports excluded_unknown; sis sanitize plan
treats them as matched; sis extract --asset refuses. A known-false sibling predicate still rules a node out, so
[format=pe][verdict=x] does not block a PDF member.
sis query "**/[format=pe]" sample.eml # executables at any depth
sis query "**/[format=ooxml][complete=false]" sample.zip # truncated OOXML members
sis query "**/[format=pe][polyglot=true]" doc.pdf # a PE inside any polyglot
sis query "**/[format=pe][view=zip]" doc.pdf # scoped to the zip interpretation
sis query "**/[shape=container.ooxml]" bundle.zip # members whose structural identity IS OOXML
sis query "**/[candidate_shape=container.zip]" bundle.zip # members where ZIP was accepted as any candidate
sis query "**/[extent=header]" bundle.zip # only header-deep (shallow) structural members
Terminals
A trailing /<terminal> projects something about the matched nodes:
/findings— the findings attributed to each node./summary— a compact per-node summary (path, format, counts)./sha256— the decoded-content digest (reported asblake3+digest_algo)./views— for a polyglot node, its interpretations (view_format, verdict, finding count)./text— each matched asset’s extracted safe-text (live scan only — a saved report carries handles, not bytes, so--from-report … /textrefuses)./bytes— write each matched asset’s decoded bytes to disk. Egress-gated: refuses without--extract-to <dir>(raw member bytes never go to stdout) and in report mode. Returns a manifest (asset_path,output_path,size,blake3) — never inline bytes.
sis query "**/[format=ooxml]/text" bundle.zip # per-member safe-text
sis query "**/[format=pe]/sha256" bundle.zip # per-member digest
sis query "**[polyglot=true]/views" doc.pdf # interpretations
sis query "**/[format=pe]/bytes" bundle.zip --extract-to /tmp/out # carve out the PEs
Scope note: /text and /bytes resolve for the root and first-level container members (the flat
member seam); deeply-nested members report unavailable honestly.
Polyglots
A polyglot is one asset whose bytes parse validly as multiple formats. It is modelled as a single node
with one interpretation subtree per format. Selectors are transparent through the polyglot
wrapper: zip/pdf reaches a PDF via zip → (polyglot) → pdf without naming the polyglot. Query for
polyglot relationships with [polyglot=true], [polyglot_views>=2], [view=<fmt>], and /views.
Related guides
sis docs query— the fullsis querynamespace +--wheregrammarsis docs shape— clustering files by asset-graph shape (campaign hunting)sis docs extract—extract --asset <selector>scoped extractionsis docs cdr—sis sanitize plan --assetscoped-drop planning