From 95e6f9acddddeed0efeb60c8f4f36b239cb02221 Mon Sep 17 00:00:00 2001 From: Tal Zaccai Date: Tue, 2 Jun 2026 12:32:43 -0700 Subject: [PATCH] ci(security): push with default GITHUB_TOKEN, not App token MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fix-dependabot-alerts workflow was using the GitHub App token for git push, which required granting the App `Contents: write` at the installation level. That permission wasn't (and didn't need to be) granted, causing the scheduled run to 403 at `git push` with 'Permission to microsoft/TypeChat.git denied to typeagent-bot[bot]'. Match the TypeAgent workflow pattern instead: keep `persist-credentials: false` on checkout (so the token isn't reachable from untrusted `npm` scripts during the verify phase), but at the very end of the job re-inject the workflow's own GITHUB_TOKEN — already scoped to `contents: write` via the workflow-level `permissions:` block — for the git push. The App token is now used only where it must be: - `gh api dependabot/alerts` (the default GITHUB_TOKEN can't reach this endpoint) - `gh pr create` / labels / closing superseded PRs (so the PR identity is the bot, not github-actions) Verified end-to-end: a manual workflow_dispatch run against a temp branch passed git push and opened #360. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/fix-dependabot-alerts.yml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/fix-dependabot-alerts.yml b/.github/workflows/fix-dependabot-alerts.yml index 9decf913..ade460f4 100644 --- a/.github/workflows/fix-dependabot-alerts.yml +++ b/.github/workflows/fix-dependabot-alerts.yml @@ -162,7 +162,17 @@ jobs: - name: Create pull request if: ${{ steps.fix.outputs.changes == 'true' && steps.build.outputs.build_ok == 'true' }} env: + # GH_TOKEN is the App token — used by the ``gh`` CLI for + # ``gh pr create`` / labelling / closing superseded PRs so the + # PR appears under the bot's identity. GH_TOKEN: ${{ steps.app-token-pr.outputs.token }} + # GIT_PUSH_TOKEN is the workflow's default GITHUB_TOKEN, scoped + # via the workflow-level ``permissions: contents: write`` block. + # We use it only at the very end, after all untrusted ``npm`` + # scripts have finished running, to avoid persisting any push + # credential in .git/config (where dependency build scripts + # could read it). + GIT_PUSH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | BRANCH="automated/fix-dependabot-alerts-$(date +%Y%m%d)-${{ github.run_number }}" git config user.name "github-actions[bot]" @@ -188,10 +198,12 @@ jobs: Unfixable: ${{ steps.fix.outputs.unfixable_count }} package(s) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>" - # Push using the App token explicitly — we disabled - # ``persist-credentials`` on checkout, so .git/config has no - # creds to fall back on. - git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git" + # Push using the workflow's default GITHUB_TOKEN (scoped to + # contents:write at the workflow level). Configured here, not + # via actions/checkout's ``persist-credentials``, so the token + # isn't reachable from the npm install / build / test phase + # earlier in the job. + git remote set-url origin "https://x-access-token:${GIT_PUSH_TOKEN}@github.com/${{ github.repository }}.git" git push origin "$BRANCH" APPLIED="${{ steps.fix.outputs.applied_packages }}"