Yann Pellegrini

Github actions credentials security without breaking your workflows

In the light of the recent Tanstack supply-chain attack, pipeline security has become the priority of several projects.

Mistakes in github actions are tricky to see and can have devastating consequences, through PR-controlled variable injection 1.

Luckily there are powerful tools to catch them:

One common suggestion from Zizmor is to set persist-credentials: false when using actions/checkout 2

The problem: persist-credentials: false + https git commands = boom

If you combine these two ingredients you will be greeted with the error

fatal: could not read Username for 'https://github.com/': No such device or address

Workaround 1: Let gh provide credentials

1 - name: Commit and push
2+   env:
3+     GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4   run: |
5+    gh auth setup-git
6     git commit -m "your message"
7     git push

gh auth setup-git configures git to fetch credentials from the gh command, which in turn reads the token from GH_TOKEN.

This avoids storing it in .git/config (which is what persist-credentials: false protects us from).

⚠️
If you self-host your runner, or use a job with a custom container:, you need to install and set up the gh cli yourself. https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-github-cli

Workaround 2: Inject the token into the remote URL (and clean it after)

 1 - name: Commit and push
 2   env:
 3     GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 4   run: |
 5     git commit -m "your message"
 6+    git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}"
 7     git push
 8
 9+    # Embedding the token in the remote URL writes it to `.git/config`, 
10+    # so we reset the URL after pushing.
11+    git remote set-url origin "https://github.com/${{ github.repository }}"
⚠️
If git push fails, the credentials stay in .git/config. This is an unlikely attack vector but workaround 1 is safer.