mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-08-03 01:10:18 +03:00
Complete folder anatomy for all 649 cybersecurity skills + update LICENSE to Mahipal
- Add scripts/agent.py and references/api-reference.md to all remaining skills - Update all 648 LICENSE files: copyright now reads 'Mahipal' - Add implementing-security-monitoring-with-datadog (new skill with full anatomy) - All 649 skills now have: SKILL.md, LICENSE, scripts/agent.py, references/api-reference.md
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Agent for verifying container image provenance using Sigstore Cosign."""
|
||||
|
||||
import json
|
||||
import argparse
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def run_cosign(args_list):
|
||||
"""Run a cosign CLI command and return output."""
|
||||
cmd = ["cosign"] + args_list
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
|
||||
return {
|
||||
"stdout": result.stdout.strip(),
|
||||
"stderr": result.stderr.strip(),
|
||||
"returncode": result.returncode,
|
||||
}
|
||||
|
||||
|
||||
def verify_image(image_ref, key=None, certificate_identity=None, certificate_oidc_issuer=None):
|
||||
"""Verify a container image signature with Cosign."""
|
||||
args = ["verify"]
|
||||
if key:
|
||||
args.extend(["--key", key])
|
||||
elif certificate_identity and certificate_oidc_issuer:
|
||||
args.extend(["--certificate-identity", certificate_identity,
|
||||
"--certificate-oidc-issuer", certificate_oidc_issuer])
|
||||
args.append(image_ref)
|
||||
result = run_cosign(args)
|
||||
verified = result["returncode"] == 0
|
||||
attestations = []
|
||||
if verified and result["stdout"]:
|
||||
try:
|
||||
attestations = json.loads(result["stdout"])
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
return {
|
||||
"image": image_ref,
|
||||
"verified": verified,
|
||||
"attestations": attestations if isinstance(attestations, list) else [attestations],
|
||||
"error": result["stderr"] if not verified else None,
|
||||
}
|
||||
|
||||
|
||||
def verify_attestation(image_ref, predicate_type, key=None):
|
||||
"""Verify an in-toto attestation attached to an image."""
|
||||
args = ["verify-attestation", "--type", predicate_type]
|
||||
if key:
|
||||
args.extend(["--key", key])
|
||||
args.append(image_ref)
|
||||
result = run_cosign(args)
|
||||
return {
|
||||
"image": image_ref,
|
||||
"predicate_type": predicate_type,
|
||||
"verified": result["returncode"] == 0,
|
||||
"output": result["stdout"][:500] if result["stdout"] else None,
|
||||
"error": result["stderr"] if result["returncode"] != 0 else None,
|
||||
}
|
||||
|
||||
|
||||
def sign_image(image_ref, key=None, keyless=False):
|
||||
"""Sign a container image with Cosign."""
|
||||
args = ["sign"]
|
||||
if key:
|
||||
args.extend(["--key", key])
|
||||
elif keyless:
|
||||
args.append("--yes")
|
||||
args.append(image_ref)
|
||||
result = run_cosign(args)
|
||||
return {
|
||||
"image": image_ref,
|
||||
"signed": result["returncode"] == 0,
|
||||
"error": result["stderr"] if result["returncode"] != 0 else None,
|
||||
}
|
||||
|
||||
|
||||
def triangulate_image(image_ref):
|
||||
"""Get the signature and attestation image references."""
|
||||
result = run_cosign(["triangulate", image_ref])
|
||||
return {
|
||||
"image": image_ref,
|
||||
"signature_ref": result["stdout"] if result["returncode"] == 0 else None,
|
||||
}
|
||||
|
||||
|
||||
def audit_registry_images(images_list, key=None, identity=None, issuer=None):
|
||||
"""Audit multiple container images for valid signatures."""
|
||||
results = []
|
||||
for image in images_list:
|
||||
result = verify_image(image, key=key, certificate_identity=identity,
|
||||
certificate_oidc_issuer=issuer)
|
||||
result["severity"] = "INFO" if result["verified"] else "HIGH"
|
||||
results.append(result)
|
||||
signed = sum(1 for r in results if r["verified"])
|
||||
return {
|
||||
"total_images": len(results),
|
||||
"signed": signed,
|
||||
"unsigned": len(results) - signed,
|
||||
"signing_rate": round(signed / len(results) * 100, 1) if results else 0,
|
||||
"details": results,
|
||||
}
|
||||
|
||||
|
||||
def generate_kyverno_policy(image_patterns, key=None, identity=None, issuer=None):
|
||||
"""Generate Kyverno ClusterPolicy for image verification."""
|
||||
policy = {
|
||||
"apiVersion": "kyverno.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {"name": "verify-image-signatures"},
|
||||
"spec": {
|
||||
"validationFailureAction": "Enforce",
|
||||
"webhookTimeoutSeconds": 30,
|
||||
"rules": [{
|
||||
"name": "verify-cosign-signature",
|
||||
"match": {"any": [{"resources": {"kinds": ["Pod"]}}]},
|
||||
"verifyImages": [{
|
||||
"imageReferences": image_patterns,
|
||||
"attestors": [{
|
||||
"entries": [{
|
||||
"keyless": {
|
||||
"subject": identity or "*",
|
||||
"issuer": issuer or "https://token.actions.githubusercontent.com",
|
||||
}
|
||||
}] if not key else [{"keys": {"publicKeys": key}}]
|
||||
}],
|
||||
}],
|
||||
}],
|
||||
},
|
||||
}
|
||||
return policy
|
||||
|
||||
|
||||
def generate_cosign_ci_workflow(image_ref, registry):
|
||||
"""Generate GitHub Actions workflow for Cosign signing."""
|
||||
return f"""name: Sign Container Image
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
jobs:
|
||||
sign:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: sigstore/cosign-installer@v3
|
||||
- name: Login to registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: {registry}
|
||||
- name: Build and push
|
||||
run: |
|
||||
docker build -t {image_ref} .
|
||||
docker push {image_ref}
|
||||
- name: Sign image (keyless)
|
||||
run: cosign sign --yes {image_ref}
|
||||
- name: Verify signature
|
||||
run: cosign verify --certificate-identity-regexp=".*" --certificate-oidc-issuer="https://token.actions.githubusercontent.com" {image_ref}
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Cosign Image Provenance Agent")
|
||||
parser.add_argument("--verify", help="Image to verify")
|
||||
parser.add_argument("--sign", help="Image to sign")
|
||||
parser.add_argument("--audit", nargs="+", help="Images to audit for signatures")
|
||||
parser.add_argument("--key", help="Cosign public key for verification")
|
||||
parser.add_argument("--identity", help="Certificate identity for keyless verification")
|
||||
parser.add_argument("--issuer", help="OIDC issuer for keyless verification")
|
||||
parser.add_argument("--gen-policy", nargs="+", help="Image patterns for Kyverno policy")
|
||||
parser.add_argument("--gen-workflow", help="Image ref for CI workflow generation")
|
||||
parser.add_argument("--output", default="cosign_provenance_report.json")
|
||||
args = parser.parse_args()
|
||||
|
||||
report = {"generated_at": datetime.utcnow().isoformat(), "results": {}}
|
||||
|
||||
if args.verify:
|
||||
result = verify_image(args.verify, key=args.key,
|
||||
certificate_identity=args.identity,
|
||||
certificate_oidc_issuer=args.issuer)
|
||||
report["results"]["verification"] = result
|
||||
status = "VERIFIED" if result["verified"] else "FAILED"
|
||||
print(f"[+] {args.verify}: {status}")
|
||||
|
||||
if args.audit:
|
||||
result = audit_registry_images(args.audit, key=args.key,
|
||||
identity=args.identity, issuer=args.issuer)
|
||||
report["results"]["audit"] = result
|
||||
print(f"[+] Audit: {result['signed']}/{result['total_images']} signed")
|
||||
|
||||
if args.gen_policy:
|
||||
policy = generate_kyverno_policy(args.gen_policy, key=args.key,
|
||||
identity=args.identity, issuer=args.issuer)
|
||||
report["results"]["kyverno_policy"] = policy
|
||||
print("[+] Kyverno policy generated")
|
||||
|
||||
if args.gen_workflow:
|
||||
workflow = generate_cosign_ci_workflow(args.gen_workflow, "ghcr.io")
|
||||
report["results"]["workflow"] = workflow
|
||||
print("[+] CI workflow generated")
|
||||
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2, default=str)
|
||||
print(f"[+] Report saved to {args.output}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user