Mathieu Larose

GitHub Commit Injection

October 2020

For those using GitHub Actions, how safe do you think it is to run the step actions/checkout@637523b962b8d2900f19acb38983d7243f64ba5c in your workflow? Example:

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@637523b962b8d2900f19acb38983d7243f64ba5c

Let's see:

  1. The step uses the official checkout action which has the "Verified creator" badge.

  2. The step specifies the full length commit SHA as recommended by GitHub:

    Although pinning to a commit SHA is the most secure option, specifying a tag is more convenient and is widely used. If you’d like to specify a tag, then be sure that you trust the action's creators. The ‘Verified creator’ badge on GitHub Marketplace is a useful signal, as it indicates that the action was written by a team whose identity has been verified by GitHub.

  3. You can browse the version in the official repo.

So, is 637523b962b8d2900f19acb38983d7243f64ba5c safe to use?

Well, no. Despite all appearances, that version has not been approved by its owners. It's not even in the official git repo. But, I was able to inject it to actions/checkout anyway. The code snippet above does work and would execute my version of actions/checkout if run.

If you look closely, I modified the second to last commit so the action displays a message at startup, but it could have been a backdoor to steal your source code or secrets.

How did I injected a version into the official checkout action? Easy:

Step 1: Fork the official repo to your account. Example: larose/checkout.

Step 2: Push a commit to your forked repo. Example: larose/checkout/tree/commit-injection-demo.

Any commits pushed to your forked repo is now available for use as a version for actions/checkout in any GitHub repos. The reason is that commits in the parent repo and its forks are stored together in the same commit pool. Contrary to branches and tags which are stored separately for each fork.

I reached out to GitHub about this issue and they replied that:

This is an intentional design decision and is working as expected. We may make this functionality more strict in the future, but don't have anything to announce right now.

So until then, be careful when pinning a third-party action to a full length commit SHA. Always check that it belongs to the main branch of the official action repo.

Also, an alternate approach could be to copy (not fork) the repo of a third-party action, audit its source code and then pin it to the full length commit SHA.

Happy coding!

Like this article? Get notified of new ones: