generate-provenance.sh 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. #!/usr/bin/env bash
  2. # generate-provenance.sh - Generate SLSA Provenance v0.2 in-toto statement for container images.
  3. # Drop-in replacement for philips-labs/slsa-provenance-action (container subcommand).
  4. #
  5. # Required environment variables (set automatically in GitHub Actions):
  6. # GITHUB_REPOSITORY - owner/repo
  7. # GITHUB_SHA - commit SHA
  8. # GITHUB_RUN_ID - workflow run ID
  9. # GITHUB_WORKFLOW - workflow name
  10. #
  11. # Usage:
  12. # ./hack/generate-provenance.sh \
  13. # --repository <image-repo> \
  14. # --digest <sha256:...> \
  15. # --tags <tag> \
  16. # --output-path <output.intoto.jsonl>
  17. set -euo pipefail
  18. REPOSITORY=""
  19. DIGEST=""
  20. TAGS=""
  21. OUTPUT_PATH=""
  22. while [[ $# -gt 0 ]]; do
  23. case "$1" in
  24. --repository) REPOSITORY="$2"; shift 2 ;;
  25. --digest) DIGEST="$2"; shift 2 ;;
  26. --tags) TAGS="$2"; shift 2 ;;
  27. --output-path) OUTPUT_PATH="$2"; shift 2 ;;
  28. *) echo "Unknown argument: $1" >&2; exit 1 ;;
  29. esac
  30. done
  31. if [[ -z "$REPOSITORY" || -z "$DIGEST" || -z "$OUTPUT_PATH" ]]; then
  32. echo "Error: --repository, --digest, and --output-path are required" >&2
  33. exit 1
  34. fi
  35. # Strip the sha256: prefix for the digest value
  36. DIGEST_VALUE="${DIGEST#sha256:}"
  37. REPO_URL="https://github.com/${GITHUB_REPOSITORY}"
  38. BUILD_INVOCATION_ID="${REPO_URL}/actions/runs/${GITHUB_RUN_ID}"
  39. BUILD_FINISHED_ON="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
  40. # Build subject name: repo:tag if tags provided, otherwise repo
  41. if [[ -n "$TAGS" ]]; then
  42. SUBJECT_NAME="${REPOSITORY}:${TAGS}"
  43. else
  44. SUBJECT_NAME="${REPOSITORY}"
  45. fi
  46. jq -n \
  47. --arg type "https://in-toto.io/Statement/v0.1" \
  48. --arg predicateType "https://slsa.dev/provenance/v0.2" \
  49. --arg subjectName "$SUBJECT_NAME" \
  50. --arg digestValue "$DIGEST_VALUE" \
  51. --arg builderId "${REPO_URL}/Attestations/GitHubHostedActions@v1" \
  52. --arg buildType "https://github.com/Attestations/GitHubActionsWorkflow@v1" \
  53. --arg entryPoint "${GITHUB_WORKFLOW:-}" \
  54. --arg buildInvocationId "$BUILD_INVOCATION_ID" \
  55. --arg buildFinishedOn "$BUILD_FINISHED_ON" \
  56. --arg materialUri "git+${REPO_URL}" \
  57. --arg materialSha "${GITHUB_SHA}" \
  58. '{
  59. "_type": $type,
  60. "subject": [
  61. {
  62. "name": $subjectName,
  63. "digest": {
  64. "sha256": $digestValue
  65. }
  66. }
  67. ],
  68. "predicateType": $predicateType,
  69. "predicate": {
  70. "builder": {
  71. "id": $builderId
  72. },
  73. "buildType": $buildType,
  74. "invocation": {
  75. "configSource": {
  76. "entryPoint": $entryPoint
  77. },
  78. "parameters": null,
  79. "environment": null
  80. },
  81. "metadata": {
  82. "buildInvocationId": $buildInvocationId,
  83. "buildFinishedOn": $buildFinishedOn,
  84. "completeness": {
  85. "parameters": false,
  86. "environment": false,
  87. "materials": false
  88. },
  89. "reproducible": false
  90. },
  91. "materials": [
  92. {
  93. "uri": $materialUri,
  94. "digest": {
  95. "sha1": $materialSha
  96. }
  97. }
  98. ]
  99. }
  100. }' > "$OUTPUT_PATH"
  101. echo "Provenance saved to ${OUTPUT_PATH}"