Coordinated Disclosure Timeline
- 2021-02-04: Issue reported to maintainers
- 2021-02-04: Report acknowledged
- 2021-02-12: Issue fixed
Summary
The zwave-js-bot.yml GitHub workflow is vulnerable to unauthorized modification of the base repository or secrets exfiltration.
Product
zwave-js/zwavejs2mqtt repository
Tested Version
The latest changeset of zwave-js-bot.yml to the date.
Details
Issue 1: A specific comment triggers a potentially untrusted pull request build in a privileged environment
When a user comments on a pull request with @zwave-js-bot fix lint
it triggers the following workflow which checks out the pull request and builds the potentially untrusted code:
on:
issue_comment:
types: [created] # edited, deleted
...
# Fix lint errors when an authorized person posts "@zwave-js-bot fix lint"
fix-lint:
if: |
contains(github.event.issue.html_url, '/pull/') &&
contains(github.event.comment.body, '@zwave-js-bot fix lint') &&
(github.event.comment.user.login != 'zwave-js-bot') && github.event.comment.user.login != 'zwave-js-assistant[bot]'
...
- name: Check user's permissions to do this
id: check
uses: actions/github-script@v3
with:
github-token: ${{secrets.BOT_TOKEN}}
result-encoding: string
script: |
const bot = require(`${process.env.GITHUB_WORKSPACE}/.github/bot-scripts/index.js`);
return await bot.checkAuthorized({github, context});
...
- name: Checkout pull request
if: steps.check.outputs.result == 'true'
uses: actions/checkout@v2
with:
token: ${{secrets.BOT_TOKEN}}
repository: ${{ fromJSON(steps.get-pr.outputs.result).head.repo.full_name }}
ref: ${{ fromJSON(steps.get-pr.outputs.result).head.ref }}
...
- name: Install dependencies
if: steps.check.outputs.result == 'true'
run: npm install
The checkAuthorized
script verifies that the user is authorized to trigger the workflow. However it also allows the pull request author to trigger the workflow:
if (context.payload.issue.html_url.includes("/pull/")) {
console.log("Comment appears in a PR, retrieving PR info...");
// Only the pull request author and authorized users may execute this command
const { data: pull } = await github.pulls.get({
...options,
pull_number: context.payload.issue.number,
});
const allowed = [...authorizedUsers, pull.user.login];
const commenting = context.payload.comment.user.login;
console.log(`Authorized users: ${allowed.join(", ")}`);
console.log(`Commenting user: ${commenting}`);
const isAuthorized = allowed.includes(commenting);
console.log(`Is authorized: ${isAuthorized}`);
if (!isAuthorized) return false;
} else {
// In issues, only the authorized users may execute any commands
Impact
The triggered workflow has access to the write repository token and secrets. The vulnerability allows for unauthorized modification of the base repository and secrets exfiltration.
Issue 2: A branch name and title from pull request are used to format inline script
on:
issue_comment:
types: [created] # edited, deleted
...
- name: Rebase the branch
if: steps.check-permissions.outputs.result == 'true'
id: fix
run: |
# Try to rebase
if git rebase "${{ fromJSON(steps.get-pr.outputs.result).base.ref }}"" ; then
...
- name: Rebase the branch
if: steps.check-permissions.outputs.result == 'true'
id: fix
run: |
# Try to reword the commit
git config user.email "bot@zwave.js"
git config user.name "Z-Wave JS Bot"
if git commit --amend -m "${{ fromJSON(steps.get-pr.outputs.result).title }}" ; then
The expression evaluation is vulnerable to inline script injection.
Impact
The triggered workflow has access to the write repository token and secrets. The vulnerability allows for unauthorized modification of the base repository and secrets exfiltration.
Credit
This issue was discovered and reported by GHSL team member @JarLob (Jaroslav Lobačevski).
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2021-047
in any communication regarding this issue.