Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

README.md

Receipt-Signed Vulnerability Disclosure

Cryptographic evidence for every step of the vulnerability lifecycle.

When AI agents discover vulnerabilities (e.g., Claude Code Security / Mythos), the disclosure process needs more than "we found and fixed it." Regulated industries and multi-vendor ecosystems need independently verifiable proof that every step was followed per policy.

This example shows how to use Ed25519 receipts + Cedar policies to create a tamper-evident, auditable vulnerability disclosure chain.

The Problem

Current vulnerability disclosure processes lack:

  • Proof a scan was performed (audit evidence for compliance)
  • Proof a finding was reviewed and approved (not just suggested)
  • Tamper-evident record of the remediation lifecycle
  • Policy governance of what the scanning agent is allowed to do

SHA-3 commitments prove "we had this file at this time." Receipt chains prove who, what, when, under which policy, and what came before.

Architecture

DISCOVER (agent finds vulnerability)
    | receipt: what was found, severity, affected software
    v
DISCLOSE (responsible disclosure to vendor)
    | receipt: who was notified, when, embargo terms
    v
PATCH (fix developed and reviewed)
    | receipt: patch hash, reviewer, test results
    v
DEPLOY (fix deployed to production)
    | receipt: deployment verification, rollback status

Each step: Ed25519-signed, chain-linked, policy-bound
Verify entire chain: npx @veritasacta/verify chain.jsonl

Cedar Policy: Agent Governance

The Cedar policy (vulnerability-disclosure.cedar) defines what the scanning agent is allowed to do:

// Agent CAN scan code and report internally
permit(principal, action == Action::"scan_code", resource);
permit(principal, action == Action::"report_finding", resource)
  when { context.report_destination == "internal" };

// Agent CANNOT disclose externally without human approval
forbid(principal, action == Action::"disclose_vulnerability", resource)
  when { context.disclosure_target == "external" && context.human_approval != true };

// Agent CANNOT deploy patches to production
forbid(principal, action == Action::"deploy_patch", resource)
  when { context.environment == "production" && context.human_approval != true };

// Agent MUST escalate critical findings
forbid(principal, action == Action::"report_finding", resource)
  when { context.severity == "critical" && context.escalated_to_human != true };

Receipt Chain Example

Each step produces a signed receipt that links to the previous one:

{
  "receipt_id": "rcpt-discover-001",
  "action": "scan_code",
  "actor": "sb:agent:security-scanner",
  "timestamp": "2026-04-08T10:00:00Z",
  "payload": {
    "vulnerability_id": "CVE-2026-XXXX",
    "severity": "high",
    "affected_package": "protect-mcp@0.5.3",
    "affected_file": "src/signing.ts",
    "description": "Timing side-channel in signature verification"
  },
  "policy_digest": "sha256:9d0f...",
  "previousReceiptHash": null,
  "signature": "ed25519:..."
}

The disclosure receipt links to the discovery:

{
  "receipt_id": "rcpt-disclose-001",
  "action": "disclose_vulnerability",
  "actor": "sb:agent:pm",
  "timestamp": "2026-04-08T10:30:00Z",
  "payload": {
    "vulnerability_id": "CVE-2026-XXXX",
    "disclosure_target": "internal",
    "notified_parties": ["security-team@scopeblind.com"],
    "embargo_until": "2026-07-08T00:00:00Z",
    "human_approval": true,
    "approver": "sb:human:tom"
  },
  "policy_digest": "sha256:9d0f...",
  "previousReceiptHash": "sha256:abc123...",
  "signature": "ed25519:..."
}

Verification

Verify the entire disclosure chain offline:

# Verify a single receipt
npx @veritasacta/verify receipt-discover.json --key <public-key-hex>

# Verify the complete chain
npx @veritasacta/verify chain.jsonl --key <public-key-hex>

# Exit codes:
# 0 = valid (cryptographically verified)
# 1 = invalid (tampered or broken chain)
# 2 = error (malformed input)

Integration with AI Security Tools

Claude Code Security / Mythos

import { signDecision } from 'protect-mcp';

// After Mythos discovers a vulnerability
const discoveryReceipt = await signDecision({
  action: 'scan_code',
  actor: 'sb:agent:mythos',
  payload: {
    vulnerability_id: finding.id,
    severity: finding.severity,
    affected_package: finding.package,
    cwe: finding.cwe_id,
  },
  policyDigest: currentPolicyHash,
});

protect-mcp Gateway

The MCP gateway automatically signs every tool call. Configure the vulnerability disclosure policy:

{
  "policyPath": "./policies/vulnerability-disclosure.cedar",
  "signingKey": "./keys/signing.json",
  "receiptLog": "./receipts/disclosures.jsonl"
}

IETF Standard

The receipt format follows draft-farley-acta-signed-receipts-01:

Field Purpose
receipt_id Unique identifier
action What was done
actor WHO signed it (Ed25519 key ID)
timestamp WHEN it happened
payload WHAT was decided/found
policy_digest WHICH policy governed the action
previousReceiptHash Chain linking (tamper-evident ordering)
signature Ed25519 signature over JCS-canonicalized payload

Why This Matters

For Glasswing's 90-day recommendations on disclosure processes:

  • Multi-vendor: AWS, Apple, Google, Microsoft won't adopt a standard owned by a competitor. IETF Internet-Drafts are neutral ground.
  • Offline verification: No network calls, no trust in the issuer. Anyone can verify.
  • Interoperability: 4 independent implementations across TypeScript and Python produce interoperable receipts.
  • Policy-bound: Cedar policies define boundaries. Receipts prove boundaries were respected.

Related