jq filter library
How Pix v1.4.0 routes every Vulnetix VDB call through a verified jq filter to keep LLM context small. Covers the library layout, verification workflow, and CLI quirks.
Pix v1.4.0 introduces a small library of jq filters at vulnetix/skills/_lib/jq/. Every skill that calls a VDB endpoint pipes the output through the matching filter so the LLM only sees the fields it actually needs.
Why
A single vulnetix vdb vuln CVE-2021-44228 -o json call returns ~4 MB (an array of 20 container views in CVE5 format). vulnetix vdb exploits CVE-2021-44228 -o json returns ~12 MB (an array of 10,000+ exploit records). Feeding either raw to the LLM blows the context budget. Filtered (sized for decision retention — keeps full Vulnetix enrichment, full affected lists, full KEV directives, recommendedVersions, etc., not just headlines):
| Endpoint | Raw | Filtered | Reduction |
|---|---|---|---|
vdb vuln | 4.0 MB | 224 KB | 94.5% |
vdb exploits | 12 MB | 17 KB | 99.9% |
vdb fixes | 87 KB | 14 KB | 84% |
vdb sightings | 294 KB | 4 KB | 98.6% |
vdb iocs get | 70 KB | 13 KB | 81% |
vdb packages search | 7 KB | 4 KB | 46% |
vdb versions | 8 KB | 6 KB | 29% |
vdb kev list | 1 KB | 1 KB | (already lean) |
The vdb vuln filter aggregates cna.affected[] across all 20 container views (each container scopes a different ecosystem — Amazon Linux entries in one, npm/maven log4j-core in another, etc.); for log4j the aggregated affected list is 639 entries, capped at the first 200 with the total surfaced as affectedTotal so the LLM can call vdb affected for the rest if needed.
Library layout
One filter per VDB endpoint Pix consumes:
| File | Endpoint(s) | Verification |
|---|---|---|
vuln.jq | vdb vuln | verified against CVE-2021-44228 |
vulns.jq | vdb vulns | partial — re-verify on first use |
fixes.jq | vdb fixes | verified |
exploits.jq | vdb exploits | verified |
sightings.jq | vdb sightings | verified |
iocs.jq | vdb iocs get | verified |
attack-techniques.jq | vdb attack-techniques get | verified |
kev.jq | vdb kev list/get | verified |
packages.jq | vdb packages search | verified |
versions.jq | vdb versions | verified |
purl.jq | vdb purl | partial |
remediation.jq | vdb remediation plan -V v2 | partial |
workarounds.jq | vdb workarounds -V v2 | partial |
triage.jq | vdb triage | partial |
scorecard.jq | vdb scorecard -V v2 | partial |
cwe.jq | vdb cwe -V v2 | partial |
ai-list.jq | vdb ai-discoveries, ai-in-wild, ai-malware, ai-assisted-exploits | partial |
“Partial” filters are inferred from the vdb-api source struct definitions; they should be verified against the live response on first use and updated if the shape diverges.
Standard skill invocation
vulnetix vdb vuln "$ARGUMENTS" -o json | jq -f "${CLAUDE_PLUGIN_ROOT}/skills/_lib/jq/vuln.jq"
CLI quirks observed in v2.7.x
A handful of vdb subcommands treat -o as output filename (not format). Use -o /dev/stdout to pipe their output:
vdb sightings <id>vdb iocs get <id>vdb attack-techniques get <id>vdb kev list/vdb kev get
Bare invocations of some subcommands return help text rather than data:
vdb iocs <id>— needsiocs get <id>oriocs list --cve-id <id>.vdb attack-techniques <id>— needsattack-techniques get <id>.vdb metrics <id>—metrics typesis the only subcommand; per-CVE metrics live invdb vuln’scontainers.adp[0].x_*blocks (usevuln.jq).
Verification workflow
When adding a new filter or updating an existing one:
- Read the response struct in
/home/chris/GitHub/Vulnetix/vdb-apifor field-name hints. - Run the live CLI:
vulnetix vdb <cmd> <args> -o json > sample.json. Pace one second between calls to avoid rate-limit retries. - Inspect actual shape:
jq -r 'if type == "array" then "array of \(length); first keys: \(.[0] | keys_unsorted)" else "object keys: \(keys_unsorted)" end' sample.json. - Draft a filter that extracts only the fields a Pix skill cares about.
cat sample.json | jq -f filter.jq | wc -c— confirm valid output and meaningful reduction.- Save to
_lib/jq/<cmd>.jqwith a header comment listing fields, source-struct path, and the CVE / package fixture used.
Filter design discipline
- Slice arrays (
.[0:N]) for top-N projections. - Truncate prose with
if length > N then .[:N-3] + "..." else . end. - Use
// nullor// "n/a"for missing fields so output is always valid JSON. - Prefer object output (
{a, b, c}) over re-keying when the result is a single record. - Don’t hand-construct CVSS — Vulnetix’s
containers.adp[0].x_threatExposurealready provides a composite score with rule breakdown.