diff --git a/.github/actions/setup-openclaw/action.yml b/.github/actions/setup-openclaw/action.yml index fb3cc1a478..d4a8367db7 100644 --- a/.github/actions/setup-openclaw/action.yml +++ b/.github/actions/setup-openclaw/action.yml @@ -81,7 +81,15 @@ runs: shell: bash env: ACTION_DIR: ${{ github.action_path }} - run: | + # zizmor github-env, analysed and accepted: the audit fires on any + # $GITHUB_PATH write, because a write whose value an attacker controls + # puts a directory of their choosing ahead of the real tools. The value + # here is `github.action_path` — the runner's own checkout path for this + # action, not a caller input, not event data — bound through env rather + # than expanded into the script. Exposing its own node_modules/.bin is + # the whole point of the step, and there is no other way to put a + # composite action's binaries on PATH for the steps that follow. + run: | # zizmor: ignore[github-env] set -e npm ci --no-audit --no-fund --prefix "$ACTION_DIR" echo "$ACTION_DIR/node_modules/.bin" >> "$GITHUB_PATH" @@ -103,7 +111,15 @@ runs: shell: bash env: OPENCLAW_GATEWAY_TOKEN_INPUT: ${{ inputs.gateway-token }} - run: | + # zizmor github-env, analysed and accepted: the audit fires on any + # $GITHUB_ENV write built from a value the step did not define itself, + # because $GITHUB_ENV is line-oriented and a newline in the value + # defines extra variables for every later step in the job. That is + # exactly the case the `case` guard below rejects, loudly and before + # the write — which is the mitigation the audit asks for, and predates + # this declaration. The value also reaches the script through env + # rather than being expanded into it, so it is never parsed as shell. + run: | # zizmor: ignore[github-env] set -e # Downstream workflow steps read this to authenticate dashboard # requests against the running gateway. Empty token = dashboard diff --git a/.github/workflows/supply-chain.yml b/.github/workflows/supply-chain.yml index a4e0ee6fa7..a91f5759f3 100644 --- a/.github/workflows/supply-chain.yml +++ b/.github/workflows/supply-chain.yml @@ -249,17 +249,26 @@ jobs: GH_TOKEN: ${{ github.token }} run: | mkdir -p audit - # Workflow definitions only, which is the scope the SecurityAuditScanner - # component describes. + # Workflow definitions AND the composite actions under .github/actions. # - # The composite action under .github/actions/ is a real gap and is NOT - # covered here: its steps run inline in the calling job, with that job's - # token and secrets, so it carries the same rule families a workflow - # does, and nothing scans it today. Widening this input set to include - # it is a one-line change, but it is a scope the blueprint does not - # describe, so it needs the product record first rather than arriving - # as a side effect of turning the scanner on. Tracked as follow-up. + # A composite action's steps run inline in the calling job, with that + # job's token and secrets, so it carries the same rule families a + # workflow does — and until now nothing scanned it. This was recorded + # here as a known gap and deferred; clawmetry-cloud closed the same + # gap on its own mirror of this scan (cloud #2299), which left this + # repo the only one of the three whose actions nothing audited. + # + # Composite actions are named individually rather than by directory: + # pointing zizmor at a directory hands it every YAML file inside, and + # a non-action YAML landing there later would abort the audit + # (recorded as a scanner outage below) rather than be skipped. inputs=".github/workflows" + if [ -d .github/actions ]; then + while IFS= read -r a; do + inputs="$inputs $a" + done < <(find .github/actions -type f \ + \( -name 'action.yml' -o -name 'action.yaml' \) | sort) + fi # Record the file set the scanner was handed. A finding list is # identical whether it audited thirty-seven files or none, so # coverage is the one thing the JSON cannot tell you afterwards.