article
Stay Secure with GitHub Actions
Actions are awesome, they allow you to easily extend your GitHub Actions workflow with new functionality, without the need to reinvent the wheel.
The flexibility and offering of the marketplace with official
, verified
and 3rd party
actions is great, but sadly the wrong security of the marketplace has a high risk associated with it.
There's a huge amount of actions you can use to build the project, but only a small amount is from GitHub officially, or from verified sources. 3rd party actions should be reviewed so they are secure.
GitHub Actions Introduction
Risk
But what is the risk of using potentially compromised or insecure actions?
- Source code being stolen / leaked
- Result artifacts being exposed
- Keys or credentials being stolen / exposed
- Code getting injected into the runner / resulting app
- Secrets being available to non trusted entities
Stay secure
GitHub offers different options to allow repo administrators to enforce strict rules on which actions can be used within a given repository.
Beside forcing only specific actions being allowed for builds, it's also possible to prevent workflows to be executed for PRs from forks and even more specifically to prevent fork pull request to retrieve write tokens or secrets.

Generally official
and verified
actions can be considered to be ok (please be aware that it is never wrong to also review the code of these plugins, if a high risk system is involved).
Protect Secrets
The easiest step to ensure secrets are only available to trusted parts of your build, is by having them specified for the smallest scope possible.
Prefer specifying secrets only to the steps they are needed for, instead of globally. The general rule of thumb for scoping secrets is:
step > job > workflow
Fork PRs
One risk with pull requests from forks is, that 3rd party entities can fully modify the workflows and introduce risky actions or extract secrets from the repository. As such, if you can't trust all entities having access to the repository and to forks, do prevent these PRs from running workflows.
3rd party actions
Beside untrusted entities potentially getting access and extracting secrets from a repository, the biggest risk are 3rd party
Actions which may contain insecure or malicious code.
The most secure would be to simply prevent any 3rd party action from being used and executed, which sadly impacts the flexibility and power of GitHub Actions. Resulting in significant overhead in having all actions in house.
If regardless of the potential risk involved with 3rd party action, you want to use them, make sure to follow these steps of reviewing the source:
- Review the code inside the Actions repository
- Verify the compiled code executed by the action
- Verify the
action.yml
executing the expected code - (if docker action) Verify the
Dockerfile
and the executed steps - Use specific commits instead of versions
Example
To showcase the above steps let's have a look at the styfle/cancel-workflow-action
action. (This action is one of my personal favourites as it will ensure to cancel jobs if they get to be obsolete by new pushed commits on a PR)
Review the code
The first step is to review the code. Luckily actions are required to be on GitHub as such we can directly review it's code:
https://github.com/styfle/cancel-workflow-action/blob/main/src/index.ts
Looking into it, no malicious sections can be found. The plugin uses the github core dependency to retrieve the workflows and cancel a running workflow if applicable.
Verify compiled code
Usually actions will also have their compiled source available in the dist
folder, this will be the piece which is ultimately executed by GitHub actions.
https://github.com/styfle/cancel-workflow-action/blob/main/dist/index.js
It's more difficult to check this, generally it helps to compile the action manually and then diff the resulting files to identify any suspicious sections.
Verify action.yml
The entry point for GitHub action to run the specified action is the action.yml
so it's important that we verify, it actually executing the source as highlighted above (and not do any other malicious steps)
https://github.com/styfle/cancel-workflow-action/blob/main/action.yml#L16
As we can see, it will actually just execute the dist/index.js
using node
runs:
using: 'node12'
main: 'dist/index.js'
Verify Dockerfile
The used example is a normal action, and does not consist out of a docker container. Just to showcase, here's a small example of an action using a docker container:
https://github.com/EnricoMi/publish-unit-test-result-action/blob/master/action.yml#L25-L27
FROM python:3.6-slim
...
COPY requirements.txt /action/
RUN pip install --upgrade --force --no-cache-dir pip && pip install --upgrade --force --no-cache-dir -r /action/requirements.txt
COPY githubext /action/githubext
COPY publish_unit_test_results.py /action/
ENTRYPOINT ["python", "/action/publish_unit_test_results.py"]
https://github.com/EnricoMi/publish-unit-test-result-action/blob/master/Dockerfile
As this Dockerfile shows, it will use the python
container, and then copy various files into the container, and use python
to execute the python
file including the logic for that action
Use specific commit
After verifying that the action contains no malicious code and is considered save to be used, the last suggestion is to not use labeled versions to include the action in the workflow.
# dangerous
styfle/[email protected]
0.5.0
references to the tag with the same name which is associated with a specific commit (we just verified to be secure).
The problem with this is, that tags can be changed, and associated with a different commit. This would give malicious parties the potential to modify the plugins source to execute malicious code, and then change the tag to point to this code, at any time. This is a high risk as even if we reviewed the action, it could at any point suddenly do something else.
To prevent this find the associated commit hash from the tag and specify the action using the hash in your workfow.
styfle/cancel-workflow-action@148d9a848c6acaf90a3ec30bc5062f646f8a4163
Now your builds will use this specific state of the action, and it's not possible anymore to modify that state, as every commit will result in a different hash.
Conclusion
GitHub actions can be dangerous if used without care, but with a few small steps you can protect your code and your project.
- Prevent external entities to get access to secrets
- Disallow 3rd party actions if feasible
- Limit the scope of secrets as close to where they are needed as possible