Persistent security feeds, OSV advisory data, and authenticated MCP dependency scanning.
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-22 01:32:32 -04:00
cmd/icu-security-mcp Provide private scan caches and resilient advisory maintenance 2026-10-22 01:32:32 -04:00
internal Provide private scan caches and resilient advisory maintenance 2026-10-22 01:32:32 -04:00
pkg/client Add grouped dependency scans and bound review evidence 2026-10-13 13:36:26 -04:00
.gitignore Add grouped dependency scans and bound review evidence 2026-10-13 13:36:26 -04:00
.ignore Add grouped dependency scans and bound review evidence 2026-10-13 13:36:26 -04:00
config.example.json Initial commit 2026-10-10 00:46:32 -04:00
go.mod Initial commit 2026-10-10 00:46:32 -04:00
go.sum Initial commit 2026-10-10 00:46:32 -04:00
LICENSE Initial commit 2026-10-10 00:46:32 -04:00
Makefile Initial commit 2026-10-10 00:46:32 -04:00
README.md Provide private scan caches and resilient advisory maintenance 2026-10-22 01:32:32 -04:00
VERSION Provide private scan caches and resilient advisory maintenance 2026-10-22 01:32:32 -04:00

ICU Security MCP

Persistent RSS/Atom and OSV advisory data for agents, with dependency scanning through OSV-Scanner 2.6.0.

Build and Run

Build with Go 1.26 or newer:

make build
cp config.example.json config.local.json
bin/icu-security-mcp serve -config config.local.json

Before starting, set absolute SECURITY_DATA_DIR and SECURITY_JOB_ROOT values and supply three distinct environment tokens of at least 32 bytes, using the names in the configuration. Keep real repository paths and credentials in the ignored private configuration.

The default endpoint is http://127.0.0.1:8094/mcp. HTTP binds to loopback and requires bearer authentication. bridge forwards stdio MCP messages to that service. check reports configuration, token, and mirror status; sync requests a mirror update from the running service. install-scanner requires the upstream artifact's verified SHA256 and a new absolute destination.

Interface

The read token exposes health, feed_list, item_search, item_get, changes_read, advisory_query, advisory_get, repository_list, scan_status, scan_results, scan_evidence_get, scan_assessment, and scan_review_history. The scan token adds scan_start and scan_review_submit; the admin token adds feed_upsert, feed_refresh, and osv_sync.

advisory_query accepts a packages array of exact ecosystem, name, and version identities, with mode local or fresh. Mirrors support PyPI, crates.io, Go, and npm. changes_read accepts after and limit and returns an ordered event page and cursor.

Repository Scans

  • scan_start: registered repository_id and optional mode (combined, default, or osv). Freezes tracked source and returns initial state.
  • scan_status: scan_id. Returns lifecycle state, source fingerprint, advisory generation, grouped counts, frozen feed coverage, and gaps.
  • scan_results: scan_id, optional section (findings, default; occurrences; feed_leads; unlinked_mentions; local_packages), cursor, and limit (default 25, maximum 100). finding_id filters occurrences. Cursors bind to immutable results; mismatches fail explicitly.
  • scan_evidence_get: scan_id and evidence_id. Returns preserved advisory, feed revision, or local-package metadata. Missing historical bodies and locations remain explicit gaps.

Findings group records by explicit aliases or OSV groups and retain affected versions, source occurrences, severity claims, and known-exploited evidence. Related records and similar descriptions do not establish identity. Counts can include maintenance notices and do not establish exploitability.

Local npm directory dependencies use exact versions and SHA256 hashes from frozen metadata, scanned against the captured advisory generation. Missing versions are resolved only when unambiguous and confirmed in scanner output; conflicting registry, Git, or local identities stay unresolved.

Combined scans capture retained RSS/Atom revisions while OSV runs. Explicit advisory IDs link evidence to findings; package URLs/PURLs and canonical registry/module links produce review-required leads. Name-only matches appear separately as unlinked mentions. Feed content does not establish affected versions or change OSV severity. Matching uses retained title, text, and item URL; discarded HTML links, prose aliases, and unstored revisions are not recovered.

Unknown identities, extractor failures, stale/missing feeds, cancellation, and coverage limits remain incomplete. Complete means collection finished; deployed runtime versions and exploitability remain unverified.

Review Evidence

scan_review_submit requires scan_id, its exact fingerprint and generation, and a review containing kind, subject_id, decision, reason, and reviewer.

  • Feed/advisory reviews also require the exact package. Feed subjects use an evidence ID; advisory subjects use a finding ID.
  • Feed decisions: false_match, non_advisory, not_affected, affected, unresolved. Advisory decisions: locally_remediated, not_affected, affected, unresolved.
  • Local remediation binds repository-relative source paths and SHA256 hashes to paired failing-before/passing-after proofs. Proofs identify the case, tested artifact, source fingerprint, receipt hash, observation time, and explicit passed value.
  • Runtime reviews use kind: runtime, decision: observed, and the target as subject_id. The runtime binds target, exact package, artifact/source hashes, a 40- or 64-character hexadecimal deployment revision, proof hash, and observation time. Targets omit credentials, queries, and fragments. Observations expire after 24 hours.

Reviews are authenticated operator reports. The server validates bindings and freshness; it does not run the cited tests or inspect deployments. Identical submissions are idempotent; later decisions append history without changing original results.

scan_review_history takes scan_id, optional sequence after, and limit (default 25, maximum 100); continue with next_after. scan_assessment takes scan_id and optionally both runtime_target and an independently observed runtime_artifact_sha256. It separates collection, source, and runtime status. Changed source, advisory generation, or feed heads stale the assessment and block new reviews. Without fresh matching runtime evidence, runtime remains unverified, expired, or changed.

Operational Constraints

The daemon exits unsuccessfully when its ownership lease is lost or cannot be renewed within its safety window; a supervisor should restart failed exits. Operator-requested shutdown exits successfully. Generation cleanup preserves active, previous, and scan-pinned advisory data and resumes interrupted cleanup in bounded transactions.

Proxy URLs come from the environment variables named in proxies. proxy_mode supports failover or round_robin; direct fallback defaults to true. allowed_private_hosts explicitly permits otherwise-restricted destinations.

Feeds become validated only after a live response parses successfully; their contents remain untrusted. Failed writes do not advance feed checkpoints, and failed mirror updates preserve the active generation. Updates check available bytes without reserving storage. The job root holds frozen sources, evidence, and private Go caches. Event logs rotate at 16 MiB with five retained files.

Coverage bounds are 1,000 feeds, 100,000 retained revisions, 1 MiB per item, and 64 MiB total. Matching retains up to 1,000 package/item pairs and 16 MiB of evidence. Feed freshness allows the greater of 24 hours or twice the polling interval. Result/evidence responses are bounded to 4 MiB; private result/recovery artifacts to 64 MiB. Oversized results retain raw private evidence and report partial or failed coverage.

Review bounds are 10,000 records per scan, 16 KiB per record, 16 source bindings, and eight proofs per review. A reviewed source is not a safety certification.

Verification

make check runs vet and race-enabled tests. Set SECURITY_TEST_JOB_ROOT to an existing private temporary directory for disposable database/service fixtures, and SECURITY_TEST_SCANNER to the absolute OSV-Scanner 2.6.0 binary for native scanner checks. Checks requiring these inputs are skipped when they are absent.

License

MIT — see LICENSE.