From 510ddb5fd6e180e8648a89cca52d139dc7cde672 Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Sat, 5 Apr 2025 23:03:07 -0500 Subject: [PATCH 1/7] ci: fix issues indentified by zizmor GHA linter This fixes issues identified by the zizmor linter which checks for Github Actions security best practicies. Summary of changes: - Remove possibilities for shell injection. These can all only be activated by workflow_dispatch input provided by people who already have access to the repository but still a good idea to tidy this up. Many of these occur in the build-package-docs actions. We should test everything to make sure nothing is broken by these changes. - Explicitly set permissions. This is not strictly required, because we already enforce a limited set of default permissions in the repo's GHA settings, but zizmor wants us to be explicit. - Use `persist-credentials: false` with the checkout action. --- .github/workflows/build-package-docs.yaml | 3 +++ .github/workflows/ci.yaml | 3 +++ .github/workflows/labeler.yml | 5 ++++ .github/workflows/pip-compile-dev.yml | 7 +++++- .github/workflows/pip-compile-docs.yml | 7 +++++- .github/workflows/reusable-build-docs.yaml | 23 +++++++++-------- .github/workflows/reusable-deploy-docs.yaml | 13 +++++++--- .github/workflows/reusable-nox.yml | 7 ++++-- .github/workflows/reusable-pip-compile.yml | 28 +++++++++++++++------ .github/workflows/tag.yml | 5 ++++ 10 files changed, 74 insertions(+), 27 deletions(-) diff --git a/.github/workflows/build-package-docs.yaml b/.github/workflows/build-package-docs.yaml index 566c1f7721f..5bc73e49838 100644 --- a/.github/workflows/build-package-docs.yaml +++ b/.github/workflows/build-package-docs.yaml @@ -51,6 +51,9 @@ name: Build and deploy docs - production - test +permissions: + contents: read + jobs: build-package-docs: name: 📝 Build diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 58ab8908fa6..93ee76ba728 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -16,6 +16,9 @@ name: Ansible Docsite CI - ready_for_review # used in PRs created from GitHub Actions workflows workflow_dispatch: +permissions: + contents: read + jobs: nox: uses: ./.github/workflows/reusable-nox.yml diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index c2ad213f33c..7ca18b73634 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -29,6 +29,9 @@ name: "Triage Issues and PRs" +permissions: + contents: read + jobs: label_prs: runs-on: ubuntu-latest @@ -48,6 +51,8 @@ jobs: private-key: ${{ secrets.BOT_APP_KEY }} - name: Checkout parent repository uses: actions/checkout@v5 + with: + persist-credentials: false - name: Install Python 3.12 uses: actions/setup-python@v6 with: diff --git a/.github/workflows/pip-compile-dev.yml b/.github/workflows/pip-compile-dev.yml index a828e9bdf5f..d636c334394 100644 --- a/.github/workflows/pip-compile-dev.yml +++ b/.github/workflows/pip-compile-dev.yml @@ -13,6 +13,9 @@ name: "Refresh dev dependencies" required: false type: string +permissions: + contents: read + jobs: refresh: strategy: @@ -71,4 +74,6 @@ jobs: python-versions: "${{ matrix.python-versions }}" reset-branch: "${{ inputs.reset-branch || false }}" labels: "${{ inputs.labels || 'no_backport,tooling' }}" - secrets: inherit + secrets: + BOT_APP_ID: "${{ secrets.BOT_APP_ID }}" + BOT_APP_KEY: "${{ secrets.BOT_APP_KEY }}" diff --git a/.github/workflows/pip-compile-docs.yml b/.github/workflows/pip-compile-docs.yml index 4e6d44fce54..d52c59ab934 100644 --- a/.github/workflows/pip-compile-docs.yml +++ b/.github/workflows/pip-compile-docs.yml @@ -19,6 +19,9 @@ name: "Refresh docs build dependencies" required: false type: string +permissions: + contents: read + jobs: refresh: name: "Refresh docs build dependencies" @@ -33,4 +36,6 @@ jobs: reset-branch: "${{ inputs.reset-branch || false }}" labels: "${{ inputs.labels || 'doc builds,no_backport' }}" python-versions: "3.12" - secrets: inherit + secrets: + BOT_APP_ID: "${{ secrets.BOT_APP_ID }}" + BOT_APP_KEY: "${{ secrets.BOT_APP_KEY }}" diff --git a/.github/workflows/reusable-build-docs.yaml b/.github/workflows/reusable-build-docs.yaml index 0dd7b277258..d80270d89f7 100644 --- a/.github/workflows/reusable-build-docs.yaml +++ b/.github/workflows/reusable-build-docs.yaml @@ -32,9 +32,6 @@ name: Build docs DOCS_BOT_TOKEN: required: true -env: - PACKAGE_VERSION: ${{ inputs.ansible-package-version }} - jobs: build-package-docs: runs-on: ubuntu-latest @@ -50,6 +47,7 @@ jobs: }} ref: ${{ inputs.repository-branch }} path: build-directory + persist-credentials: false - name: Setup nox uses: wntrblm/nox@2025.10.16 @@ -68,19 +66,22 @@ jobs: -c tests/requirements.txt working-directory: build-directory - - name: Set the VERSION variable - run: echo VERSION="${PACKAGE_VERSION}" >> "${GITHUB_ENV}" - - name: Build the Ansible community package docs - run: >- - make webdocs ${{ + env: + PACKAGE_VERSION: "${{ inputs.ansible-package-version }}" + run: | + # Clear PACKAGE_VERSION if it's set to devel + if [ "${PACKAGE_VERSION}" = "devel" ]; then + PACKAGE_VERSION="" + fi + make webdocs ANSIBLE_VERSION="${PACKAGE_VERSION}" ${{ inputs.generate-redirects && 'EXTRA_TAGS="-t redirects"' || '' - }} ANSIBLE_VERSION="${{ - env.PACKAGE_VERSION != 'devel' && env.PACKAGE_VERSION || '' - }}" + }} working-directory: build-directory/docs/docsite - name: Create a tarball with the build contents + env: + PACKAGE_VERSION: "${{ inputs.ansible-package-version }}" run: >- tar -czvf ansible-package-docs-html-"${PACKAGE_VERSION}"-"$(date '+%Y-%m-%d')"-${{ diff --git a/.github/workflows/reusable-deploy-docs.yaml b/.github/workflows/reusable-deploy-docs.yaml index 2dc41fb1ae3..095fc5e34c6 100644 --- a/.github/workflows/reusable-deploy-docs.yaml +++ b/.github/workflows/reusable-deploy-docs.yaml @@ -33,13 +33,18 @@ jobs: runs-on: ubuntu-latest steps: - name: Log the workflow inputs if deployed + env: + deployment_environment: "${{ inputs.deployment-environment }}" + package_version: "${{ inputs.ansible-package-version }}" + owner: "${{ inputs.repository-owner }}" + branch: "${{ inputs.repository-branch }}" run: | { echo "## Deployment details :shipit:"; - echo "Publish to: ${{ inputs.deployment-environment }}"; - echo "Package version: ${{ inputs.ansible-package-version }}"; - echo "Owner: ${{ inputs.repository-owner }}"; - echo "Branch: ${{ inputs.repository-branch }}"; + echo "Publish to: ${deployment_environment}"; + echo "Package version: ${package_version}"; + echo "Owner: ${owner}"; + echo "Branch: ${branch}"; } >> "${GITHUB_STEP_SUMMARY}" deploy-package-docs: diff --git a/.github/workflows/reusable-nox.yml b/.github/workflows/reusable-nox.yml index c46f4a4552d..ad3aeeb4e01 100644 --- a/.github/workflows/reusable-nox.yml +++ b/.github/workflows/reusable-nox.yml @@ -38,6 +38,8 @@ jobs: steps: - name: Check out repo uses: actions/checkout@v5 + with: + persist-credentials: false - name: Setup nox uses: wntrblm/nox@2025.10.16 with: @@ -46,7 +48,8 @@ jobs: run: | nox -e clone-core - name: "Run nox -e ${{ matrix.session }}" + # Using GHA expression interpolation is fine here, + # as we control all the inputs. + # zizmor: ignore[template-injection] run: | - # Using GHA expression interpolation is fine here, - # as we control all the inputs. nox -e "${{ matrix.session }}" -- ${{ matrix.extra-args }} diff --git a/.github/workflows/reusable-pip-compile.yml b/.github/workflows/reusable-pip-compile.yml index 5c03236a6d2..22379844786 100644 --- a/.github/workflows/reusable-pip-compile.yml +++ b/.github/workflows/reusable-pip-compile.yml @@ -35,23 +35,21 @@ name: "Refresh pinned dependencies" python-versions: type: string required: true + +permissions: + contents: read + jobs: refresh: runs-on: ubuntu-latest environment: github-bot steps: - - name: Generate temp GITHUB_TOKEN - id: create_token - uses: actions/create-github-app-token@v2 - with: - app-id: ${{ secrets.BOT_APP_ID }} - private-key: ${{ secrets.BOT_APP_KEY }} - name: Check out repo uses: actions/checkout@v5 with: fetch-depth: 0 ref: "${{ inputs.base-branch }}" - token: "${{ steps.create_token.outputs.token }}" + persist-credentials: false - name: Fetch required contents of ansible-core run: | python docs/bin/clone-core.py @@ -78,11 +76,25 @@ jobs: echo "branch-exists=false" >> "${GITHUB_OUTPUT}" git switch -c "${pr_branch}" fi + - name: Generate temp GITHUB_TOKEN + id: create_token + uses: actions/create-github-app-token@v2 + with: + app-id: ${{ secrets.BOT_APP_ID }} + private-key: ${{ secrets.BOT_APP_KEY }} + # We could rely on the checkout action to persist the token in the + # repository config, but this way, we can prevent the previous steps + # from having unnecessary access. + - name: "Set up token authentication" + run: gh auth setup-git --hostname github.com - name: "Run nox ${{ inputs.nox-args }}" env: # Ensure the latest pip version is used VIRTUALENV_DOWNLOAD: '1' - # + # nox-args is defined statically in the calling workflows and passing it + # as a shell variable presents quoting issues, so ignore zizmor error + # for now. + # zizmor: ignore[template-injection] run: | nox ${{ inputs.nox-args }} - name: Push new dependency versions and create a PR diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml index 855a241a5a3..7d4fd21337b 100644 --- a/.github/workflows/tag.yml +++ b/.github/workflows/tag.yml @@ -11,6 +11,9 @@ name: Sync tags with ansible-core releases schedule: - cron: "0 * * * *" # Hourly +permissions: + contents: read # read-only because we use bot token to push + jobs: tag: runs-on: "ubuntu-latest" @@ -30,12 +33,14 @@ jobs: path: ansible-documentation fetch-depth: 0 token: "${{ steps.create_token.outputs.token }}" + persist-credentials: true - name: Check out core uses: actions/checkout@v5 with: repository: ansible/ansible path: ansible fetch-depth: 0 + persist-credentials: false - name: Setup nox uses: wntrblm/nox@2025.10.16 with: From 860767b994f9c95e7f51e66790d97c7756abb75a Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Sat, 5 Apr 2025 23:26:57 -0500 Subject: [PATCH 2/7] ci: run zizmor in CI and noxfile - Adds lockfile - Adds nox session - Adds nox session to CI matrix --- .github/workflows/reusable-nox.yml | 2 ++ noxfile.py | 9 +++++++++ tests/zizmor.in | 1 + tests/zizmor.txt | 4 ++++ 4 files changed, 16 insertions(+) create mode 100644 tests/zizmor.in create mode 100644 tests/zizmor.txt diff --git a/.github/workflows/reusable-nox.yml b/.github/workflows/reusable-nox.yml index ad3aeeb4e01..08c1df6421a 100644 --- a/.github/workflows/reusable-nox.yml +++ b/.github/workflows/reusable-nox.yml @@ -34,6 +34,8 @@ jobs: - session: "pip-compile" extra-args: "--check" python-versions: "3.12" + - session: "zizmor" + python-versions: "3.12" name: "Run nox ${{ matrix.session }} session" steps: - name: Check out repo diff --git a/noxfile.py b/noxfile.py index 6ac33a3f134..053179f6ec0 100644 --- a/noxfile.py +++ b/noxfile.py @@ -140,6 +140,15 @@ def actionlint(session: nox.Session) -> None: ) +@nox.session +def zizmor(session: nox.Session) -> None: + """ + Ren zizmor, a Github Actions security checker + """ + install(session, req="zizmor") + session.run("zizmor", "--persona=regular", ".github/workflows") + + @nox.session def lint(session: nox.Session): session.notify("typing") diff --git a/tests/zizmor.in b/tests/zizmor.in new file mode 100644 index 00000000000..d0c48825813 --- /dev/null +++ b/tests/zizmor.in @@ -0,0 +1 @@ +zizmor diff --git a/tests/zizmor.txt b/tests/zizmor.txt new file mode 100644 index 00000000000..9cbdbd2f23f --- /dev/null +++ b/tests/zizmor.txt @@ -0,0 +1,4 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile --universal --output-file tests/zizmor.txt tests/zizmor.in +zizmor==1.16.0 + # via -r tests/zizmor.in From 2048554773eef2ab124b17932dde9c92da1e11a6 Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Mon, 27 Oct 2025 17:06:52 -0500 Subject: [PATCH 3/7] ci: fix additional issues identified by zizmor - Add default permissions to new workflows - Add cooldown to dependabot --- .github/dependabot.yml | 2 ++ .github/workflows/build-devel-docs.yaml | 3 +++ .github/workflows/build-latest-docs.yaml | 3 +++ .github/workflows/labeler.yml | 4 +++- .github/workflows/release-porting-guide.yml | 4 ++++ .github/workflows/reusable-build-docs.yaml | 4 +++- 6 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2f4ff900d88..88cbda4414f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,3 +9,5 @@ updates: directory: "/" schedule: interval: "weekly" + cooldown: + default-days: 4 diff --git a/.github/workflows/build-devel-docs.yaml b/.github/workflows/build-devel-docs.yaml index 115908c88be..d0c77b37840 100644 --- a/.github/workflows/build-devel-docs.yaml +++ b/.github/workflows/build-devel-docs.yaml @@ -5,6 +5,9 @@ name: Scheduled build for devel docs # Run at 05:22 daily - cron: '22 5 * * *' +permissions: + contents: read + jobs: build-package-docs: name: 📝 Build diff --git a/.github/workflows/build-latest-docs.yaml b/.github/workflows/build-latest-docs.yaml index 0427c022c71..d89cdd28ce6 100644 --- a/.github/workflows/build-latest-docs.yaml +++ b/.github/workflows/build-latest-docs.yaml @@ -5,6 +5,9 @@ name: Scheduled build for latest docs # Run at 05:41 on Monday - cron: '41 5 * * 1' +permissions: + contents: read + jobs: build-package-docs: name: 📝 Build diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 7ca18b73634..1fbd9905b44 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -3,7 +3,9 @@ # SPDX-License-Identifier: GPL-3.0-or-later "on": - pull_request_target: + # This workflow does not execute untrusted code from pull requests and all + # inputs are properly sanitized, + pull_request_target: # zizmor: ignore[dangerous-triggers] types: - opened # default - synchronize # default diff --git a/.github/workflows/release-porting-guide.yml b/.github/workflows/release-porting-guide.yml index 4c96d438291..5f993788292 100644 --- a/.github/workflows/release-porting-guide.yml +++ b/.github/workflows/release-porting-guide.yml @@ -12,6 +12,8 @@ on: description: >- Exact release version. For example, 12.1.0 required: true +permissions: + contents: read jobs: upload-porting-guide: @@ -40,6 +42,7 @@ jobs: uses: actions/checkout@v5 with: token: ${{ steps.create_token.outputs.token }} + persist-credentials: true # Needed to push to the repo - name: Check out ansible-build-data uses: actions/checkout@v5 @@ -47,6 +50,7 @@ jobs: repository: ansible-community/ansible-build-data ref: ${{ inputs.ansible-build-data-branch }} path: ansible-build-data + persist-credentials: false - name: Copy the RST file to the correct path run: >- diff --git a/.github/workflows/reusable-build-docs.yaml b/.github/workflows/reusable-build-docs.yaml index d80270d89f7..ec190cde663 100644 --- a/.github/workflows/reusable-build-docs.yaml +++ b/.github/workflows/reusable-build-docs.yaml @@ -117,8 +117,10 @@ jobs: run: echo "TX_ID=$(date +%s)" >> "${GITHUB_ENV}" - name: Notify the DaWGs in Matrix + # FAIL_MESSAGE is trusted input so okay to inject here. + # zizmor: ignore[template-injection] run: | - curl -X PUT "${{ env.ROOM_URL }}/${TX_ID}" \ + curl -X PUT "${ROOM_URL}/${TX_ID}" \ -H "Authorization: Bearer ${{ secrets.DOCS_BOT_TOKEN }}" \ -H "Content-Type: application/json" \ -d '{"msgtype": "m.text", "body": "${{ env.FAIL_MESSAGE }}"}' From fdaae59a743626488ed6baf8e3b2986345349a9c Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Mon, 27 Oct 2025 17:09:38 -0500 Subject: [PATCH 4/7] ci: add zizmor configuration for unpinned-uses We could configure dependabot to pin shared workflow commit SHA hashes, but for now, let's relax the unpinned-uses relax --- .github/zizmor.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/zizmor.yml diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 00000000000..9dbdee8ad32 --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,7 @@ +rules: + unpinned-uses: + config: + policies: + "wntrblm/nox": ref-pin + "re-actors/alls-green": ref-pin + "actions/*": ref-pin From f67e1370deaad2d6d98f057140ebb865643afd87 Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Mon, 27 Oct 2025 17:25:05 -0500 Subject: [PATCH 5/7] ci: restore secrets: inheirt for pip-compile workflows See comment for more details. --- .github/workflows/pip-compile-dev.yml | 9 ++++++--- .github/workflows/pip-compile-docs.yml | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pip-compile-dev.yml b/.github/workflows/pip-compile-dev.yml index d636c334394..c8a6cbd7410 100644 --- a/.github/workflows/pip-compile-dev.yml +++ b/.github/workflows/pip-compile-dev.yml @@ -74,6 +74,9 @@ jobs: python-versions: "${{ matrix.python-versions }}" reset-branch: "${{ inputs.reset-branch || false }}" labels: "${{ inputs.labels || 'no_backport,tooling' }}" - secrets: - BOT_APP_ID: "${{ secrets.BOT_APP_ID }}" - BOT_APP_KEY: "${{ secrets.BOT_APP_KEY }}" + # Pass using inherit, as this seems to be the only possible way to access + # secrets defined in an enviornment when using nested workflows. + secrets: inherit # zizmor: ignore[secrets-inherit] + # secrets: + # BOT_APP_ID: "${{ secrets.BOT_APP_ID }}" + # BOT_APP_KEY: "${{ secrets.BOT_APP_KEY }}" diff --git a/.github/workflows/pip-compile-docs.yml b/.github/workflows/pip-compile-docs.yml index d52c59ab934..2ae80090ebe 100644 --- a/.github/workflows/pip-compile-docs.yml +++ b/.github/workflows/pip-compile-docs.yml @@ -36,6 +36,9 @@ jobs: reset-branch: "${{ inputs.reset-branch || false }}" labels: "${{ inputs.labels || 'doc builds,no_backport' }}" python-versions: "3.12" - secrets: - BOT_APP_ID: "${{ secrets.BOT_APP_ID }}" - BOT_APP_KEY: "${{ secrets.BOT_APP_KEY }}" + # Pass using inherit, as this seems to be the only possible way to access + # secrets defined in an enviornment when using nested workflows. + secrets: inherit # zizmor: ignore[secrets-inherit] + # secrets: + # BOT_APP_ID: "${{ secrets.BOT_APP_ID }}" + # BOT_APP_KEY: "${{ secrets.BOT_APP_KEY }}" From 5214e734e0f3bb9a45636aed8c6d5c0a1b511801 Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Mon, 27 Oct 2025 17:27:44 -0500 Subject: [PATCH 6/7] ci: fix token auth for pip-compile workflow --- .github/workflows/reusable-pip-compile.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/reusable-pip-compile.yml b/.github/workflows/reusable-pip-compile.yml index 22379844786..078ed638e21 100644 --- a/.github/workflows/reusable-pip-compile.yml +++ b/.github/workflows/reusable-pip-compile.yml @@ -76,17 +76,6 @@ jobs: echo "branch-exists=false" >> "${GITHUB_OUTPUT}" git switch -c "${pr_branch}" fi - - name: Generate temp GITHUB_TOKEN - id: create_token - uses: actions/create-github-app-token@v2 - with: - app-id: ${{ secrets.BOT_APP_ID }} - private-key: ${{ secrets.BOT_APP_KEY }} - # We could rely on the checkout action to persist the token in the - # repository config, but this way, we can prevent the previous steps - # from having unnecessary access. - - name: "Set up token authentication" - run: gh auth setup-git --hostname github.com - name: "Run nox ${{ inputs.nox-args }}" env: # Ensure the latest pip version is used @@ -97,6 +86,17 @@ jobs: # zizmor: ignore[template-injection] run: | nox ${{ inputs.nox-args }} + - name: Generate temp GITHUB_TOKEN + id: create_token + uses: actions/create-github-app-token@v2 + with: + app-id: ${{ secrets.BOT_APP_ID }} + private-key: ${{ secrets.BOT_APP_KEY }} + # We could rely on the checkout action to persist the token in the + # repository config, but this way, we can prevent the previous steps + # from having unnecessary access. + - name: "Set up token authentication" + run: gh auth setup-git --force --hostname github.com - name: Push new dependency versions and create a PR env: GITHUB_TOKEN: ${{ steps.create_token.outputs.token }} From bb63f4d764e4116549deaf14db91f93ed5c11f59 Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Mon, 27 Oct 2025 18:06:56 -0500 Subject: [PATCH 7/7] README: mention that lint session runs GHA checks --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ae2ba67ce8a..8c5f80e4982 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ The `nox` configuration also contains session to run automated docs checkers. If you want to view the generated HTML in your browser, you should build the documentation locally. See [Building the documentation locally](https://docs.ansible.com/ansible/latest/community/documentation_contributions.html#building-the-documentation-locally) for more information. -* Lint, type check, and format Python scripts in this repository. +* Lint, type check, and format Python scripts in this repository and lint the Github Actions workflows for syntax and security issues: ``` bash nox -s lint