From c05ffd699bbf70a6bbf1cf8b45994f697b150bb6 Mon Sep 17 00:00:00 2001 From: Colin Daglish Date: Thu, 24 Sep 2026 10:15:55 +0100 Subject: [PATCH 1/6] feat: add security analysis workflow and configuration files --- .github/workflows/security-analysis.yml | 24 ++++++++++++++++++++++++ configs/checkov.yml | 11 +++++++++++ configs/zizmor.yaml | 1 + 3 files changed, 36 insertions(+) create mode 100644 .github/workflows/security-analysis.yml create mode 100644 configs/checkov.yml create mode 100644 configs/zizmor.yaml diff --git a/.github/workflows/security-analysis.yml b/.github/workflows/security-analysis.yml new file mode 100644 index 0000000..de2dd6c --- /dev/null +++ b/.github/workflows/security-analysis.yml @@ -0,0 +1,24 @@ +name: Security Analysis + +on: + push: + branches: [main] + pull_request: + branches: [main] + +run-name: "${{ github.workflow }} - ${{ github.actor }} - ${{ github.event_name == 'pull_request' && format('PR #{0}', github.event.pull_request.number) || github.ref_name }}" + +permissions: {} # Deny token access by default; jobs grant only what they need. + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + security-analysis: + name: security-analysis + permissions: + actions: read # Required for SARIF upload metadata lookups in private or internal repositories. + contents: read # Required to read repository contents during analysis. + security-events: write # Required to upload security analysis results. + uses: datasciencecampus/github-actions/.github/workflows/security-analysis.yml@caf4ab7c789a34efb07d61113830bcb67d634a38 # v1.7.0 \ No newline at end of file diff --git a/configs/checkov.yml b/configs/checkov.yml new file mode 100644 index 0000000..2a8e24b --- /dev/null +++ b/configs/checkov.yml @@ -0,0 +1,11 @@ +compact: true +directory: + - . +download-external-modules: false +evaluate-variables: true +framework: github_actions +output: cli +quiet: true +soft-fail: false +skip-check: +summary-position: bottom \ No newline at end of file diff --git a/configs/zizmor.yaml b/configs/zizmor.yaml new file mode 100644 index 0000000..3b7c80b --- /dev/null +++ b/configs/zizmor.yaml @@ -0,0 +1 @@ +rules: {} \ No newline at end of file From 20b4791e9195a0537042ada408a874841b14b6f5 Mon Sep 17 00:00:00 2001 From: Colin Daglish Date: Thu, 24 Sep 2026 10:17:31 +0100 Subject: [PATCH 2/6] fix: correct permissions structure in move-major-tag workflow --- .github/workflows/move-major-tag.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/move-major-tag.yml b/.github/workflows/move-major-tag.yml index d25f44a..e771065 100644 --- a/.github/workflows/move-major-tag.yml +++ b/.github/workflows/move-major-tag.yml @@ -11,12 +11,13 @@ on: required: false type: string -permissions: - contents: write +permissions: {} jobs: move-major-tag: runs-on: ubuntu-latest + permissions: + contents: write steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 From 9e32e9e0826f46f6e3bbb2c612bdb875fd15aa94 Mon Sep 17 00:00:00 2001 From: Colin Daglish Date: Thu, 24 Sep 2026 10:25:08 +0100 Subject: [PATCH 3/6] refactor: simplify workflow_dispatch inputs and use environment variables --- .github/workflows/update-tf-modules.yml | 62 ++++++++----------------- 1 file changed, 20 insertions(+), 42 deletions(-) diff --git a/.github/workflows/update-tf-modules.yml b/.github/workflows/update-tf-modules.yml index 072334a..cd7f2ea 100644 --- a/.github/workflows/update-tf-modules.yml +++ b/.github/workflows/update-tf-modules.yml @@ -43,33 +43,7 @@ on: description: "Pull request URL when a PR is created" value: ${{ jobs.update-modules.outputs.pr_url }} - workflow_dispatch: - inputs: - manifest_path: - description: "Path to manifest file in this repository" - required: false - default: .github/update-modules-manifest.yml - type: string - terraform_root: - description: "Root directory containing Terraform code to validate and include in PRs" - required: false - default: terraform - type: string - base_branch: - description: "Base branch for pull requests" - required: false - default: main - type: string - create_pr: - description: "Whether to create a pull request after changes are made" - required: false - default: true - type: boolean - updater_ref: - description: "Git ref (branch/tag/SHA) for the update-tf-modules repository" - required: false - default: v0 - type: string + workflow_dispatch: {} concurrency: group: update-tf-modules-${{ github.repository }}-${{ inputs.base_branch || github.ref_name }} cancel-in-progress: false @@ -77,6 +51,12 @@ concurrency: jobs: update-modules: runs-on: ubuntu-latest + env: + MANIFEST_PATH: ${{ inputs.manifest_path || '.github/update-modules-manifest.yml' }} + TERRAFORM_ROOT: ${{ inputs.terraform_root || 'terraform' }} + BASE_BRANCH: ${{ inputs.base_branch || 'main' }} + CREATE_PR: ${{ github.event_name == 'workflow_dispatch' || inputs.create_pr }} + UPDATER_REF: ${{ inputs.updater_ref || 'v0' }} permissions: contents: write pull-requests: write @@ -92,7 +72,6 @@ jobs: - name: Validate updater_ref exists env: UPDATER_REPO: datasciencecampus/update-tf-modules - UPDATER_REF: ${{ inputs.updater_ref }} run: | set -euo pipefail @@ -121,7 +100,6 @@ jobs: id: resolve_updater_ref env: UPDATER_REPO: datasciencecampus/update-tf-modules - UPDATER_REF: ${{ inputs.updater_ref }} run: | set -euo pipefail @@ -164,36 +142,36 @@ jobs: - name: Run module update script env: GITHUB_TOKEN: ${{ secrets.token || secrets.GITHUB_TOKEN }} - # Keep Python discovery aligned with workflow terraform_root input. - UPDATE_TF_MODULES_TARGET_TERRAFORM_ROOT: ${{ inputs.terraform_root }} + # Keep Python discovery aligned with the Terraform root being processed. + UPDATE_TF_MODULES_TARGET_TERRAFORM_ROOT: ${{ env.TERRAFORM_ROOT }} run: | - update-tf-modules --manifest-path "${{ inputs.manifest_path }}" + update-tf-modules --manifest-path "$MANIFEST_PATH" - name: Show changed files run: | git status --short git diff --stat - git diff -- "${{ inputs.terraform_root }}" + git diff -- "$TERRAFORM_ROOT" - name: Detect Terraform changes id: detect_changes run: | - if git diff --quiet -- "${{ inputs.terraform_root }}"; then + if git diff --quiet -- "$TERRAFORM_ROOT"; then echo "changed=false" >> "$GITHUB_OUTPUT" - echo "No Terraform changes detected under ${{ inputs.terraform_root }}" + echo "No Terraform changes detected under $TERRAFORM_ROOT" else echo "changed=true" >> "$GITHUB_OUTPUT" - echo "Terraform changes detected under ${{ inputs.terraform_root }}" + echo "Terraform changes detected under $TERRAFORM_ROOT" fi - name: Check Terraform formatting if: ${{ steps.detect_changes.outputs.changed == 'true' }} run: | - terraform fmt -check -recursive "${{ inputs.terraform_root }}" + terraform fmt -check -recursive "$TERRAFORM_ROOT" - name: Create pull request id: create_pull_request - if: ${{ inputs.create_pr && steps.detect_changes.outputs.changed == 'true' }} + if: ${{ env.CREATE_PR == 'true' && steps.detect_changes.outputs.changed == 'true' }} uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8 continue-on-error: true with: @@ -203,13 +181,13 @@ jobs: body: | Automated update of Terraform module versions. branch: automation/update-terraform-modules - base: ${{ inputs.base_branch }} + base: ${{ env.BASE_BRANCH }} delete-branch: true add-paths: | - ${{ inputs.terraform_root }}/**/*.tf + ${{ env.TERRAFORM_ROOT }}/**/*.tf - name: Validate pull request creation result - if: ${{ inputs.create_pr && steps.detect_changes.outputs.changed == 'true' }} + if: ${{ env.CREATE_PR == 'true' && steps.detect_changes.outputs.changed == 'true' }} run: | if [[ "${{ steps.create_pull_request.outcome }}" != "success" ]]; then echo "Pull request creation failed." @@ -229,7 +207,7 @@ jobs: echo "## update-tf-modules result" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" echo "- changed: ${{ steps.detect_changes.outputs.changed }}" >> "$GITHUB_STEP_SUMMARY" - echo "- create_pr: ${{ inputs.create_pr }}" >> "$GITHUB_STEP_SUMMARY" + echo "- create_pr: $CREATE_PR" >> "$GITHUB_STEP_SUMMARY" PR_NUMBER="${{ steps.create_pull_request.outputs.pull-request-number || 'n/a' }}" echo "- pr_number: ${PR_NUMBER}" >> "$GITHUB_STEP_SUMMARY" echo "- pr_url: ${{ steps.create_pull_request.outputs.pull-request-url || 'n/a' }}" >> "$GITHUB_STEP_SUMMARY" From cd5e8951758c05d8340e19bca68d2ee007856c54 Mon Sep 17 00:00:00 2001 From: Colin Daglish Date: Thu, 24 Sep 2026 10:26:44 +0100 Subject: [PATCH 4/6] refactor: simplify workflow_dispatch inputs for move-major-tag workflow --- .github/workflows/move-major-tag.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/move-major-tag.yml b/.github/workflows/move-major-tag.yml index e771065..c6bdaea 100644 --- a/.github/workflows/move-major-tag.yml +++ b/.github/workflows/move-major-tag.yml @@ -4,12 +4,7 @@ on: release: types: - published - workflow_dispatch: - inputs: - tag: - description: "Optional stable release tag to map (for example v0.2.1). If omitted, uses latest release." - required: false - type: string + workflow_dispatch: {} permissions: {} @@ -25,7 +20,7 @@ jobs: - name: Move major tag to released version env: REPO: ${{ github.repository }} - TAG: ${{ inputs.tag || github.event.release.tag_name }} + TAG: ${{ github.event.release.tag_name }} run: | set -euo pipefail From 01ee76e8ee58794a8bb841af96734be45c90070e Mon Sep 17 00:00:00 2001 From: Colin Daglish Date: Thu, 24 Sep 2026 10:30:39 +0100 Subject: [PATCH 5/6] refactor: update permissions structure across workflows for clarity and security --- .github/workflows/move-major-tag.yml | 4 ++-- .github/workflows/project-automation.yml | 7 +++++-- .github/workflows/release-please.yml | 7 ++++--- .github/workflows/test-reusable-workflow-contract.yml | 5 +++-- .github/workflows/test-suite.yml | 5 +++-- .github/workflows/update-tf-modules.yml | 7 +++++-- 6 files changed, 22 insertions(+), 13 deletions(-) diff --git a/.github/workflows/move-major-tag.yml b/.github/workflows/move-major-tag.yml index c6bdaea..91c7322 100644 --- a/.github/workflows/move-major-tag.yml +++ b/.github/workflows/move-major-tag.yml @@ -6,13 +6,13 @@ on: - published workflow_dispatch: {} -permissions: {} +permissions: {} # Deny token access by default; the tag-moving job grants only tag update access. jobs: move-major-tag: runs-on: ubuntu-latest permissions: - contents: write + contents: write # Required to move the major release tag. steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 diff --git a/.github/workflows/project-automation.yml b/.github/workflows/project-automation.yml index dc16684..fbb1131 100644 --- a/.github/workflows/project-automation.yml +++ b/.github/workflows/project-automation.yml @@ -6,8 +6,7 @@ on: pull_request: types: [opened, reopened] -permissions: - contents: read # Required by the reusable project-routing workflows. +permissions: {} # Deny token access by default; project-routing jobs grant read access. concurrency: group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.issue.number || github.event.pull_request.number }} @@ -17,6 +16,8 @@ jobs: add-issue-to-projects: if: github.event_name == 'issues' uses: datasciencecampus/github-actions/.github/workflows/add-issue-to-projects.yml@caf4ab7c789a34efb07d61113830bcb67d634a38 # v1.7.0 + permissions: + contents: read # Required by the reusable issue-routing workflow. secrets: PROJECT_ROUTER_BOT_PRIVATE_KEY: ${{ secrets.PROJECT_ROUTER_BOT_PRIVATE_KEY }} with: @@ -27,6 +28,8 @@ jobs: add-pr-to-projects: if: github.event_name == 'pull_request' uses: datasciencecampus/github-actions/.github/workflows/add-pr-to-projects.yml@caf4ab7c789a34efb07d61113830bcb67d634a38 # v1.7.0 + permissions: + contents: read # Required by the reusable pull-request routing workflow. secrets: PROJECT_ROUTER_BOT_PRIVATE_KEY: ${{ secrets.PROJECT_ROUTER_BOT_PRIVATE_KEY }} with: diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index c992771..96cf9d8 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -6,13 +6,14 @@ on: - main workflow_dispatch: -permissions: - contents: write - pull-requests: write +permissions: {} # Deny token access by default; the release job grants only what it needs. jobs: release-please: runs-on: ubuntu-latest + permissions: + contents: write # Required to create release commits and move the major tag. + pull-requests: write # Required to create and update release pull requests. steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 diff --git a/.github/workflows/test-reusable-workflow-contract.yml b/.github/workflows/test-reusable-workflow-contract.yml index a3a5940..03b9352 100644 --- a/.github/workflows/test-reusable-workflow-contract.yml +++ b/.github/workflows/test-reusable-workflow-contract.yml @@ -1,7 +1,6 @@ name: Test reusable workflow contract -permissions: - contents: read +permissions: {} # Deny token access by default; the reusable workflow job grants read access. on: workflow_dispatch: @@ -17,6 +16,8 @@ on: jobs: invoke-reusable-workflow: uses: ./.github/workflows/update-tf-modules.yml + permissions: + contents: read # Required for the reusable workflow contract test to read repository contents. with: manifest_path: .github/update-modules-manifest.yml create_pr: false diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml index c628bdd..ba9ab91 100644 --- a/.github/workflows/test-suite.yml +++ b/.github/workflows/test-suite.yml @@ -4,12 +4,13 @@ on: pull_request: branches: [ "main" ] -permissions: - contents: read +permissions: {} # Deny token access by default; the test job grants checkout access. jobs: run-tests: runs-on: ubuntu-latest + permissions: + contents: read # Required to check out the repository before running tests. steps: - name: Checkout code diff --git a/.github/workflows/update-tf-modules.yml b/.github/workflows/update-tf-modules.yml index cd7f2ea..48ed9fa 100644 --- a/.github/workflows/update-tf-modules.yml +++ b/.github/workflows/update-tf-modules.yml @@ -44,6 +44,9 @@ on: value: ${{ jobs.update-modules.outputs.pr_url }} workflow_dispatch: {} + +permissions: {} # Deny token access by default; the update job grants only the write access it needs. + concurrency: group: update-tf-modules-${{ github.repository }}-${{ inputs.base_branch || github.ref_name }} cancel-in-progress: false @@ -58,8 +61,8 @@ jobs: CREATE_PR: ${{ github.event_name == 'workflow_dispatch' || inputs.create_pr }} UPDATER_REF: ${{ inputs.updater_ref || 'v0' }} permissions: - contents: write - pull-requests: write + contents: write # Required to commit updated Terraform module versions. + pull-requests: write # Required to create and update the automated pull request. outputs: changed: ${{ steps.detect_changes.outputs.changed }} pr_number: ${{ steps.create_pull_request.outputs.pull-request-number }} From 537e05fc8dddae83eba069722728a242fbcef0e3 Mon Sep 17 00:00:00 2001 From: Colin Daglish Date: Thu, 24 Sep 2026 10:40:45 +0100 Subject: [PATCH 6/6] refactor: enhance workflow_dispatch inputs for move-major-tag and update-tf-modules workflows --- .github/workflows/move-major-tag.yml | 10 +++- .github/workflows/update-tf-modules.yml | 63 +++++++++++++++++-------- 2 files changed, 51 insertions(+), 22 deletions(-) diff --git a/.github/workflows/move-major-tag.yml b/.github/workflows/move-major-tag.yml index 91c7322..7f8bc82 100644 --- a/.github/workflows/move-major-tag.yml +++ b/.github/workflows/move-major-tag.yml @@ -1,10 +1,16 @@ name: Move Major Tag +# checkov:skip=CKV_GHA_7:Manual tag input is required to repair or remap a specific stable release. on: release: types: - published - workflow_dispatch: {} + workflow_dispatch: + inputs: + tag: + description: "Optional stable release tag to map (for example v0.2.1). If omitted, uses latest release." + required: false + type: string permissions: {} # Deny token access by default; the tag-moving job grants only tag update access. @@ -20,7 +26,7 @@ jobs: - name: Move major tag to released version env: REPO: ${{ github.repository }} - TAG: ${{ github.event.release.tag_name }} + TAG: ${{ inputs.tag || github.event.release.tag_name }} run: | set -euo pipefail diff --git a/.github/workflows/update-tf-modules.yml b/.github/workflows/update-tf-modules.yml index 48ed9fa..f31d34c 100644 --- a/.github/workflows/update-tf-modules.yml +++ b/.github/workflows/update-tf-modules.yml @@ -1,5 +1,6 @@ name: Update Terraform module versions +# checkov:skip=CKV_GHA_7:Manual inputs are required to select the manifest, Terraform root, target branch, updater ref, and PR behavior. on: workflow_call: inputs: @@ -43,7 +44,33 @@ on: description: "Pull request URL when a PR is created" value: ${{ jobs.update-modules.outputs.pr_url }} - workflow_dispatch: {} + workflow_dispatch: + inputs: + manifest_path: + description: "Path to manifest file in this repository" + required: false + default: .github/update-modules-manifest.yml + type: string + terraform_root: + description: "Root directory containing Terraform code to validate and include in PRs" + required: false + default: terraform + type: string + base_branch: + description: "Base branch for pull requests" + required: false + default: main + type: string + create_pr: + description: "Whether to create a pull request after changes are made" + required: false + default: true + type: boolean + updater_ref: + description: "Git ref (branch/tag/SHA) for the update-tf-modules repository" + required: false + default: v0 + type: string permissions: {} # Deny token access by default; the update job grants only the write access it needs. @@ -54,12 +81,6 @@ concurrency: jobs: update-modules: runs-on: ubuntu-latest - env: - MANIFEST_PATH: ${{ inputs.manifest_path || '.github/update-modules-manifest.yml' }} - TERRAFORM_ROOT: ${{ inputs.terraform_root || 'terraform' }} - BASE_BRANCH: ${{ inputs.base_branch || 'main' }} - CREATE_PR: ${{ github.event_name == 'workflow_dispatch' || inputs.create_pr }} - UPDATER_REF: ${{ inputs.updater_ref || 'v0' }} permissions: contents: write # Required to commit updated Terraform module versions. pull-requests: write # Required to create and update the automated pull request. @@ -75,6 +96,7 @@ jobs: - name: Validate updater_ref exists env: UPDATER_REPO: datasciencecampus/update-tf-modules + UPDATER_REF: ${{ inputs.updater_ref }} run: | set -euo pipefail @@ -103,6 +125,7 @@ jobs: id: resolve_updater_ref env: UPDATER_REPO: datasciencecampus/update-tf-modules + UPDATER_REF: ${{ inputs.updater_ref }} run: | set -euo pipefail @@ -145,36 +168,36 @@ jobs: - name: Run module update script env: GITHUB_TOKEN: ${{ secrets.token || secrets.GITHUB_TOKEN }} - # Keep Python discovery aligned with the Terraform root being processed. - UPDATE_TF_MODULES_TARGET_TERRAFORM_ROOT: ${{ env.TERRAFORM_ROOT }} + # Keep Python discovery aligned with workflow terraform_root input. + UPDATE_TF_MODULES_TARGET_TERRAFORM_ROOT: ${{ inputs.terraform_root }} run: | - update-tf-modules --manifest-path "$MANIFEST_PATH" + update-tf-modules --manifest-path "${{ inputs.manifest_path }}" - name: Show changed files run: | git status --short git diff --stat - git diff -- "$TERRAFORM_ROOT" + git diff -- "${{ inputs.terraform_root }}" - name: Detect Terraform changes id: detect_changes run: | - if git diff --quiet -- "$TERRAFORM_ROOT"; then + if git diff --quiet -- "${{ inputs.terraform_root }}"; then echo "changed=false" >> "$GITHUB_OUTPUT" - echo "No Terraform changes detected under $TERRAFORM_ROOT" + echo "No Terraform changes detected under ${{ inputs.terraform_root }}" else echo "changed=true" >> "$GITHUB_OUTPUT" - echo "Terraform changes detected under $TERRAFORM_ROOT" + echo "Terraform changes detected under ${{ inputs.terraform_root }}" fi - name: Check Terraform formatting if: ${{ steps.detect_changes.outputs.changed == 'true' }} run: | - terraform fmt -check -recursive "$TERRAFORM_ROOT" + terraform fmt -check -recursive "${{ inputs.terraform_root }}" - name: Create pull request id: create_pull_request - if: ${{ env.CREATE_PR == 'true' && steps.detect_changes.outputs.changed == 'true' }} + if: ${{ inputs.create_pr && steps.detect_changes.outputs.changed == 'true' }} uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8 continue-on-error: true with: @@ -184,13 +207,13 @@ jobs: body: | Automated update of Terraform module versions. branch: automation/update-terraform-modules - base: ${{ env.BASE_BRANCH }} + base: ${{ inputs.base_branch }} delete-branch: true add-paths: | - ${{ env.TERRAFORM_ROOT }}/**/*.tf + ${{ inputs.terraform_root }}/**/*.tf - name: Validate pull request creation result - if: ${{ env.CREATE_PR == 'true' && steps.detect_changes.outputs.changed == 'true' }} + if: ${{ inputs.create_pr && steps.detect_changes.outputs.changed == 'true' }} run: | if [[ "${{ steps.create_pull_request.outcome }}" != "success" ]]; then echo "Pull request creation failed." @@ -210,7 +233,7 @@ jobs: echo "## update-tf-modules result" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" echo "- changed: ${{ steps.detect_changes.outputs.changed }}" >> "$GITHUB_STEP_SUMMARY" - echo "- create_pr: $CREATE_PR" >> "$GITHUB_STEP_SUMMARY" + echo "- create_pr: ${{ inputs.create_pr }}" >> "$GITHUB_STEP_SUMMARY" PR_NUMBER="${{ steps.create_pull_request.outputs.pull-request-number || 'n/a' }}" echo "- pr_number: ${PR_NUMBER}" >> "$GITHUB_STEP_SUMMARY" echo "- pr_url: ${{ steps.create_pull_request.outputs.pull-request-url || 'n/a' }}" >> "$GITHUB_STEP_SUMMARY"