From 58355637d38cf1c6e31d4d4c8ca6a9ca0fa50a99 Mon Sep 17 00:00:00 2001 From: Tim Diekmann Date: Tue, 4 Aug 2026 15:30:26 +0200 Subject: [PATCH 1/3] SRE-901: Harden the centralized Renovate workflow against compromised actions Install Renovate locally with --ignore-scripts instead of globally, scope the App token to the calling repo, and drop contents: write. --- .../workflows/housekeeping-dependencies.yml | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/housekeeping-dependencies.yml b/.github/workflows/housekeeping-dependencies.yml index 8e0680f..b5274f7 100644 --- a/.github/workflows/housekeeping-dependencies.yml +++ b/.github/workflows/housekeeping-dependencies.yml @@ -69,7 +69,7 @@ on: permissions: actions: read - contents: write + contents: read id-token: write jobs: @@ -121,20 +121,33 @@ jobs: automation/data/pipelines/hash/dev github_worker_app_id | GITHUB_WORKER_APP_ID ; automation/data/pipelines/hash/dev github_worker_app_private_key | GITHUB_WORKER_APP_PRIVATE_KEY ; + - name: Resolve repository name + id: repo + run: echo "name=${GITHUB_REPOSITORY#*/}" >> "$GITHUB_OUTPUT" + - name: Get token id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 with: app-id: ${{ steps.secrets.outputs.GITHUB_WORKER_APP_ID }} private-key: ${{ steps.secrets.outputs.GITHUB_WORKER_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: ${{ steps.repo.outputs.name }} - name: Setup Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 24 + # Renovate treats `re2` as optional and falls back to native RegExp when the + # native build is missing, which is what skipping install scripts causes here. - name: Install Renovate - run: npm install --global "renovate@${{ steps.renovate-version.outputs.version }}" + env: + RENOVATE_VERSION: ${{ steps.renovate-version.outputs.version }} + run: | + mkdir -p "$RUNNER_TEMP/renovate" + npm install --ignore-scripts --no-audit --no-fund \ + --prefix "$RUNNER_TEMP/renovate" "renovate@$RENOVATE_VERSION" - name: Download renovate cache uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21 @@ -164,7 +177,9 @@ jobs: RENOVATE_PLATFORM_COMMIT: enabled RENOVATE_REPOSITORIES: ${{ github.repository }} RENOVATE_REPOSITORY_CACHE: ${{ env.repo_cache }} - run: renovate + RENOVATE_ALLOW_SCRIPTS: "false" + RENOVATE_IGNORE_SCRIPTS: "true" + run: "$RUNNER_TEMP/renovate/node_modules/.bin/renovate" - name: Compress renovate cache if: env.is_pr != 'true' && env.dry_run == 'disabled' && env.repo_cache != 'disabled' From b448e414a180d5049964c1162f285f1110fedce9 Mon Sep 17 00:00:00 2001 From: Tim Diekmann Date: Tue, 4 Aug 2026 15:56:42 +0200 Subject: [PATCH 2/3] SRE-901: Install Renovate from the lockfile instead of resolving at runtime npm ci cannot pull a package published after the lockfile was written, which is what let the malicious cacheable-request into the tree. This also removes the renovate@ string, so a package.json value can no longer redirect the install. --- .../workflows/housekeeping-dependencies.yml | 31 +++++++------------ .github/workflows/lint.yml | 4 +-- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/.github/workflows/housekeeping-dependencies.yml b/.github/workflows/housekeeping-dependencies.yml index b5274f7..bac4442 100644 --- a/.github/workflows/housekeeping-dependencies.yml +++ b/.github/workflows/housekeeping-dependencies.yml @@ -1,9 +1,9 @@ # Centralized Renovate workflow # # Reusable workflow that runs Renovate with a centrally pinned version. -# The Renovate version is defined in /package.json and managed by Renovate itself. +# The Renovate version is locked in /package-lock.json and managed by Renovate itself. # -# Uses OIDC token to resolve the correct checkout ref for the package.json. +# Uses OIDC token to resolve the correct checkout ref for the lockfile. # # TODO: Replace OIDC workaround with $/ syntax once available # see: https://github.com/orgs/community/discussions/26245#discussioncomment-15601440 @@ -96,19 +96,14 @@ jobs: const ref = job_workflow_ref.split('@')[1]; core.setOutput('ref', ref); - - name: Checkout .github repo for Renovate version + - name: Checkout .github repo for the Renovate lockfile uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: hashintel/.github ref: ${{ steps.workflow-ref.outputs.ref }} - sparse-checkout: package.json - - - name: Read Renovate version - id: renovate-version - run: | - version=$(jq -r '.devDependencies.renovate' package.json) - echo "version=$version" >> "$GITHUB_OUTPUT" - echo "Renovate version: $version" + sparse-checkout: | + package.json + package-lock.json - name: Authenticate Vault id: secrets @@ -139,15 +134,13 @@ jobs: with: node-version: 24 - # Renovate treats `re2` as optional and falls back to native RegExp when the - # native build is missing, which is what skipping install scripts causes here. + # `npm ci` resolves nothing at runtime, so a package published after the lockfile + # was written cannot enter the tree. Renovate treats `re2` as optional and falls + # back to native RegExp when the native build is missing. - name: Install Renovate - env: - RENOVATE_VERSION: ${{ steps.renovate-version.outputs.version }} run: | - mkdir -p "$RUNNER_TEMP/renovate" - npm install --ignore-scripts --no-audit --no-fund \ - --prefix "$RUNNER_TEMP/renovate" "renovate@$RENOVATE_VERSION" + npm ci --ignore-scripts --no-audit --no-fund + echo "$PWD/node_modules/.bin" >> "$GITHUB_PATH" - name: Download renovate cache uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21 @@ -179,7 +172,7 @@ jobs: RENOVATE_REPOSITORY_CACHE: ${{ env.repo_cache }} RENOVATE_ALLOW_SCRIPTS: "false" RENOVATE_IGNORE_SCRIPTS: "true" - run: "$RUNNER_TEMP/renovate/node_modules/.bin/renovate" + run: renovate - name: Compress renovate cache if: env.is_pr != 'true' && env.dry_run == 'disabled' && env.repo_cache != 'disabled' diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index b4b851e..3bb3ba6 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -18,8 +18,8 @@ jobs: - name: Validate renovate config run: | - version=$(jq -r '.devDependencies.renovate' package.json) - npm install --global "renovate@$version" + npm ci --ignore-scripts --no-audit --no-fund + export PATH="$PWD/node_modules/.bin:$PATH" # Without arguments the validator only checks this repo's own config # (`renovate.json`). The shared preset `renovate-config.json`, which # other repos extend via `github>hashintel/.github:renovate-config`, From db91559cc1fa8b14c466830d6d00bd2da07a13b3 Mon Sep 17 00:00:00 2001 From: Ciaran Morinan Date: Tue, 4 Aug 2026 16:30:23 +0100 Subject: [PATCH 3/3] Trigger CI