Skip to content

Commit aec1e31

Browse files
authored
feat(zizmor): add cross-repo SARIF reconciler workflow (SEC-200) (#7)
🔒 Scanned for secrets using gitleaks 8.30.1
1 parent 62057f6 commit aec1e31

1 file changed

Lines changed: 142 additions & 0 deletions

File tree

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
name: zizmor-reconcile
2+
3+
on:
4+
repository_dispatch:
5+
types: [zizmor-reconcile]
6+
workflow_dispatch:
7+
inputs:
8+
repo:
9+
description: "Target repo (name only, e.g. rudder-sdk-swift)"
10+
required: true
11+
type: string
12+
sha:
13+
description: "Target commit SHA (defaults to default-branch HEAD at checkout)"
14+
required: false
15+
type: string
16+
default_branch:
17+
description: "Target default branch (required — no fallback, to keep SARIF attribution correct)"
18+
required: true
19+
type: string
20+
21+
permissions:
22+
contents: read
23+
24+
jobs:
25+
reconcile:
26+
runs-on: ubuntu-latest
27+
timeout-minutes: 10
28+
concurrency:
29+
group: zizmor-reconcile-${{ github.event.client_payload.repo || inputs.repo }}
30+
cancel-in-progress: true
31+
env:
32+
TARGET_REPO: ${{ github.event.client_payload.repo || inputs.repo }}
33+
TARGET_SHA_INPUT: ${{ github.event.client_payload.sha || inputs.sha }}
34+
TARGET_BRANCH: ${{ github.event.client_payload.default_branch || inputs.default_branch }}
35+
36+
steps:
37+
- name: Harden the runner (Audit all outbound calls)
38+
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
39+
with:
40+
egress-policy: audit
41+
42+
- name: Validate dispatch payload
43+
run: |
44+
if [[ -z "${TARGET_REPO}" ]]; then
45+
echo "::error::dispatch payload missing 'repo'"
46+
exit 1
47+
fi
48+
if [[ -z "${TARGET_BRANCH}" ]]; then
49+
echo "::error::dispatch payload missing 'default_branch' — refuse to guess, SARIF would mis-attribute"
50+
exit 1
51+
fi
52+
53+
# Mint a token scoped to the target repo only, with the permissions
54+
# needed for zizmor (contents/metadata for ref resolution) plus
55+
# security_events: write so we can POST the SARIF back.
56+
- name: Mint scanner token (target repo scope)
57+
id: app-token
58+
uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
59+
with:
60+
app-id: ${{ vars.ZIZMOR_SCANNER_APP_ID }}
61+
private-key: ${{ secrets.ZIZMOR_SCANNER_PRIVATE_KEY }}
62+
owner: rudderlabs
63+
repositories: ${{ env.TARGET_REPO }}
64+
permission-contents: read
65+
permission-metadata: read
66+
permission-security-events: write
67+
68+
- name: Checkout target repo
69+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
70+
with:
71+
repository: rudderlabs/${{ env.TARGET_REPO }}
72+
ref: ${{ env.TARGET_SHA_INPUT || env.TARGET_BRANCH }}
73+
token: ${{ steps.app-token.outputs.token }}
74+
path: target
75+
persist-credentials: false
76+
77+
- name: Resolve target SHA
78+
id: sha
79+
working-directory: target
80+
run: |
81+
resolved=$(git rev-parse HEAD)
82+
echo "sha=${resolved}" >> "$GITHUB_OUTPUT"
83+
echo "Resolved target SHA: ${resolved}"
84+
85+
- name: Checkout shared zizmor config
86+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
87+
with:
88+
repository: rudderlabs/shared-workflows
89+
ref: main
90+
path: .zizmor-shared
91+
persist-credentials: false
92+
sparse-checkout: |
93+
.github/zizmor.yml
94+
sparse-checkout-cone-mode: false
95+
96+
- name: Run zizmor
97+
id: zizmor
98+
env:
99+
SCANNER_TOKEN: ${{ steps.app-token.outputs.token }}
100+
run: |
101+
sarif="${RUNNER_TEMP}/zizmor.sarif"
102+
docker run --rm \
103+
--volume "${GITHUB_WORKSPACE}/target:/workspace:ro" \
104+
--volume "${GITHUB_WORKSPACE}/.zizmor-shared:/zizmor-config:ro" \
105+
--workdir "/workspace" \
106+
--env "GH_TOKEN=${SCANNER_TOKEN}" \
107+
"ghcr.io/zizmorcore/zizmor:1.23.1@sha256:a58f658823d78dd38762f1ce44e31265d636bbfb8b1462cef1dfb1af788d3b86" \
108+
--format=sarif \
109+
--persona=regular \
110+
--min-severity=medium \
111+
--config=/zizmor-config/.github/zizmor.yml \
112+
-- . | tee "${sarif}" || true
113+
114+
exitcode="${PIPESTATUS[0]}"
115+
116+
if [[ -s "${sarif}" ]] && jq empty "${sarif}" 2>/dev/null; then
117+
echo "sarif-valid=true" >> "${GITHUB_OUTPUT}"
118+
else
119+
echo "sarif-valid=false" >> "${GITHUB_OUTPUT}"
120+
fi
121+
122+
case "${exitcode}" in
123+
0|1) ;; # clean or findings — both OK
124+
3) echo "::warning::No auditable inputs in target repo"; echo "skip=true" >> "${GITHUB_OUTPUT}" ;;
125+
*) echo "::error::zizmor failed with exit code ${exitcode}"; exit "${exitcode}" ;;
126+
esac
127+
128+
- name: Upload SARIF to target repo
129+
if: steps.zizmor.outputs.sarif-valid == 'true' && steps.zizmor.outputs.skip != 'true'
130+
env:
131+
GH_TOKEN: ${{ steps.app-token.outputs.token }}
132+
TARGET_SHA: ${{ steps.sha.outputs.sha }}
133+
run: |
134+
sarif="${RUNNER_TEMP}/zizmor.sarif"
135+
gzip -c "${sarif}" | base64 -w0 > "${RUNNER_TEMP}/sarif.b64"
136+
gh api \
137+
--method POST \
138+
-H "Accept: application/vnd.github+json" \
139+
"/repos/rudderlabs/${TARGET_REPO}/code-scanning/sarifs" \
140+
-f commit_sha="${TARGET_SHA}" \
141+
-f ref="refs/heads/${TARGET_BRANCH}" \
142+
-F "sarif=@${RUNNER_TEMP}/sarif.b64"

0 commit comments

Comments
 (0)