From 9ca4fac4c1b08b75b7d86edc1aaa75d61908b3e0 Mon Sep 17 00:00:00 2001 From: Kevin Zhao Date: Fri, 13 Mar 2026 15:14:06 +0000 Subject: [PATCH] ci: harden lint workflows against credential exposure in fork PRs Split the single lint job into two separate jobs to follow the principle of least privilege: - remove-label: runs with write permissions but does NOT check out or execute any PR code - lint: checks out and executes PR code but only has read permissions Also add persist-credentials: false to the checkout step to prevent the GITHUB_TOKEN from being stored in git credentials where it could be read by subsequent steps. This follows the same pattern applied to googleapis/genai-toolbox in commits 949e8242 and 3f83c497. Reference: https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/ --- .github/workflows/lint-toolbox-adk.yaml | 31 +++++++++++-------- .github/workflows/lint-toolbox-core.yaml | 31 +++++++++++-------- .github/workflows/lint-toolbox-langchain.yaml | 31 +++++++++++-------- .../workflows/lint-toolbox-llamaindex.yaml | 31 +++++++++++-------- 4 files changed, 72 insertions(+), 52 deletions(-) diff --git a/.github/workflows/lint-toolbox-adk.yaml b/.github/workflows/lint-toolbox-adk.yaml index 6bb8e3bc1..508875a77 100644 --- a/.github/workflows/lint-toolbox-adk.yaml +++ b/.github/workflows/lint-toolbox-adk.yaml @@ -25,23 +25,14 @@ on: permissions: read-all jobs: - lint: - if: "${{ github.event.action != 'labeled' || github.event.label.name == 'tests: run' }}" - name: lint + remove-label: + if: "${{ github.event.action == 'labeled' && github.event.label.name == 'tests: run' }}" runs-on: ubuntu-latest - concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - defaults: - run: - working-directory: ./packages/toolbox-adk permissions: - contents: 'read' issues: 'write' pull-requests: 'write' steps: - name: Remove PR Label - if: "${{ github.event.action == 'labeled' && github.event.label.name == 'tests: run' }}" uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} @@ -56,12 +47,26 @@ jobs: } catch (e) { console.log('Failed to remove label. Another job may have already removed it!'); } + + lint: + if: "${{ github.event.action != 'labeled' || github.event.label.name == 'tests: run' }}" + name: lint + runs-on: ubuntu-latest + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + defaults: + run: + working-directory: ./packages/toolbox-adk + permissions: + contents: 'read' + steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ github.event.pull_request.head.sha }} repository: ${{ github.event.pull_request.head.repo.full_name }} - token: ${{ secrets.GITHUB_TOKEN }} + persist-credentials: false - name: Setup Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: @@ -81,4 +86,4 @@ jobs: - name: Run type-check env: MYPYPATH: './src' - run: mypy --install-types --non-interactive --cache-dir=.mypy_cache/ -p toolbox_adk \ No newline at end of file + run: mypy --install-types --non-interactive --cache-dir=.mypy_cache/ -p toolbox_adk diff --git a/.github/workflows/lint-toolbox-core.yaml b/.github/workflows/lint-toolbox-core.yaml index 05994176b..18c3a9de0 100644 --- a/.github/workflows/lint-toolbox-core.yaml +++ b/.github/workflows/lint-toolbox-core.yaml @@ -25,23 +25,14 @@ on: permissions: read-all jobs: - lint: - if: "${{ github.event.action != 'labeled' || github.event.label.name == 'tests: run' }}" - name: lint + remove-label: + if: "${{ github.event.action == 'labeled' && github.event.label.name == 'tests: run' }}" runs-on: ubuntu-latest - concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - defaults: - run: - working-directory: ./packages/toolbox-core permissions: - contents: 'read' issues: 'write' pull-requests: 'write' steps: - name: Remove PR Label - if: "${{ github.event.action == 'labeled' && github.event.label.name == 'tests: run' }}" uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} @@ -56,12 +47,26 @@ jobs: } catch (e) { console.log('Failed to remove label. Another job may have already removed it!'); } + + lint: + if: "${{ github.event.action != 'labeled' || github.event.label.name == 'tests: run' }}" + name: lint + runs-on: ubuntu-latest + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + defaults: + run: + working-directory: ./packages/toolbox-core + permissions: + contents: 'read' + steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ github.event.pull_request.head.sha }} repository: ${{ github.event.pull_request.head.repo.full_name }} - token: ${{ secrets.GITHUB_TOKEN }} + persist-credentials: false - name: Setup Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: @@ -81,4 +86,4 @@ jobs: - name: Run type-check env: MYPYPATH: './src' - run: mypy --install-types --non-interactive --cache-dir=.mypy_cache/ -p toolbox_core \ No newline at end of file + run: mypy --install-types --non-interactive --cache-dir=.mypy_cache/ -p toolbox_core diff --git a/.github/workflows/lint-toolbox-langchain.yaml b/.github/workflows/lint-toolbox-langchain.yaml index 79d46adaf..c80b43e0d 100644 --- a/.github/workflows/lint-toolbox-langchain.yaml +++ b/.github/workflows/lint-toolbox-langchain.yaml @@ -25,23 +25,14 @@ on: permissions: read-all jobs: - lint: - if: "${{ github.event.action != 'labeled' || github.event.label.name == 'tests: run' }}" - name: lint + remove-label: + if: "${{ github.event.action == 'labeled' && github.event.label.name == 'tests: run' }}" runs-on: ubuntu-latest - concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - defaults: - run: - working-directory: ./packages/toolbox-langchain permissions: - contents: 'read' issues: 'write' pull-requests: 'write' steps: - name: Remove PR Label - if: "${{ github.event.action == 'labeled' && github.event.label.name == 'tests: run' }}" uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} @@ -56,12 +47,26 @@ jobs: } catch (e) { console.log('Failed to remove label. Another job may have already removed it!'); } + + lint: + if: "${{ github.event.action != 'labeled' || github.event.label.name == 'tests: run' }}" + name: lint + runs-on: ubuntu-latest + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + defaults: + run: + working-directory: ./packages/toolbox-langchain + permissions: + contents: 'read' + steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ github.event.pull_request.head.sha }} repository: ${{ github.event.pull_request.head.repo.full_name }} - token: ${{ secrets.GITHUB_TOKEN }} + persist-credentials: false - name: Setup Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: @@ -81,4 +86,4 @@ jobs: - name: Run type-check env: MYPYPATH: './src' - run: mypy --install-types --non-interactive --cache-dir=.mypy_cache/ -p toolbox_langchain \ No newline at end of file + run: mypy --install-types --non-interactive --cache-dir=.mypy_cache/ -p toolbox_langchain diff --git a/.github/workflows/lint-toolbox-llamaindex.yaml b/.github/workflows/lint-toolbox-llamaindex.yaml index c6f12ea56..c4bbe8f92 100644 --- a/.github/workflows/lint-toolbox-llamaindex.yaml +++ b/.github/workflows/lint-toolbox-llamaindex.yaml @@ -25,23 +25,14 @@ on: permissions: read-all jobs: - lint: - if: "${{ github.event.action != 'labeled' || github.event.label.name == 'tests: run' }}" - name: lint + remove-label: + if: "${{ github.event.action == 'labeled' && github.event.label.name == 'tests: run' }}" runs-on: ubuntu-latest - concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - defaults: - run: - working-directory: ./packages/toolbox-llamaindex permissions: - contents: 'read' issues: 'write' pull-requests: 'write' steps: - name: Remove PR Label - if: "${{ github.event.action == 'labeled' && github.event.label.name == 'tests: run' }}" uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} @@ -56,12 +47,26 @@ jobs: } catch (e) { console.log('Failed to remove label. Another job may have already removed it!'); } + + lint: + if: "${{ github.event.action != 'labeled' || github.event.label.name == 'tests: run' }}" + name: lint + runs-on: ubuntu-latest + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + defaults: + run: + working-directory: ./packages/toolbox-llamaindex + permissions: + contents: 'read' + steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ github.event.pull_request.head.sha }} repository: ${{ github.event.pull_request.head.repo.full_name }} - token: ${{ secrets.GITHUB_TOKEN }} + persist-credentials: false - name: Setup Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: @@ -81,4 +86,4 @@ jobs: - name: Run type-check env: MYPYPATH: './src' - run: mypy --install-types --non-interactive --cache-dir=.mypy_cache/ -p toolbox_llamaindex \ No newline at end of file + run: mypy --install-types --non-interactive --cache-dir=.mypy_cache/ -p toolbox_llamaindex