{"threat_model":{"markdown_url":"/api/v1/registry/threat-model.md","html_url":"https://nerlo.ai/threat-model","git_commit_sha":"889227636a63ab7b78a0695e177a39c617068246","last_reviewed_date":"2026-06-04"},"scanner_manifest":{"$schema_description":"Per Req 21.6: records the verification SHA for every scanner installed in the scanner-base image. Consumers (security-conscious organizations, XPRIZE judges, future-us) can independently verify that the tool running in our image matches the claimed source. Updated in the same commit that bumps the scanner version or install method. See docs/scanner-sources.md for distribution channels.","image_base":{"name":"ubuntu","tag":"24.04","digest":"sha256:786a8b558f7be160c6c8c4a54f9a57274f3b4fb1491cf65146521ae77ff1dc54","verified_at":"2026-06-04","verified_by":"docker pull ubuntu:24.04 && docker inspect --format='{{index .RepoDigests 0}}' ubuntu:24.04"},"scanners":[{"name":"agentshield","phase":"1-sast","version":"1.5.0","source":"npm","source_url":"https://www.npmjs.com/package/ecc-agentshield","verification_method":"npm ci against committed package-lock.json with SHA-512 integrity per dep","lockfile_path":"docker/scanner-base/agentshield/package-lock.json","verified_at":"2026-06-04","verified_by":"npm install --package-lock-only ecc-agentshield@1.4.0","notes":"Lockfile lists 29 transitive deps with SHA-512 integrity hashes. `npm ci` aborts on hash mismatch. Version corrected 1.4.0 -> 1.5.0 (2026-07-23, Req 21.6 manifest accuracy): the lockfile pins the npm tarball labeled ecc-agentshield-1.4.0.tgz, but the binary it installs self-reports 1.5.0 (`agentshield --version` in the built image; matches the 1.5.0 dev-build story in agentshield_adapter.py's docstring). This field records what actually runs.","license":"MIT"},{"name":"cisco-skill-scanner","phase":"1-sast","version":"2.0.11","source":"pypi","source_url":"https://pypi.org/project/cisco-ai-skill-scanner/2.0.11/","verification_method":"pip install --require-hashes against a 91-dep hash-pinned requirements.txt (generated via `uv pip compile --generate-hashes --python-version 3.12 --python-platform linux`). Mirrors the AgentShield npm ci + package-lock.json pattern: every transitive tarball has its SHA-256 verified by pip before installation, abort on mismatch.","lockfile_path":"docker/scanner-base/cisco-skill-scanner/requirements.txt","verified_at":"2026-06-04","verified_by":"uv pip compile --generate-hashes --python-version 3.12 --python-platform linux against cisco-ai-skill-scanner==2.0.11","offline_mode":"fully supported for static + bytecode + pipeline + behavioral analyzers (no API key needed). LLM, Meta, VirusTotal, AI Defense analyzers require network and are not invoked. Smoke verified 2026-06-04: 6 findings detected on claude-skill fixture (1 CRITICAL eval, 2 HIGH command injection, 1 MEDIUM file magic mismatch, 1 LOW vague description, 1 INFO missing license) in offline mode.","license":"Apache-2.0"},{"name":"agent-audit-kit","phase":"1-sast","version":"0.3.74","source":"pypi","source_url":"https://pypi.org/project/agent-audit-kit/0.3.74/","verification_method":"pip install --require-hashes against a 3-dep hash-pinned requirements.txt (generated via `uv pip compile --generate-hashes --python-version 3.12 --python-platform linux`). Light dep tree (agent-audit-kit + click + pyyaml). Same pattern as cisco-skill-scanner.","lockfile_path":"docker/scanner-base/agent-audit-kit/requirements.txt","verified_at":"2026-08-15","verified_by":"uv pip compile --generate-hashes --python-version 3.12 --python-platform linux against agent-audit-kit==0.3.74","offline_mode":"fully supported per upstream docs ('zero network calls in the default scan path'). LLM scan (--llm-scan) and key probing (--verify-secrets) are opt-in and not invoked. Smoke verified 2026-06-04 across 6 fixtures: caught complementary findings the other Phase 1 scanners missed (e.g. clean-claude flagged HIGH for hook-trust bypass; nothing else flagged it).","bump_note":"0.3.26 -> 0.3.74 on 2026-08-15. The rule registry grew 211 -> 294 (`agent_audit_kit.rules.builtin.RULES`), and AAK-AGENT-001 lost its `` `[^`]+` `` inline-code alternation, which had made every markdown backtick span a CRITICAL. Measured on the golden corpus + a real-world tier: AAK-AGENT-001 179 -> 0 across real artifacts, 188 -> 0 on one real vendor .cursorrules, 2 -> 3 on a deliberately malicious CLAUDE.md. TWO-SIDED, DO NOT READ THIS AS PURE NOISE REMOVAL: the removed arm was also the only thing matching dangerous commands quoted in backticks that no specific arm covers. Measured on .cursorrules, backticked `nc -e /bin/sh`, `zsh -c`, `pwsh -EncodedCommand`, `base64 -d | bash`, `/dev/tcp`, `chmod 777 /etc/shadow` and `dd of=/dev/sda` go from 1 CRITICAL each to no finding of any rule at any severity; adding \\b to the sh -c arm separately loses UNBACKTICKED `zsh -c`, which 0.3.26 caught by matching inside the word. No nerlo rule compensates (cursor_rules.yaml has no shell-directive rule). See .kiro/reviews/2026-08-15-security-reviewer.md B1. Exactly one corpus delta is attributable to the bump - AAK-MCP-SDK-CVE-2026-52869-001 x2 added on good-mcp-shell-server - established by running the full 17-fixture corpus TWICE, once against the unmodified base image as a control, which came back 0 blocking. It is a TRUE positive, but NOT for the CVE in its rule id: that fixture declares `mcp>=1.1.2` (a lower bound, not a pin) and resolves to 1.27.2 in uv.lock, and per OSV 1.27.2 is already fixed for CVE-2026-52869/52870 and carries only CVE-2026-59950 (WebSocket Host/Origin). The rule bundles three advisories under one id and its >=1.28.1 floor is correct, so the finding stands and the reason does not - do not repeat the rule id as the vulnerability. Baselines regenerated in the same commit; the agent-audit-kit body changes in that one cell and no other. NOTE the regenerated baselines carry a LOCAL image tag in `image`, as the ones they replace did; that field is not reproducible from this repo.","license":"MIT"},{"name":"bearer","phase":"1-sast","version":"2.0.2","source":"github-releases","source_url":"https://github.com/Bearer/bearer/releases/tag/v2.0.2","verification_method":"SHA-256 of install script verified before exec; script verifies binary against GitHub release SHA","install_script_url":"https://raw.githubusercontent.com/Bearer/bearer/main/contrib/install.sh","install_script_sha256":"13ed2d511adb45f19fde869340744ce04d964efc7ae4662fc4001ea45057f484","verified_at":"2026-06-04","verified_by":"curl ... -o /tmp/install.sh && sha256sum /tmp/install.sh","checksum_justification":"Justification: the pinned artifact is Bearer's version-independent contrib/install.sh from the main branch, for which upstream publishes no checksum (Req 31.3 unverifiable-upstream-checksum). The install_script_sha256 pin was hand-verified at adoption, and the script itself verifies the downloaded bearer binary against checksums.txt from the v-tagged GitHub release at build time — so the shipped binary IS upstream-checksum-verified, one hop removed. Required by the 34.3 vetting gate (.github/workflows/scanner-bump-verify.yml); a bearer bump additionally requires a human approver on the PR.","limitations":"Requires network at scan time for rule registry validation; offline mode = status=incomplete. See bearer_adapter.py docstring.","deregistered_date":"2026-08-27","deregistered_task":117,"deregistered_reason":"Elastic License 2.0 restricts providing the software's functionality to third parties as a hosted or managed service, which is what serving its findings through the registry, API and CLI was. There is no commerciality test, so charging nothing is not a defence and a free registry is the WIDEST distribution of the restricted functionality.","deregistered_state":"INSTALLED IN THE IMAGE, PUBLISHES NOTHING - the same state as gosec. BearerAdapter is constructed nowhere in src/; LICENCE_RESTRICTED_SCANNERS in src/nerlo/api/server_routes.py filters historical findings out of API responses (held internally for the precision study, because holding data is not the exposed act). GET /api/v1/registry/stats reports it under installed_not_publishing.","deregistered_open_work":"The binary is still installed by docker/scanner-base/Dockerfile (grep -n 'bearer-install.sh'). Removing it needs an image rebuild plus a golden-corpus re-verify and has NOT been done.","license":"Elastic-2.0","license_exemption":{"task":117,"reason":"Elastic-2.0 forbids providing the functionality to third parties as a service. Bearer findings are being removed from the serving path (public registry, API, CLI) under task 117; the tool stays in the image only to keep historical findings reproducible for the precision study. THIS EXEMPTION IS NOT A LEGAL OPINION and must be removed, not renewed.","granted":"2026-08-21"}},{"name":"gosec","phase":"1-sast","version":"2.28.0","source":"github-releases","source_url":"https://github.com/securego/gosec/releases/tag/v2.28.0","archive":"gosec_2.28.0_linux_amd64.tar.gz","archive_sha256":"d7882e505b1ff345d458bf0e893eec8019bc849f861ad73a212869540dd505ff","checksums_source":"gosec_2.28.0_checksums.txt (same GitHub release)","verification_method":"SHA-256 of the linux_amd64 release tarball verified before extraction, cross-referenced against the release's own gosec_2.28.0_checksums.txt. Same pattern as osv-scanner's binary_sha256. Under the 34.3 CI gate as of the install commit (upstream-checksum-sources.yaml `installed: true`).","verified_at":"2026-08-02","verified_by":"curl -sfL https://github.com/securego/gosec/releases/download/v2.28.0/gosec_2.28.0_checksums.txt, then downloaded the tarball and confirmed with `sha256sum -c -`. Re-verified 2026-08-02 at install; a one-digit corruption of the pin was shown to abort the build at `sha256sum -c` (mutation M4).","offline_mode":"fully supported. gosec needs no network of its own — its rules are compiled into the binary, so there is no Bearer-style rule registry and no OSV-style DB to pre-bake. Verified 2026-08-02 under the REAL scan sandbox (--network=none, read-only rootfs, /tmp tmpfs 256m, mem 2g, pids 256, cap-drop ALL) against tests/corpus/golden-scanner-corpus/fixtures/go-mcp-relay: clean = 7 files / 3 findings, backdoored = 7 files / 7 findings, zero `Golang errors`, ~6.6s wall per cold scan.","requires_go_toolchain":"go1.26.5 linux-amd64, installed in the same image (Dockerfile ARGs GO_VERSION / GO_TARBALL_SHA256, pinned by SHA-256 against go.dev's published digest). This is a RUNTIME dependency, not a build-time one: gosec analyses type-checked packages via golang.org/x/tools/go/packages, which shells out to `go list`. With `go` absent from $PATH, gosec 2.28.0 exits 0 under -no-fail and emits {\"Issues\": [], \"Stats\": {\"files\": 0, ...}} — a report that parses as a clean 100/Verified scan. gosec_adapter.py fails closed to `incomplete` on that shape, and docker/scanner-base/Dockerfile now carries a build-time assertion that FAILS the build if the warmup scan reports zero files or zero issues. Toolchain authorized by JesseTop 2026-08-02 (hard rule 1). The extracted tree is PRUNED (go/test, go/api, go/doc, go/misc, stdlib testdata + *_test.go) from 269 MB to 191 MB; gosec output over both corpus fixtures is byte-identical before and after pruning. The SHA-256 pin is verified against the UNMODIFIED upstream tarball before any pruning — an auditor must hash the tarball, not /usr/local/go.","notes":"NOT YET IN THE SAST ROSTER. The adapter (src/nerlo/scanners/gosec_adapter.py) exists and is tested, but registering it into sast_engine.py is task 41.4 — that changes the composite denominator for every Go artifact and belongs behind the 34.7 canary. This entry records the pin and the install, not roster membership.","license":"Apache-2.0"},{"name":"trivy","phase":"2-dep","version":"0.71.0","source":"github-releases","source_url":"https://github.com/aquasecurity/trivy/releases/tag/v0.71.0","verification_method":"SHA-256 of .deb verified before dpkg -i; vulnerability DB (~1.2GB) pre-cached at /opt/trivy-cache during build from a DIGEST-PINNED OCI artifact (--db-repository ghcr.io/aquasecurity/trivy-db@sha256:..., see docker/scanner-base/feed-pins.json, task 34.14). The BINARY pin and the FEED pin are separate things and move on separate schedules: this entry pins the binary; feed-pins.json pins the database, refreshed daily by .github/workflows/scanner-feed-bump.yml.","deb_url":"https://github.com/aquasecurity/trivy/releases/download/v0.71.0/trivy_0.71.0_Linux-64bit.deb","deb_sha256":"d5f52c441416cc23f3636106cbf514a786373cb5a11534253db9333cc93afb3d","checksums_source":"trivy_0.71.0_checksums.txt (downloaded from same GitHub release)","verified_at":"2026-06-04","verified_by":"gh release download v0.71.0 --repo aquasecurity/trivy --pattern checksums.txt","offline_mode":"fully supported via --offline-scan --skip-db-update with pre-baked /opt/trivy-cache. Smoke verified: 19 CVEs detected on vulnerable-deps fixture in offline mode.","license":"Apache-2.0"},{"name":"osv-scanner","phase":"2-dep","version":"2.3.8","source":"github-releases","source_url":"https://github.com/google/osv-scanner/releases/tag/v2.3.8","verification_method":"SHA-256 of linux_amd64 binary verified before chmod +x; vulnerability DBs (~250MB across npm/PyPI/Go ecosystems) pre-cached at /opt/osv-cache during build from GCS-GENERATION-PINNED snapshots, each SHA-256 verified by docker/scanner-base/fetch-osv-db.py (see feed-pins.json, task 34.14). osv-scanner 2.3.8 has no pinning flag of its own, so the pin is the object generation; it EXPIRES after 7 days and the build fails rather than falling back to a live download.","binary_url":"https://github.com/google/osv-scanner/releases/download/v2.3.8/osv-scanner_linux_amd64","binary_sha256":"bc98e15319ed0d515e3f9235287ba53cdc5535d576d24fd573978ecfe9ab92dc","checksums_source":"osv-scanner_SHA256SUMS (downloaded from same GitHub release)","verified_at":"2026-06-04","verified_by":"gh release download v2.3.8 --repo google/osv-scanner --pattern SHA256SUMS","offline_mode":"fully supported via --offline-vulnerabilities --local-db-path /opt/osv-cache. Smoke verified: 19 CVEs detected on vulnerable-deps fixture in offline mode, agreeing with Trivy's count (different vuln databases — strong cross-scanner signal for Phase 2 orchestrator de-dup in Task 6.5).","license":"Apache-2.0"},{"name":"govulncheck","task":"41.2","phase":"2-dep","module":"golang.org/x/vuln/cmd/govulncheck","version":"v1.6.0","source":"go-module-proxy","source_url":"https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck","module_h1_sha256":"h1:FeMO9Rm/HwyduOztbvKcOw+zvDEPr4I4aQNSfevFcKY=","checksums_source":"https://sum.golang.org/lookup/golang.org/x/vuln@v1.6.0 (the SIGNED go checksum transparency log — a stronger provenance record than a GitHub release checksums.txt, because the log is append-only and independently auditable)","verification_method":"`go install golang.org/x/vuln/cmd/govulncheck@v1.6.0` with GOSUMDB=sum.golang.org and GOPRIVATE/GONOSUMDB/GONOPROXY cleared: the go command verifies the module zip against the h1: hash in the signed transparency log and aborts on mismatch. Belt-and-braces, the Dockerfile ALSO compares `go mod download -json .Sum` against ARG GOVULNCHECK_MODULE_H1 before installing, so the committed pin appears in the build log as a value a reviewer can read. Under the 34.3 CI gate as of the install commit (upstream-checksum-sources.yaml `installed: true`, verify mode `go-checksumdb`).","verified_at":"2026-08-06","verified_by":"curl -s https://sum.golang.org/lookup/golang.org/x/vuln@v1.6.0 returns the same h1:; installed into the real scanner-base image and run under the real scan sandbox (--network=none, read-only rootfs, /tmp tmpfs 256m, mem 2g, pids 256, cap-drop ALL, no-new-privileges).","measured_binary_bytes":14967027,"offline_mode":"Requires a BAKED DB SNAPSHOT (govulncheck has no --download-offline-databases equivalent — `govulncheck -h` lists one DB flag, `-db url`, and no download subcommand). `-db file:///opt/govulncheck-db` is the direct mirror of osv-scanner's --offline-vulnerabilities --local-db-path. Baked by docker/scanner-base/govulncheck-db/snapshot.sh; measured 2026-08-06 by running it inside the image, exit 0: 4,134 advisories, 6,155,130 bytes of content (18 MB ALLOCATED — 4 KiB blocks over 4,137 small files; the payload is the 6.2 MB figure). A build-time assertion (docker/scanner-base/go-scanner-warmup/assert_govulncheck.py) fails the build unless an offline scan of a vendored warmup emits an SBOM block, reads THIS db path, returns >= 20 osv records and >= 1 symbol-reachable finding.","known_limit":"NOT A COMPLETE OFFLINE STORY, and installing the binary does not change it. Source-mode govulncheck must LOAD the artifact's dependency packages, and Phases 1-3 have no network while dependency_resolver.py resolves npm/pip/cargo and NOT Go. Measured under --network=none with an empty module cache: a module with unvendored third-party deps produces stdout carrying ONLY the config object (280 bytes) and exit 1 (`module lookup disabled by GOPROXY=off`). `-scan module` does not dodge it. Two populations DO scan clean today: stdlib-only modules, and modules that commit a vendor/ tree (which needs GOFLAGS=-mod=vendor — `-mod=readonly` disables vendor mode). Everything else fails closed to `incomplete` on the SBOM-less shape rather than reporting a clean 100. Closing the gap means teaching dependency_resolver.py to populate a Go module cache before Phase 2, which is a separate task.","notes":"REGISTERED in the Phase 2 roster (dependency_auditor.py) 2026-08-06, in the same commit as this install. Registration before the install would have been strictly worse than not registering: an adapter with no binary returns `incomplete`, trust_engine._assessed_component sets degraded=True, and the composite badge caps at Caution for every Go artifact. tests/test_go_scanner_registration.py asserts a Go artifact does NOT come out degraded now that both binaries are present.","license":"BSD-3-Clause"},{"name":"capslock","task":"41.3","phase":"1-sast","module":"github.com/google/capslock/cmd/capslock","version":"v0.3.2","source":"go-module-proxy","source_url":"https://github.com/google/capslock/releases/tag/v0.3.2","module_h1_sha256":"h1:0ZQa9YR8s9ewFu1g5w6Rgd/lW/4dga7qJew3K6Ql7aM=","checksums_source":"https://sum.golang.org/lookup/github.com/google/capslock@v0.3.2 (signed go checksum transparency log)","verification_method":"Same as govulncheck: `go install` under GOSUMDB=sum.golang.org verifies the module zip against the signed log, and the Dockerfile additionally asserts `go mod download -json .Sum` equals ARG CAPSLOCK_MODULE_H1 before installing. Under the 34.3 CI gate (`go-checksumdb`).","verified_at":"2026-08-06","verified_by":"curl -s https://sum.golang.org/lookup/github.com/google/capslock@v0.3.2 returns the same h1:; installed into the real scanner-base image and run under the real scan sandbox against tests/corpus/golden-scanner-corpus/fixtures/go-mcp-relay/{clean,backdoored}.","measured_binary_bytes":13702511,"offline_mode":"fully supported, NO data bake needed — capslock's capability map is compiled into the binary. Verified under --network=none with an empty module cache. Same vendor-mode caveat as govulncheck (-mod=vendor for a vendored tree). A build-time assertion (docker/scanner-base/go-scanner-warmup/assert_capslock.py) fails the build unless an offline scan emits a non-empty packageInfo AND capabilityInfo — packageInfo is the discriminator between 'capability-free module' (capabilityInfo omitted, packageInfo present) and 'package load failed' (zero bytes, exit 2), and both of the latter score 100/Verified to anything that only counts findings.","measured_corpus_behaviour":"capslock does NOT separate the go-mcp-relay pair: both halves exercise the identical capability SET {EXEC, NETWORK, READ_SYSTEM_STATE, FILES, UNANALYZED}, differing only by one extra READ_SYSTEM_STATE call path. That is why the adapter scores a declared-vs-exercised DIFF against a per-ArtifactType baseline rather than capabilities absolutely.","notes":"REGISTERED in the SAST roster (sast_engine.py) 2026-08-06, in the same commit as this install, for the same reason recorded on govulncheck. Requires the Go toolchain at scan time (already installed for gosec) — this install is the ~13.7 MB binary only.","license":"BSD-3-Clause"}],"pending_scanners":[{"name":"augustus","task":"7.1","phase":"3-fuzz","status":"blocked","blocked_reason":"Augustus is an LLM vulnerability scanner. Its MCP generator supports http/sse transports only and requires an endpoint URL; upstream declines to implement stdio because launching the server as a local subprocess is an arbitrary-code-execution surface. This pipeline never executes a scanned artifact, and remote-only registry entries (the ones that do expose an endpoint) are dropped at crawl time. Unblocking depends on task 39.8.","blocked_at":"2026-07-31"},{"name":"garak","task":"9.1","phase":"4-deep","status":"blocked","blocked_reason":"garak probes a live model endpoint (all 25 generators in 0.15.1 are model backends; its CLI takes --target_type/--target_name and no path argument), and this pipeline never produces one — the artifact is mounted read-only and never executed, and remote-only registry entries are dropped at crawl time. Phase 4 already has bridge networking, so egress is not what is missing. Installing it anyway costs 44 unconditional requirements incl. torch>=2.6.0 and transformers>=5.0 for a scanner with no target. Evidence and the two unblock paths: task 9.1 in .kiro/specs/nubifer-vsk/tasks.md; the decision is queued in .kiro/reviews/2026-07-25-jesse-review-queue.md.","blocked_at":"2026-07-30"},{"name":"modelscan","task":"9.3","phase":"4-deep","status":"not started"},{"name":"pompelmi","task":"9.6","phase":"4-deep","status":"not started"}],"maintenance":"When adding a new scanner: add an entry to `scanners[]` with verification_method, source URL, pinned version, and SHA. Move corresponding entry out of `pending_scanners[]`. Same commit also updates Dockerfile, scanner-sources.md, and ticks the relevant task in .kiro/specs/nubifer-vsk/tasks.md.","$license_note":"`license` is the license of the PINNED version above, not of the project's current HEAD — relicensing is not retroactive, so the binding license is the one shipped with what we run. Verified against docs/scanner-sources.md and gated by tools/check_scanner_licenses.py, which fails the build on a denied license or on drift between these two records. Added 2026-08-21 after Bearer's Elastic-2.0 terms were found recorded but never evaluated."},"image_digest_in_use":"sha256:099764caca3c7606166353607f25c58ac099f2342c06d1884c71f8794816c34d","sbom":{"status":"pending"},"economics":{"per_scan_cost_p50_usd":0.000622,"per_scan_cost_p95_usd":0.002001,"computed_over":"last_30d","scan_count_last_30d":19085,"priced_scan_count_last_30d":19085},"recent_scanner_bumps":[],"generated_at":"2026-09-02T13:08:34.682694+00:00","staleness_warning":null,"emergency_brake":{"active":false,"source":null,"set_by":null,"reason":null,"updated_at":null}}