SIS CLI
Query Guide
sis query projects findings, structure and content. Live queries analyse the file; --from-report reads supported projections from saved report JSON without rerunning detectors. Format is derived from content. Some namespaces need the PDF object graph or live bytes.
Command shape
sis query findings sample.html --format json
sis query sample.html
sis query findings --path intake --glob '*.html' --format jsonl
sis query findings --from-report report.json --format json
With two positionals the query comes first, then the input. A single positional without a source flag is the file and defaults to findings. With --path or --from-report, give only the query positional. --format accepts text, json, jsonl, yaml/yml and csv; --json remains a shorthand.
JSON uses { "file", "query", "result", ... }. Findings are under .result[]. A structured query error (.result.status == "error", with .result.error_code) is printed in every output format and the process exits non-zero: 4 for an unreadable report (FILE_READ_ERROR), 3 for another execution failure, and 2 for a bad query, a report that does not parse, or a projection the mode cannot provide (QUERY_SYNTAX_ERROR, REPORT_PARSE_ERROR, UNSUPPORTED_IN_REPORT_MODE, REQUIRES_REPORT_MODE). Exit 0 with a .result that is not an error object is the only success. Retain stderr diagnostics separately; an empty collection is not the same as an error or unavailable projection.
Multi-format namespaces
sis query findings sample.html --format json
sis query findings.composite sample.html --format json
sis query chains sample.html --chain-summary events --format json
sis query correlations sample.html --format json
sis query urls sample.html --format json
sis query iocs sample.html --format json
sis query policy.active sample.html --format json
Use chains for general detector-chain triage. Base urls and iocs are multi-format; PDF-specific aggregations such as urls.telemetry and iocs.count.unique.by_family may return QUERY_SCOPE_ERROR on another root.
Saved report projections
sis query findings --from-report report.json --format json
sis explain verdict --from-report report.json --json
sis explain members --from-report report.json
sis query shape --from-report report.json --format json
Supported portable projections include findings/counts/filtering, chains, IOCs, shape and asset selectors. The verdict, why, tree and intent spellings were removed from sis query and refuse with QUERY_REMOVED; sis explain verdict --from-report and sis explain members --from-report read a saved report instead. The input must be a single report object, not a whole JSONL capture, workspace or semantic snapshot. --from-report first validates the report’s embedded semantic_snapshot section (version, binding to the report by source_report_id and input_sha256, snapshot consistency, and a recompute of every interpretation id) and refuses with PERSISTED_ANALYSIS_REFUSED (exit 2) rather than reading a missing or invalid section as “no analysis”. Reports saved by earlier builds have no section and must be rescanned. A passing check shows the section is internally consistent and was written beside this report body (an unkeyed digest: it catches accidents, it is not authentication); it does not check that the report’s findings agree with its evidence. If the report’s findings were filtered after the scan (--focused-triage), the query is still answered, the result carries a top-level context with findings_view: "filtered" and the filter, and a stderr note says answers over findings reflect the filtered view. A scan whose artefact could not be built prints a warning on stderr and records the reason in the section; its exit status is unchanged. Unsupported live-only namespaces return UNSUPPORTED_IN_REPORT_MODE. policy.active is an exception: it describes current process policy, even with --from-report; inspect the report’s own policy field to read original provenance.
PDF-only namespaces
sis query pages sample.pdf
sis query catalog sample.pdf
sis query trailer sample.pdf
sis query 'object 45 0' sample.pdf
sis query xref.sections sample.pdf
sis query revisions.detail sample.pdf --where "meta.revision.anomaly_score >= 4"
sis query actions.chains sample.pdf --chain-summary events --format json
These need a live PDF object graph. On a non-PDF root, inspect the structured QUERY_SCOPE_ERROR. actions.chains is the PDF structural action graph; it is not the multi-format chains surface.
--where predicate grammar
Finding-shaped namespaces support comparisons ==, !=, >=, <=, substring contains, and boolean and, or, not. Quote strings with single quotes inside the expression. There is no in [...], starts_with, or bare </> in this grammar. Asset selector predicates have a separate grammar.
sis query findings sample.html --where "severity == 'High'" --format json
sis query findings sample.html --where "confidence == 'Strong' or confidence == 'Certain'" --format json
sis query findings sample.html --where "kind contains 'html:'" --format json
Field names are checked: an unknown field or an unrecognised character (including a lone =) is an error that names the offending text and suggests the nearest field. Metadata keys are reached only through the meta. prefix, for example --where "meta.revision.anomaly_score >= 4"; a bare key such as anomaly_score is not a field. sis query objects --where "provenance == 'objstm:12:0'" selects the objects held inside object stream 12 0 (provenance is indirect, objstm:<obj>:<gen> or carved_stream:<obj>:<gen>; use --deep so embedded objects are present). Numbers may be signed (-5), and quoted strings are UTF-8.
With --from-report, --where applies to the findings projections and chains. Giving it to any other portable projection (verdict, why, tree, intent, iocs, shape, asset selectors, policy.active) returns UNSUPPORTED_IN_REPORT_MODE and exits 2 rather than ignoring the predicate.
Absent values are unknown, not false
--where is three-valued: a comparison is true, false or unknown. A subject whose field was never recorded (a finding with no severity, an object with no /Filter, a context that carries no length) is unknown for every operator, == and != and contains alike, and not of an unknown is still unknown. An absent number is absent; it is never read as 0, so length < 5 does not match a subject with no length. and/or decide what they can: false and unknown is false, true or unknown is true.
sis query excludes unknown subjects. It does not return them as matches and does not treat them as evaluated non-matches. The count of distinct subjects excluded is reported as "excluded_unknown": <n> in the JSON/YAML envelope beside result (so lists, counts and arrays carry it too; in --path batch output it is a per-file field beside result), and object results also carry it inside the structure. It is present only when n is above zero. The REPL prints the same envelope. A note: line on stderr gives the same count, which is the only report in --format text. A subject evaluated by more than one filter is counted once. A subject with no stable identity (a context built without an id) is counted by its content, so two such subjects with identical content merge into one in excluded_unknown; the count is then a lower bound.
A query with [polyglot=false] therefore returns nothing and counts every node in excluded_unknown: only a recorded polyglot is known. [format=pe] and [format!=pe] are likewise unknown for any member the scanner has not identified as a known format, including plain text; select that population with [format_state=text] (see sis docs asset-graph). A format, observed_format or format_family literal that is not an identified value (exe, pe32, gzip, a typo, unknown, text) is a parse error (ASSET_SELECTOR_ERROR, exit 2), not an empty result; case is ignored and aliases such as ole are accepted.
That applies to queries that only report. A query that writes files (/bytes --extract-to, and the PDF queries js, embedded, xfa-scripts, swf and images with --extract-to and --where) acts on the selection, so it refuses when any subject is undecided: it writes nothing and exits 2, as sis extract --asset does. /text prints to stdout and stays a reporting query that carries the count.
Literals must suit the field, and a mismatch is a parse error rather than a quiet non-match: numeric fields (length, entropy, width, height, pixels, objects, evidence, script_count) take numbers, text fields take quoted strings, risky and has_doctype take 'true' or 'false', contains needs a text field, and ordered operators (<, >, <=, >=) are numeric only. risky is unknown for rows that never assess it. Image width, height and pixels are absent when the dictionary records no valid /Width or /Height, and entropy is absent for a stream that could not be read. A query that reads as “everything except High” therefore no longer silently includes the findings that have no severity. Before this change not severity == 'High' and != matched an absent field.
Other consumers decide for themselves, and each errs in its own restrictive direction: sis sanitize plan --asset treats unknown as matched (the asset is planned for removal), and sis extract --asset refuses when any selected subject is unknown.
If a namespace rejects filtering, inspect that error and filter its valid JSON result downstream.
Asset-path selectors
sis query '**' bundle.zip --format json
sis query '**/[format=pe]' bundle.zip --format json
sis query '**/[format=ooxml]/text' bundle.zip --format json
sis query '**/[format=pe]/sha256' bundle.zip --format json
sis query '**/[format=pe]/bytes' bundle.zip --extract-to carved --format json
sis query '**[polyglot=true]/views' sample.pdf --format json
A predicate on a field a node never recorded ([declared_type!=pdf] on a node with no declared type, [role=payload] on a node with no role, [producer!=x] on a node with no metadata) is unknown for = and != alike, not a match. The node is excluded and the result envelope reports "excluded_unknown": <n> (present only when above zero); --explain carries it too.
Selectors work live and over saved reports except byte-backed /text and /bytes terminals. /bytes requires --extract-to; deeply nested byte/text projections can be unavailable at the current materialisation seam. The legacy terminal name /sha256 projects a Blake3 digest; read the algorithm field instead of assuming SHA-256. See sis docs asset-graph for grammar and node attribution.
PDF streams and REPL
sis query 'stream 8 0' sample.pdf --decode
sis query 'stream 8 0' sample.pdf --raw --extract-to streams
sis query 'stream 8 0' sample.pdf --hexdump
sis query repl sample.pdf
Quote the complete stream/object query as one argument. Choose one of raw, decode or hexdump. The PDF-only REPL builds one parse context and caches detector results within the session; it is not a general multi-format shell.
Related guides
sis docs investigate— investigation loop and finding IDssis docs scan— capture and gatessis docs asset-graph— selectors and interpretationssis docs shape— structural clustering