From 032f5581a070f96f8b1133d0bbcf6dc8e5a18eee Mon Sep 17 00:00:00 2001 From: Carlos Lopez <9074620+rastut@users.noreply.github.com> Date: Mon, 1 Jun 2026 16:37:26 +0200 Subject: [PATCH 1/4] Harden GHA + Zizmor CI --- .github/workflows/bd_sca_scanner.yaml | 2 ++ .../hyperforge_agent_package_workflow.yaml | 20 +++++++++-- .github/workflows/hyperforge_workflow.yaml | 34 ++++++++++++++++--- .github/workflows/trufflehog.yaml | 2 ++ .github/workflows/zizmor.yaml | 26 ++++++++++++++ 5 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/zizmor.yaml diff --git a/.github/workflows/bd_sca_scanner.yaml b/.github/workflows/bd_sca_scanner.yaml index e24322d..6066ca3 100644 --- a/.github/workflows/bd_sca_scanner.yaml +++ b/.github/workflows/bd_sca_scanner.yaml @@ -15,6 +15,8 @@ on: jobs: blackduck-sca-scan: runs-on: ubuntu-latest + permissions: + contents: read # actions/checkout concurrency: group: bd-sca-scan-${{ github.ref_name }} cancel-in-progress: true diff --git a/.github/workflows/hyperforge_agent_package_workflow.yaml b/.github/workflows/hyperforge_agent_package_workflow.yaml index 4f051f4..e504b07 100644 --- a/.github/workflows/hyperforge_agent_package_workflow.yaml +++ b/.github/workflows/hyperforge_agent_package_workflow.yaml @@ -21,6 +21,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Install uv uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 @@ -44,6 +46,8 @@ jobs: - test steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: @@ -56,16 +60,26 @@ jobs: - name: Set dependency versions in requirements run: | - python bump.py --package=${{ inputs.package }} --build=${{ github.run_number }} + python bump.py --package=${INPUTS_PACKAGE} --build=${{ github.run_number }} + env: + INPUTS_PACKAGE: ${{ inputs.package }} - name: Install package run: | - pip install -e ./${{ inputs.package }} + pip install -e ./${INPUTS_PACKAGE} + env: + INPUTS_PACKAGE: ${{ inputs.package }} - name: Build wheels - run: pip wheel --no-deps -w dist ./${{ inputs.package }} + run: pip wheel --no-deps -w dist ./${INPUTS_PACKAGE} + env: + INPUTS_PACKAGE: ${{ inputs.package }} - name: Upload to pypi + # zizmor: ignore[use-trusted-publishing] -- PYPI_TOKEN is a scoped + # API token stored as a repo secret. Migration to OIDC Trusted + # Publishing is tracked separately and requires PyPI-side setup for + # all 17 agent packages before the secret can be removed. run: twine upload --verbose dist/* env: TWINE_USERNAME: __token__ diff --git a/.github/workflows/hyperforge_workflow.yaml b/.github/workflows/hyperforge_workflow.yaml index 945fc26..584f65e 100644 --- a/.github/workflows/hyperforge_workflow.yaml +++ b/.github/workflows/hyperforge_workflow.yaml @@ -44,11 +44,31 @@ jobs: chart-version: ${{ steps.param.outputs.chart-version }} steps: + + - name: Generate a token + id: app-token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + app-id: ${{ secrets.GHAPP_ID_NUCLIABOT }} + private-key: ${{ secrets.PK_GHAPP_NUCLIABOT }} + owner: nuclia + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Checkout tooling repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: nuclia/tooling + ref: main + path: tooling + token: ${{ steps.app-token.outputs.token }} + persist-credentials: false - name: Parameter generator - uses: nuclia/tooling/.github/actions/parameter-generator@e3b8787755f131d70b5346b2704b66cd0accb7cb + uses: ./tooling/.github/actions/parameter-generator id: param with: component: forge @@ -83,7 +103,7 @@ jobs: make test - name: Get Cover - uses: orgoro/coverage@v3.1 + uses: orgoro/coverage@ca0c362dc1a4f100447309405e6dfea47e251495 # v3.3.1 if: github.event_name == 'pull_request' with: coverageFile: forge_cov.xml @@ -127,7 +147,7 @@ jobs: password: ${{ steps.gcp-auth-registry.outputs.access_token }} - name: Push image to registries - uses: nuclia/tooling/.github/actions/build-img-regcache@1c637a038d11b783a7ae1f3752b666c16466314e + uses: ./tooling/.github/actions/build-img-regcache with: file: RAO.Dockerfile image-name: ${{ steps.param.outputs.component }} @@ -142,7 +162,7 @@ jobs: if: github.event_name == 'push' - name: Build and push helm chart - uses: nuclia/tooling/.github/actions/build-helm-chart@e3b8787755f131d70b5346b2704b66cd0accb7cb + uses: ./tooling/.github/actions/build-helm-chart with: component: ${{ steps.param.outputs.component }} chart-version: ${{ steps.param.outputs.chart-version }} @@ -174,13 +194,15 @@ jobs: - name: Build failed slack message id: build_message run: | - SLACK_MESSAGE="Testing for component ${{ needs.test.outputs.component }} in main/master has failed for SHA ${{ github.sha }}. Promotion can't be executed if a component is not passing tests." + SLACK_MESSAGE="Testing for component ${NEEDS_TEST_OUTPUTS_COMPONENT} in main/master has failed for SHA ${{ github.sha }}. Promotion can't be executed if a component is not passing tests." COLOR="danger" echo "color=$COLOR" >> $GITHUB_OUTPUT echo "message=$SLACK_MESSAGE" >> $GITHUB_OUTPUT shell: bash + env: + NEEDS_TEST_OUTPUTS_COMPONENT: ${{ needs.test.outputs.component }} - name: Slack notification id: slack @@ -210,6 +232,8 @@ jobs: - test steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: diff --git a/.github/workflows/trufflehog.yaml b/.github/workflows/trufflehog.yaml index 784e14e..f291a0d 100644 --- a/.github/workflows/trufflehog.yaml +++ b/.github/workflows/trufflehog.yaml @@ -8,6 +8,8 @@ on: jobs: trufflehog-scan: runs-on: ubuntu-latest + permissions: + contents: read # actions/checkout steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/zizmor.yaml b/.github/workflows/zizmor.yaml new file mode 100644 index 0000000..451f3a8 --- /dev/null +++ b/.github/workflows/zizmor.yaml @@ -0,0 +1,26 @@ +name: GitHub Actions Security Analysis with zizmor + +on: + push: + branches: [main] + pull_request: + paths: + - '.github/workflows/**' + - '.github/actions/**' + +jobs: + zizmor: + name: Run zizmor + runs-on: ubuntu-latest + permissions: + security-events: write # required by github/codeql-action/upload-sarif + contents: read # required by actions/checkout (private-repo safe default) + actions: read # required by upload-sarif to read workflow run info on private repos + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Run zizmor + uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6 \ No newline at end of file From 89440be8be628801d3eaa7285308607cb8eb77d9 Mon Sep 17 00:00:00 2001 From: Carlos Lopez <9074620+rastut@users.noreply.github.com> Date: Mon, 1 Jun 2026 16:40:46 +0200 Subject: [PATCH 2/4] Advance security to false until oss --- .github/workflows/zizmor.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/zizmor.yaml b/.github/workflows/zizmor.yaml index 451f3a8..39d4d08 100644 --- a/.github/workflows/zizmor.yaml +++ b/.github/workflows/zizmor.yaml @@ -23,4 +23,6 @@ jobs: persist-credentials: false - name: Run zizmor - uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6 \ No newline at end of file + uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6 + with: + advanced-security: false \ No newline at end of file From 8848e6b8d436df834e531459159435cea68b0969 Mon Sep 17 00:00:00 2001 From: Carlos Lopez <9074620+rastut@users.noreply.github.com> Date: Mon, 1 Jun 2026 16:46:03 +0200 Subject: [PATCH 3/4] Harden 2 --- .github/workflows/hyperforge_workflow.yaml | 27 +++++++++++++++------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/.github/workflows/hyperforge_workflow.yaml b/.github/workflows/hyperforge_workflow.yaml index 584f65e..5c43f36 100644 --- a/.github/workflows/hyperforge_workflow.yaml +++ b/.github/workflows/hyperforge_workflow.yaml @@ -25,16 +25,14 @@ env: GCP_WORKLOAD_IDENTITY_PROVIDER: "projects/224545243904/locations/global/workloadIdentityPools/gh-nuclia/providers/gh-nuclia-provider" GCP_SERVICE_ACCOUNT: "github-actions@nuclia-internal.iam.gserviceaccount.com" -permissions: - id-token: write - contents: read - actions: read - packages: write - jobs: test: name: Testing runs-on: learning-simple-arc-runners + permissions: + id-token: write # google-github-actions/auth (OIDC) + contents: read # actions/checkout ×2 + pull-requests: write # orgoro/coverage outputs: component: ${{ steps.param.outputs.component }} hash: ${{ steps.param.outputs.hash }} @@ -52,6 +50,8 @@ jobs: app-id: ${{ secrets.GHAPP_ID_NUCLIABOT }} private-key: ${{ secrets.PK_GHAPP_NUCLIABOT }} owner: nuclia + repositories: tooling + permission-contents: read - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -190,6 +190,7 @@ jobs: needs: test if: (failure() || cancelled()) && github.event_name == 'push' runs-on: nuclia-base + permissions: {} # slackapi/slack-github-action uses webhook secret, not GITHUB_TOKEN steps: - name: Build failed slack message id: build_message @@ -230,6 +231,8 @@ jobs: if: github.event_name == 'push' needs: - test + permissions: + contents: read # actions/checkout steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -257,6 +260,7 @@ jobs: run: pip wheel --no-deps -w dist ./hyperforge - name: Upload to pypi + # zizmor: ignore[use-trusted-publishing] -- migrating to OIDC trusted publishing is tracked separately; PYPI_TOKEN is a scoped API token run: twine upload --verbose dist/* env: TWINE_USERNAME: __token__ @@ -267,6 +271,7 @@ jobs: needs: [test] if: | !failure() && !cancelled() && github.event_name == 'push' + permissions: {} # all API calls use GitHub App token, not GITHUB_TOKEN steps: - name: Generate a token id: app-token @@ -275,6 +280,8 @@ jobs: app-id: ${{ vars.GHAPP_NUCLIA_SERVICE_BOT_ID }} private-key: ${{ secrets.GHAPP_NUCLIA_SERVICE_BOT_PK }} owner: nuclia + repositories: core-apps + permission-contents: write - name: Create components promotion summary. Trigger1 env: @@ -284,6 +291,8 @@ jobs: PROMO_PRODUCTION: ${{ env.PROMO_PRODUCTION }} TEAM: "learning" SLACK_TEAM: "S06B50DM2FL" + COMPONENT: ${{ needs.test.outputs.component }} + CHART_VERSION: ${{ needs.test.outputs.chart-version }} run: | MERGED_JSON=$(jq -cs \ --arg environment "$ENVIRONMENT" \ @@ -293,6 +302,8 @@ jobs: --arg slack_team "$SLACK_TEAM" \ --arg promo_production "$PROMO_PRODUCTION" \ --arg commit_sha "${{ github.sha }}" \ + --arg component "$COMPONENT" \ + --arg chart_version "$CHART_VERSION" \ '{ components: map(.components) | add } + { @@ -317,8 +328,8 @@ jobs: } + { "components": [ { - "component": "${{ needs.test.outputs.component }}", - "chart-version": "${{ needs.test.outputs.chart-version }}", + "component": $component, + "chart-version": $chart_version, "component-type": "regional" } ] From a7515e47f90775fd3e626940a93476218a9d1b5e Mon Sep 17 00:00:00 2001 From: Carlos Lopez <9074620+rastut@users.noreply.github.com> Date: Mon, 1 Jun 2026 16:47:07 +0200 Subject: [PATCH 4/4] Harden 3 --- .../hyperforge_agent_package_workflow.yaml | 10 +++++----- .github/workflows/hyperforge_workflow.yaml | 13 ++++++------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/.github/workflows/hyperforge_agent_package_workflow.yaml b/.github/workflows/hyperforge_agent_package_workflow.yaml index e504b07..7d3c148 100644 --- a/.github/workflows/hyperforge_agent_package_workflow.yaml +++ b/.github/workflows/hyperforge_agent_package_workflow.yaml @@ -22,7 +22,7 @@ jobs: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - persist-credentials: false + persist-credentials: false - name: Install uv uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 @@ -47,7 +47,7 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - persist-credentials: false + persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: @@ -62,18 +62,18 @@ jobs: run: | python bump.py --package=${INPUTS_PACKAGE} --build=${{ github.run_number }} env: - INPUTS_PACKAGE: ${{ inputs.package }} + INPUTS_PACKAGE: ${{ inputs.package }} - name: Install package run: | pip install -e ./${INPUTS_PACKAGE} env: - INPUTS_PACKAGE: ${{ inputs.package }} + INPUTS_PACKAGE: ${{ inputs.package }} - name: Build wheels run: pip wheel --no-deps -w dist ./${INPUTS_PACKAGE} env: - INPUTS_PACKAGE: ${{ inputs.package }} + INPUTS_PACKAGE: ${{ inputs.package }} - name: Upload to pypi # zizmor: ignore[use-trusted-publishing] -- PYPI_TOKEN is a scoped diff --git a/.github/workflows/hyperforge_workflow.yaml b/.github/workflows/hyperforge_workflow.yaml index 5c43f36..d74ade3 100644 --- a/.github/workflows/hyperforge_workflow.yaml +++ b/.github/workflows/hyperforge_workflow.yaml @@ -30,9 +30,9 @@ jobs: name: Testing runs-on: learning-simple-arc-runners permissions: - id-token: write # google-github-actions/auth (OIDC) - contents: read # actions/checkout ×2 - pull-requests: write # orgoro/coverage + id-token: write # google-github-actions/auth (OIDC) + contents: read # actions/checkout ×2 + pull-requests: write # orgoro/coverage outputs: component: ${{ steps.param.outputs.component }} hash: ${{ steps.param.outputs.hash }} @@ -42,7 +42,6 @@ jobs: chart-version: ${{ steps.param.outputs.chart-version }} steps: - - name: Generate a token id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 @@ -190,7 +189,7 @@ jobs: needs: test if: (failure() || cancelled()) && github.event_name == 'push' runs-on: nuclia-base - permissions: {} # slackapi/slack-github-action uses webhook secret, not GITHUB_TOKEN + permissions: {} # slackapi/slack-github-action uses webhook secret, not GITHUB_TOKEN steps: - name: Build failed slack message id: build_message @@ -232,7 +231,7 @@ jobs: needs: - test permissions: - contents: read # actions/checkout + contents: read # actions/checkout steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -271,7 +270,7 @@ jobs: needs: [test] if: | !failure() && !cancelled() && github.event_name == 'push' - permissions: {} # all API calls use GitHub App token, not GITHUB_TOKEN + permissions: {} # all API calls use GitHub App token, not GITHUB_TOKEN steps: - name: Generate a token id: app-token