build: lock file maintenance (main) #370
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: 'GitHub Actions Scan' | |
| on: | |
| workflow_call: | |
| inputs: | |
| wif_provider: | |
| type: 'string' | |
| zizmor_result_bucket: | |
| type: 'string' | |
| zizmor_config_bucket: | |
| type: 'string' | |
| zizmor_enforce: | |
| type: 'string' | |
| # zizmor: ignore[dangerous-triggers] Required to have `id-token: write` for GCP auth. | |
| pull_request_target: | |
| permissions: {} | |
| env: | |
| ZIZMOR_VERSION: '1.25.2' | |
| ZIZMOR_DOCKER_DIGEST: 'sha256:14ea7f5cc7c67933394a35b5a38a277397818d232602635edb2010b313afb110' | |
| ZIZMOR_ENFORCE: >- | |
| ${{ inputs.zizmor_enforce != '' && inputs.zizmor_enforce || 'false' }} | |
| ZIZMOR_RESULT_BUCKET: >- | |
| ${{ inputs.zizmor_result_bucket != '' && inputs.zizmor_result_bucket || 'zizmor-7165' }} | |
| ZIZMOR_CONFIG_BUCKET: >- | |
| ${{ inputs.zizmor_config_bucket != '' && inputs.zizmor_config_bucket || 'zizmor-configs-cc63' }} | |
| WIF_PROVIDER: >- | |
| ${{ inputs.wif_provider != '' && inputs.wif_provider || 'projects/102295818544/locations/global/workloadIdentityPools/gitsec-gha-artifacts/providers/gitsec-gha-artifacts-provider' }} | |
| jobs: | |
| check-changes: | |
| runs-on: 'ubuntu-latest' | |
| outputs: | |
| files: '${{ steps.check.outputs.files }}' | |
| permissions: | |
| contents: 'read' | |
| steps: | |
| - name: 'Print Help Link' | |
| shell: 'bash' | |
| run: | | |
| echo "For Googlers: Refer to go/github-zizmor-help for internal guidance on resolving issues." | |
| - name: 'Checkout source' | |
| uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # ratchet:actions/checkout@v6 | |
| with: | |
| ref: '${{ github.event.pull_request.head.sha }}' | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: 'Check for workflow changes' | |
| id: 'check' | |
| shell: 'bash' | |
| env: | |
| GIT_HEAD_SHA: '${{ github.event.pull_request.head.sha }}' | |
| GIT_BASE_SHA: '${{ github.event.pull_request.base.sha }}' | |
| run: | | |
| files=$(git diff --name-only --diff-filter=d "${GIT_BASE_SHA}" "${GIT_HEAD_SHA}" | grep -E '^\.github/(workflows/.+\.ya?ml|dependabot\.ya?ml)$' | xargs || true) | |
| echo "files=$files" >> "$GITHUB_OUTPUT" | |
| zizmor-config: | |
| needs: ['check-changes'] | |
| runs-on: 'ubuntu-latest' | |
| if: >- | |
| needs.check-changes.outputs.files != '' | |
| permissions: | |
| id-token: 'write' | |
| outputs: | |
| mandatory-checks: '${{ steps.identify-mandatory.outputs.mandatory-checks }}' | |
| enforce: '${{ steps.identify-enforcement.outputs.enforce }}' | |
| steps: | |
| - name: 'Authenticate to GCP' | |
| uses: 'google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed' # ratchet:google-github-actions/auth@v2 | |
| id: 'gcp-auth' | |
| with: | |
| workload_identity_provider: '${{ env.WIF_PROVIDER }}' | |
| - name: 'Download zizmor config' | |
| shell: 'bash' | |
| env: | |
| REPO_PATH: '${{ github.repository }}' | |
| ORG_PATH: '${{ github.repository_owner }}' | |
| GOOGLE_ACCESS_TOKEN: '${{ steps.gcp-auth.outputs.auth_token }}' | |
| run: | | |
| if [ -z "${ZIZMOR_CONFIG_BUCKET}" ]; then | |
| echo "::error::No config bucket configured. Config bucket is required." | |
| exit 1 | |
| fi | |
| gcs_download() { | |
| local src_path="$1" | |
| local dst_path="$2" | |
| curl -f -sS -L \ | |
| -H "Authorization: Bearer ${GOOGLE_ACCESS_TOKEN}" \ | |
| -o "$dst_path" \ | |
| "https://storage.googleapis.com/${src_path}" | |
| } | |
| if gcs_download "${ZIZMOR_CONFIG_BUCKET}/${REPO_PATH}.yaml" zizmor-config.yaml &>/dev/null; then | |
| echo "Using repository config: ${ZIZMOR_CONFIG_BUCKET}/${REPO_PATH}.yaml" | |
| elif gcs_download "${ZIZMOR_CONFIG_BUCKET}/${ORG_PATH}.yaml" zizmor-config.yaml &>/dev/null; then | |
| echo "Using organization config: ${ZIZMOR_CONFIG_BUCKET}/${ORG_PATH}.yaml" | |
| else | |
| echo "Attempting to fetch default config: ${ZIZMOR_CONFIG_BUCKET}/default.yaml" | |
| if ! gcs_download "${ZIZMOR_CONFIG_BUCKET}/default.yaml" zizmor-config.yaml; then | |
| echo "::error::Failed to download default config. Crucial step failed." | |
| exit 1 | |
| fi | |
| fi | |
| - name: 'Identify mandatory checks' | |
| id: 'identify-mandatory' | |
| shell: 'bash' | |
| run: | | |
| MANDATORY_LIST=$(yq eval '[.rules | to_entries | .[] | select(.value.remap.severity == "mandatory" and .value.disable != true) | .key]' -o json --indent 0 zizmor-config.yaml) | |
| echo "mandatory-checks=${MANDATORY_LIST}" >> "$GITHUB_OUTPUT" | |
| - name: 'Identify enforcement status checks' | |
| id: 'identify-enforcement' | |
| shell: 'bash' | |
| run: | | |
| FINAL_ENFORCE="${ZIZMOR_ENFORCE}" | |
| CONFIG_ENFORCE="$(yq eval '.google.enforce' zizmor-config.yaml)" | |
| if [[ "${CONFIG_ENFORCE}" = "true" || "${CONFIG_ENFORCE}" = "false" ]]; then | |
| echo "Using enforcment value from config: ${CONFIG_ENFORCE}" | |
| FINAL_ENFORCE="${CONFIG_ENFORCE}" | |
| fi | |
| echo "enforce=${FINAL_ENFORCE}" >> "$GITHUB_OUTPUT" | |
| - name: 'Remove non-standard google key' | |
| shell: 'bash' | |
| run: | | |
| yq 'del(.google)' -i zizmor-config.yaml | |
| - name: 'Rewrite mandatory to high in config' | |
| shell: 'bash' | |
| run: | | |
| yq '(.rules.[] | select(.remap.severity == "mandatory") | .remap.severity) = "high"' -i zizmor-config.yaml | |
| - name: 'Save config' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # ratchet:actions/upload-artifact@v7 | |
| with: | |
| name: 'zizmor-config' | |
| path: 'zizmor-config.yaml' | |
| if-no-files-found: 'error' | |
| retention-days: 1 | |
| zizmor-scan: | |
| needs: ['check-changes', 'zizmor-config'] | |
| runs-on: 'ubuntu-latest' | |
| if: >- | |
| needs.check-changes.outputs.files != '' && | |
| ( | |
| inputs.wif_provider != '' || | |
| ( | |
| github.event_name == 'pull_request_target' && | |
| !startsWith(github.workflow_ref, format('{0}/', github.repository)) | |
| ) | |
| ) | |
| permissions: | |
| contents: 'read' | |
| outputs: | |
| offline: '${{ steps.run-zizmor.outputs.offline }}' | |
| steps: | |
| - name: 'Checkout source' | |
| uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # ratchet:actions/checkout@v6 | |
| with: | |
| ref: '${{ github.event.pull_request.head.sha }}' | |
| persist-credentials: false | |
| - name: 'Download config' | |
| uses: 'actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c' # ratchet:actions/download-artifact@v8 | |
| with: | |
| name: 'zizmor-config' | |
| - name: 'Run zizmor' | |
| id: 'run-zizmor' | |
| shell: 'bash' | |
| env: | |
| GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}' | |
| CHANGED_FILES: '${{ needs.check-changes.outputs.files }}' | |
| run: | | |
| read -r -a FILES_ARR <<< "${CHANGED_FILES}" | |
| for i in "${!FILES_ARR[@]}"; do | |
| [ -n "${FILES_ARR[i]}" ] || unset "FILES_ARR[i]" | |
| done | |
| set +e | |
| docker run \ | |
| --rm \ | |
| --volume "${GITHUB_WORKSPACE}:/workspace:ro" \ | |
| --workdir "/workspace" \ | |
| --env GH_TOKEN="${GH_TOKEN}" \ | |
| "ghcr.io/zizmorcore/zizmor:${ZIZMOR_VERSION}@${ZIZMOR_DOCKER_DIGEST}" \ | |
| --config /workspace/zizmor-config.yaml \ | |
| --format sarif \ | |
| --no-ignores \ | |
| -- \ | |
| "${FILES_ARR[@]}" \ | |
| > zizmor.sarif.json 2> zizmor.err | |
| EXIT_CODE=$? | |
| set -e | |
| OFFLINE_MODE="false" | |
| if [ $EXIT_CODE -ne 0 ]; then | |
| if grep -q "missing or you have no access" zizmor.err; then | |
| echo "::warning::Zizmor token has insufficient permissions. Falling back to offline scan." | |
| docker run \ | |
| --rm \ | |
| --volume "${GITHUB_WORKSPACE}:/workspace:ro" \ | |
| --workdir "/workspace" \ | |
| "ghcr.io/zizmorcore/zizmor:${ZIZMOR_VERSION}@${ZIZMOR_DOCKER_DIGEST}" \ | |
| --config /workspace/zizmor-config.yaml \ | |
| --format sarif \ | |
| --no-ignores \ | |
| -- \ | |
| "${FILES_ARR[@]}" \ | |
| > zizmor.sarif.json | |
| OFFLINE_MODE="true" | |
| else | |
| echo "Zizmor scan failed with error:" >&2 | |
| cat zizmor.err >&2 | |
| exit $EXIT_CODE | |
| fi | |
| fi | |
| echo "offline=${OFFLINE_MODE}" >> "$GITHUB_OUTPUT" | |
| - name: 'Enrich SARIF with GitHub metadata' | |
| shell: 'bash' | |
| env: | |
| OFFLINE_MODE: '${{ steps.run-zizmor.outputs.offline }}' | |
| run: >- | |
| jq | |
| --arg uri "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}" | |
| --arg sha "${GITHUB_SHA}" | |
| --argjson repo_id "${GITHUB_REPOSITORY_ID}" | |
| --argjson owner_id "${GITHUB_REPOSITORY_OWNER_ID}" | |
| --arg run_id "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/attempts/${GITHUB_RUN_ATTEMPT}" | |
| --argjson offline "${OFFLINE_MODE:-false}" | |
| '.runs[] |= . + { | |
| "versionControlProvenance": [ | |
| { | |
| "repositoryUri": $uri, | |
| "revisionId": $sha, | |
| "properties": { | |
| "github_repository_id": $repo_id, | |
| "github_owner_id": $owner_id | |
| } | |
| } | |
| ], | |
| "invocations": [ | |
| { | |
| "executionSuccessful": true, | |
| "properties": { | |
| "execution_id": $run_id, | |
| "offline": $offline | |
| } | |
| } | |
| ] | |
| }' zizmor.sarif.json > enriched.sarif.json | |
| - name: 'Save result' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # ratchet:actions/upload-artifact@v7 | |
| with: | |
| name: 'zizmor' | |
| path: 'enriched.sarif.json' | |
| if-no-files-found: 'error' | |
| retention-days: 1 | |
| zizmor-upload: | |
| needs: ['zizmor-scan'] | |
| runs-on: 'ubuntu-latest' | |
| permissions: | |
| id-token: 'write' | |
| steps: | |
| - name: 'Retrieve result' | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # ratchet:actions/download-artifact@v8 | |
| with: | |
| name: 'zizmor' | |
| - name: 'Authenticate to GCP' | |
| id: 'gcp-auth' | |
| uses: 'google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed' # ratchet:google-github-actions/auth@v2 | |
| with: | |
| workload_identity_provider: '${{ env.WIF_PROVIDER }}' | |
| - name: 'Upload result' | |
| shell: 'bash' | |
| env: | |
| GITHUB_PULL_REQUEST_NUMBER: '${{ github.event.pull_request.number }}' | |
| GOOGLE_ACCESS_TOKEN: '${{ steps.gcp-auth.outputs.auth_token }}' | |
| run: | | |
| gcs_upload() { | |
| local src_path="$1" | |
| local dst_path="$2" | |
| curl -f -sS -L \ | |
| -T "$src_path" \ | |
| -H "Authorization: Bearer ${GOOGLE_ACCESS_TOKEN}" \ | |
| "https://storage.googleapis.com/${dst_path}" | |
| } | |
| gcs_upload \ | |
| enriched.sarif.json \ | |
| "${ZIZMOR_RESULT_BUCKET}/${GITHUB_REPOSITORY}/${GITHUB_PULL_REQUEST_NUMBER}_${GITHUB_RUN_ID}_${GITHUB_RUN_ATTEMPT}.sarif.json" | |
| zizmor-output: | |
| needs: ['check-changes', 'zizmor-scan', 'zizmor-config'] | |
| runs-on: 'ubuntu-latest' | |
| permissions: | |
| contents: 'read' | |
| if: >- | |
| needs.zizmor-config.outputs.enforce == 'true' | |
| steps: | |
| - name: 'Checkout source' | |
| uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # ratchet:actions/checkout@v6 | |
| with: | |
| ref: '${{ github.event.pull_request.head.sha }}' | |
| persist-credentials: false | |
| - name: 'Download config' | |
| uses: 'actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c' # ratchet:actions/download-artifact@v8 | |
| with: | |
| name: 'zizmor-config' | |
| - name: 'Retrieve result' | |
| uses: 'actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c' # ratchet:actions/download-artifact@v8 | |
| with: | |
| name: 'zizmor' | |
| - name: 'Re-run zizmor with github format' | |
| id: 'run-zizmor' | |
| shell: 'bash' | |
| env: | |
| GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}' | |
| CHANGED_FILES: '${{ needs.check-changes.outputs.files }}' | |
| OFFLINE_MODE: '${{ needs.zizmor-scan.outputs.offline }}' | |
| run: | | |
| set +e | |
| read -r -a FILES_ARR <<< "${CHANGED_FILES}" | |
| for i in "${!FILES_ARR[@]}"; do | |
| [ -n "${FILES_ARR[i]}" ] || unset "FILES_ARR[i]" | |
| done | |
| if [ "${OFFLINE_MODE}" = "true" ]; then | |
| echo "Running zizmor offline because the initial scan was offline." | |
| docker run \ | |
| --rm \ | |
| --volume "${GITHUB_WORKSPACE}:/workspace:ro" \ | |
| --workdir "/workspace" \ | |
| "ghcr.io/zizmorcore/zizmor:${ZIZMOR_VERSION}@${ZIZMOR_DOCKER_DIGEST}" \ | |
| --config /workspace/zizmor-config.yaml \ | |
| --format github \ | |
| --min-confidence medium \ | |
| -- \ | |
| "${FILES_ARR[@]}" | |
| else | |
| docker run \ | |
| --rm \ | |
| --volume "${GITHUB_WORKSPACE}:/workspace:ro" \ | |
| --workdir "/workspace" \ | |
| --env GH_TOKEN="${GH_TOKEN}" \ | |
| "ghcr.io/zizmorcore/zizmor:${ZIZMOR_VERSION}@${ZIZMOR_DOCKER_DIGEST}" \ | |
| --config /workspace/zizmor-config.yaml \ | |
| --format github \ | |
| --min-confidence medium \ | |
| -- \ | |
| "${FILES_ARR[@]}" | |
| fi | |
| ZIZMOR_EXIT_CODE=$? | |
| set -e | |
| echo "exit-code=$ZIZMOR_EXIT_CODE" >> "$GITHUB_OUTPUT" | |
| - name: 'Evaluate mandatory checks' | |
| if: 'always()' | |
| shell: 'bash' | |
| env: | |
| MANDATORY_CHECKS: '${{ needs.zizmor-config.outputs.mandatory-checks }}' | |
| ZIZMOR_EXIT_CODE: '${{ steps.run-zizmor.outputs.exit-code }}' | |
| run: | | |
| trap 'if [ "$?" -ne 0 ]; then echo ""; echo "For Googlers: Refer to go/github-zizmor-help for internal guidance on resolving issues."; fi' EXIT | |
| echo "Mandatory checks to evaluate: ${MANDATORY_CHECKS}" | |
| echo "Zizmor format github exit code: ${ZIZMOR_EXIT_CODE}" | |
| HAS_MANDATORY_FINDINGS="false" | |
| if [ -n "${MANDATORY_CHECKS}" ] && [ "${MANDATORY_CHECKS}" != "[]" ]; then | |
| FINDINGS=$(jq --argjson checks "${MANDATORY_CHECKS}" ' | |
| [.runs[]?.results[]? | select(.ruleId | split("/") | last as $rule | $checks | index($rule))] | |
| ' enriched.sarif.json) | |
| NUM_FINDINGS=$(echo "${FINDINGS}" | jq 'length') | |
| if [ "${NUM_FINDINGS}" -gt 0 ]; then | |
| echo "::error::Found ${NUM_FINDINGS} findings for mandatory checks that must always succeed." | |
| echo "${FINDINGS}" | jq -r ' | |
| .[] | |
| | "::error file=\(.locations[0].logicalLocations[0]?.properties?.symbolic?.key?.Local?.given_path // .locations[0].physicalLocation.artifactLocation.uri),line=\(.locations[0].physicalLocation.region.startLine),title=\(.ruleId)::\(.message.text)" | |
| ' | |
| echo "=========================================" | |
| echo "CRITICAL: The following mandatory checks failed:" | |
| echo "${FINDINGS}" | jq -r ' | |
| .[] | "- \(.ruleId) at line \(.locations[0].physicalLocation.region.startLine) of \(.locations[0].physicalLocation.artifactLocation.uri): \(.message.text)" | |
| ' | |
| echo "=========================================" | |
| HAS_MANDATORY_FINDINGS="true" | |
| fi | |
| fi | |
| # Exit codes 11 and 12 are for Informational and Low findings, respectively. | |
| # Exit codes 13 and 14 represent Medium and High findings. | |
| # Fail if the exit code is greater than 12 (i.e. 13 for Medium, 14 for High). | |
| if [ "${ZIZMOR_EXIT_CODE}" -gt 12 ]; then | |
| echo "Failing due to zizmor finding severity of Medium/High (exit code: ${ZIZMOR_EXIT_CODE})." | |
| echo "" | |
| echo "See https://docs.zizmor.sh/audits/ for more info about these checks." | |
| echo "See https://docs.zizmor.sh/usage/#ignoring-results for ignoring checks that are false positive." | |
| exit 1 | |
| fi | |
| if [ "${HAS_MANDATORY_FINDINGS}" = "true" ]; then | |
| echo "Blocking due to failed mandatory checks. These checks cannot be ignored." | |
| exit 1 | |
| fi | |
| echo "All checks evaluated successfully. No mandatory or medium/high findings." |