- Go 99.9%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| cmd/icu-security-mcp | ||
| internal | ||
| pkg/client | ||
| .gitignore | ||
| .ignore | ||
| config.example.json | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| Makefile | ||
| README.md | ||
| VERSION | ||
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: registeredrepository_idand optionalmode(combined, default, orosv). 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, optionalsection(findings, default;occurrences;feed_leads;unlinked_mentions;local_packages),cursor, andlimit(default 25, maximum 100).finding_idfilters occurrences. Cursors bind to immutable results; mismatches fail explicitly.scan_evidence_get:scan_idandevidence_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
passedvalue. - Runtime reviews use
kind: runtime,decision: observed, and the target assubject_id. Theruntimebinds 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.