From 934939f2f078bad2ba50f8b67aef0fc8480b1e02 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Sat, 11 Apr 2026 04:12:16 -0700 Subject: [PATCH] fix: harden the action - Mask the retrieved GitHub token with core.setSecret so it is redacted in logs - Stop logging the raw API response (contained the token) - Drop raw response body from error messages - Pass workflow_dispatch inputs via env vars instead of interpolating into shell - Pin all actions to SHA (via pinact) - Add least-privilege permissions blocks to workflows - Use yarn install --frozen-lockfile in CI - Set persist-credentials: false on read-only checkout - Bump action runtime from node16 (EOL) to node20 --- .github/workflows/ci.yml | 30 +++++++++++++++-------- .github/workflows/update-main-version.yml | 14 ++++++++--- action.yml | 2 +- dist/index.js | 7 +++--- index.js | 7 +++--- 5 files changed, 40 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b97698e..4acf024 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,21 +3,26 @@ on: push: pull_request: +permissions: + contents: read + jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false - name: Set up Node - uses: actions/setup-node@v3 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: - node-version: '16' + node-version: '20' cache: 'yarn' - name: Install dependencies run: | - yarn install + yarn install --frozen-lockfile - name: Lint run: | @@ -26,25 +31,27 @@ jobs: build: runs-on: ubuntu-latest if: ${{ github.ref == 'refs/heads/main' }} + permissions: + contents: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Set up Node - uses: actions/setup-node@v3 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: - node-version: '16' + node-version: '20' cache: 'yarn' - name: Install dependencies run: | - yarn install + yarn install --frozen-lockfile - name: Build dist run: | yarn build - name: Push changes - uses: stefanzweifel/git-auto-commit-action@v4 + uses: stefanzweifel/git-auto-commit-action@3ea6ae190baf489ba007f7c92608f33ce20ef04a # v4.16.0 with: commit_message: "chore(build): auto-generate dist" commit_user_name: "github-actions[bot]" @@ -56,8 +63,11 @@ jobs: if: ${{ github.ref == 'refs/heads/main' }} needs: build runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write steps: - - uses: google-github-actions/release-please-action@v3 + - uses: google-github-actions/release-please-action@db8f2c60ee802b3748b512940dde88eabd7b7e01 # v3.7.13 id: release with: release-type: simple diff --git a/.github/workflows/update-main-version.yml b/.github/workflows/update-main-version.yml index 06ac099..f2f6721 100644 --- a/.github/workflows/update-main-version.yml +++ b/.github/workflows/update-main-version.yml @@ -14,11 +14,14 @@ on: options: - v1 +permissions: + contents: write + jobs: tag: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: fetch-depth: 0 - name: Git config @@ -26,6 +29,11 @@ jobs: git config user.name github-actions git config user.email github-actions@github.com - name: Tag new target - run: git tag -f ${{ github.event.inputs.major_version }} ${{ github.event.inputs.target }} + env: + MAJOR_VERSION: ${{ github.event.inputs.major_version }} + TARGET: ${{ github.event.inputs.target }} + run: git tag -f "$MAJOR_VERSION" "$TARGET" - name: Push new tag - run: git push origin ${{ github.event.inputs.major_version }} --force + env: + MAJOR_VERSION: ${{ github.event.inputs.major_version }} + run: git push origin "$MAJOR_VERSION" --force diff --git a/action.yml b/action.yml index 18737e9..53f4b51 100644 --- a/action.yml +++ b/action.yml @@ -3,7 +3,7 @@ description: 'Get a Github access token to access a private repo managed by Stai author: Stainless runs: - using: 'node16' + using: 'node20' main: 'dist/index.js' inputs: diff --git a/dist/index.js b/dist/index.js index de82661..6cdaf0e 100644 --- a/dist/index.js +++ b/dist/index.js @@ -27568,6 +27568,7 @@ async function main() { const result = await retrieveGithubAccessToken(fullRepo, apiKey); + core.setSecret(result.token); core.setOutput('github_access_token', result.token); } catch (err) { core.setFailed(`retrieve-github-access-token failed: ${err.message}`); @@ -27608,11 +27609,9 @@ async function retrieveGithubAccessToken(fullRepo, apiKey) { const data = safeJson(text); if (data instanceof Error) { - throw new Error(`Could not process API response. text=${text} data=${data} status=${res.status}`); + throw new Error(`Could not process API response. status=${res.status} parseError=${data.message}`); } - console.log('API Response', data); - if (data?.error) { throw new Error(`API Error ${res.status} - ${data.error}`); } @@ -27624,6 +27623,8 @@ async function retrieveGithubAccessToken(fullRepo, apiKey) { if (data?.token) { return { token: data.token }; } + + throw new Error(`API response did not include a token. status=${res.status}`); } /** diff --git a/index.js b/index.js index ee817ca..8ad4707 100644 --- a/index.js +++ b/index.js @@ -9,6 +9,7 @@ async function main() { const result = await retrieveGithubAccessToken(fullRepo, apiKey); + core.setSecret(result.token); core.setOutput('github_access_token', result.token); } catch (err) { core.setFailed(`retrieve-github-access-token failed: ${err.message}`); @@ -49,11 +50,9 @@ async function retrieveGithubAccessToken(fullRepo, apiKey) { const data = safeJson(text); if (data instanceof Error) { - throw new Error(`Could not process API response. text=${text} data=${data} status=${res.status}`); + throw new Error(`Could not process API response. status=${res.status} parseError=${data.message}`); } - console.log('API Response', data); - if (data?.error) { throw new Error(`API Error ${res.status} - ${data.error}`); } @@ -65,6 +64,8 @@ async function retrieveGithubAccessToken(fullRepo, apiKey) { if (data?.token) { return { token: data.token }; } + + throw new Error(`API response did not include a token. status=${res.status}`); } /**