Add optional security-scan receipt _meta extension (v1) — resolves #1273#1404
Add optional security-scan receipt _meta extension (v1) — resolves #1273#1404eeee2345 wants to merge 3 commits into
Conversation
Adds an optional io.modelcontextprotocol.registry/security-scan extension under the existing _meta reverse-DNS namespace, holding an array of evidence-scoped, scanner-neutral security scan receipts. Resolves the converged v1 proposal in modelcontextprotocol#1273. Each receipt binds a verdict (clean | warnings | findings | inconclusive) to a specific scanner, rule_set_ref, policy_profile, and scanned_artifact_digest, with an explicit machine-readable scan_scope of what was actually evaluated. The schema enforces the client invariant named in-thread: scanned_artifact_digest is required so a clean verdict binds to exact bytes, scan_scope is a required non-empty array, and inconclusive verdicts require a machine-readable inconclusive_reason (artifact_digest_mismatch, unsupported_package_type, scope_excludes_handler_validation, evidence_unavailable, stale_scan) so a dependency or package scan that leaves handler-side validation unassessed stays representable rather than collapsing into clean. attestation (publisher-asserted | registry-attested | third-party-attested) keeps self-asserted receipts distinct from attested ones. Signatures and capability posture are intentionally out of scope for this version. scanner and rule_set_ref stay open strings so any community scanner can populate them; the registry does not endorse any particular one. Schema is defined in openapi.yaml and regenerated via make generate-schema. Documented in generic-server-json.md and official-registry-requirements.md, with a CHANGELOG entry. Additive and optional; existing server.json documents remain valid. Signed-off-by: Adam Lin <adam@agentthreatrule.org>
|
Thanks for turning the discussion into a concrete PR. This looks like the right v1 boundary to me: optional, scanner-neutral, Keeping From my side, I would support this as a small additive schema/docs change. Happy to review wording or field-shape adjustments if maintainers prefer a narrower first cut. |
|
This is the right PR to converge on from #1273. I opened #1405 before noticing this one, but closed mine to avoid splitting review. The important invariant is preserved here: One downstream client test I would add when this lands: a receipt with |
|
@HarperZ9 thanks — and for closing #1405 to keep review in one place. Agreed on the invariant: the digest binding plus a non-empty Good call on the downstream client test — a receipt with |
…iant Adds a downstream-client test for the io.modelcontextprotocol.registry/security-scan extension (modelcontextprotocol#1404) and a named render-invariant note in the format spec. The test compiles the canonical draft server.schema.json (the same file tools/validate-examples compiles) and validates _meta security-scan receipts the way a client would read them: - clean receipt with a well-formed scanned_artifact_digest and a non-empty scan_scope is accepted - mismatched/malformed scanned_artifact_digest (not algorithm:hex) is rejected, and a missing digest is rejected - empty scan_scope is rejected (minItems 1) - verdict inconclusive without inconclusive_reason is rejected, and is accepted once the machine-readable reason is present generic-server-json.md gains a Render invariant subsection making the client rule explicit: do not surface clean unless the receipt binds to the current artifact digest and the displayed claim names the covered scan_scope; an unbound digest or empty scope must be treated as inconclusive rather than clean. Signed-off-by: Adam Lin <adam@agentthreatrule.org>
golangci-lint flagged cloneReceipt as unused (the table cases build maps inline, no clone needed). Removing it; the schema test is unchanged. Signed-off-by: Adam Lin <adam@agentthreatrule.org>
Resolves the converged v1 proposal in #1273. Thanks to @JinNing6 and @HarperZ9 for the design discussion in that thread; the shape here follows what the three of us converged on, scoped to the small v1 cut @HarperZ9 outlined.
What this adds
An optional io.modelcontextprotocol.registry/security-scan extension under the existing _meta reverse-DNS namespace, holding an array of evidence-scoped, scanner-neutral security scan receipts. It sits next to io.modelcontextprotocol.registry/publisher-provided and follows the same convention, rather than adding a bare top-level field.
Each receipt binds a verdict to the exact evidence that produced it: scanner, scanner_version, rule_set_ref, policy_profile, scanned_artifact_ref, scanned_artifact_digest, scan_scope, verdict, scanned_at, freshness_expires_at, evidence_ref, evidence_digest, and attestation. The point is that clean only ever means clean under this scanner version, rule set, policy profile, and artifact digest, for the listed scope, not a server-level safety property.
The client invariant @HarperZ9 named is enforced in the schema, not just prose:
attestation (publisher-asserted, registry-attested, third-party-attested) keeps a self-asserted receipt distinct from an attested one. Signatures and capability posture are intentionally out of scope for this version; the docs note capability posture as a possible future sibling under the same namespace, and attestation plus evidence_digest carry verifiability for now without pulling in key distribution.
scanner and rule_set_ref are open strings so any community scanner can populate them; the registry does not endorse any particular one.
Scope and conventions
Files
Validation
make generate-schema, make check-schema, and make validate (schema validity, sync, and all doc examples against both the JSON schema and the Go validator) pass locally. go build ./..., go vet, and go test ./internal/validators/... pass. The change touches only schema and docs, no Go code, API handlers, or database, so existing server behavior is unaffected.
Happy to adjust naming or field shapes to match registry conventions, and to split anything out if a smaller first cut is preferred. Reviews welcome async whenever it suits.