diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 000000000000..060f8afa8084 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,68 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +name: "Zizmor Workflow Audit" +on: + pull_request: + paths: + - '.github/workflows/**' + +permissions: + contents: read + +jobs: + zizmor: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5 + - name: Run zizmor audit + # Runs zizmor (https://docs.zizmor.sh/) in offline mode to detect + # unpinned third-party actions. Actions referenced by mutable tag + # (e.g. `actions/checkout@v4`) can be silently replaced by a + # compromised or force-pushed tag, allowing arbitrary code execution + # in CI. Pinning to a full commit SHA makes the reference immutable. + run: | + findings=$(uvx --from zizmor zizmor \ + --offline \ + --format json-v1 \ + .github/workflows 2>/dev/null \ + | jq -r ' + [ + .[] + | select(.ident == "unpinned-uses") + | .locations[] + | select(.symbolic.kind == "Primary") + | { + path: .symbolic.key.Local.given_path, + row: (.concrete.location.start_point.row + 1), + col: (.concrete.location.start_point.column + 1), + feature: .concrete.feature + } + ] + | sort_by(.feature) + | .[] + | "\(.path):\(.row):\(.col)\t\(.feature)" + ') + + if [ -n "$findings" ]; then + echo "::error::Found unpinned GitHub Actions:" + echo "$findings" + exit 1 + fi