OSVauco/opax-mcp/tyr/tools/attest_tyr_supply_chain.py

96 lines
3.3 KiB
Python

#!/usr/bin/env python
#
# tyr/tools/attest_tyr_supply_chain.py - MCP Tool for verifying supply chain integrity
#
import os
import json
import subprocess
# Use the cosign binary we compiled earlier
COSIGN_PATH = os.path.expanduser("~/go/bin/cosign")
def attest_tyr_supply_chain(p: dict) -> dict:
"""
Verifies the supply chain integrity of a container image using Cosign.
Checks for signature, SLSA provenance, and a CycloneDX SBOM.
"""
image_uri = p.get("image_uri")
if not image_uri:
raise ValueError("Missing required parameter: 'image_uri'")
# The KMS key used for verification
kms_key = "gcpkms://projects/propane-will-491900-m5/locations/global/keyRings/tyr/cryptoKeys/cosign"
report = {
"image_uri": image_uri,
"checks": []
}
# Check 1: Verify Signature
try:
cmd = [COSIGN_PATH, "verify", "--key", kms_key, image_uri]
# Cosign verify prints human-readable output to stderr on success
result = subprocess.run(cmd, capture_output=True,
text=True, check=True, timeout=45)
report["checks"].append({
"check": "signature",
"status": "PASS",
"details": result.stderr.strip()
})
except subprocess.CalledProcessError as e:
report["checks"].append({
"check": "signature",
"status": "FAIL",
"details": e.stderr.strip() or e.stdout.strip()
})
except Exception as e:
report["checks"].append(
{"check": "signature", "status": "ERROR", "message": str(e)})
# Check 2: Verify SLSA Provenance Attestation
try:
cmd = [COSIGN_PATH, "verify-attestation", "--key",
kms_key, "--type", "slsaprovenance", image_uri]
result = subprocess.run(cmd, capture_output=True,
text=True, check=True, timeout=45)
# The attestation predicate is printed to stdout
provenance = json.loads(result.stdout)
report["checks"].append({
"check": "slsa_provenance",
"status": "PASS",
"predicate": provenance.get("predicate", {})
})
except subprocess.CalledProcessError as e:
report["checks"].append({
"check": "slsa_provenance",
"status": "FAIL",
"details": e.stderr.strip() or e.stdout.strip()
})
except Exception as e:
report["checks"].append(
{"check": "slsa_provenance", "status": "ERROR", "message": str(e)})
# Check 3: Verify SBOM (CycloneDX) Attestation
try:
cmd = [COSIGN_PATH, "verify-attestation", "--key",
kms_key, "--type", "cyclonedx", image_uri]
result = subprocess.run(cmd, capture_output=True,
text=True, check=True, timeout=45)
report["checks"].append({
"check": "sbom_cyclonedx",
"status": "PASS",
"details": "CycloneDX SBOM attestation found and verified."
})
except subprocess.CalledProcessError as e:
report["checks"].append({
"check": "sbom_cyclonedx",
"status": "FAIL",
"details": e.stderr.strip() or e.stdout.strip()
})
except Exception as e:
report["checks"].append(
{"check": "sbom_cyclonedx", "status": "ERROR", "message": str(e)})
return report