#!/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