Notify #gh-wallet-eng when a new issue is opened - #972
Conversation
Posts to #gh-wallet-eng whenever an issue is opened. Fires on `opened` only, which the GitHub Slack app's `issues` subscription cannot express (it always covers opened + closed + reopened). Notifies for every new issue for now; the workflow comments document the `author_association` filter to add if the channel becomes too noisy. This repo had no Slack workflows previously, so the SLACK_WEBHOOK secret needs to resolve here — see the note in the workflow header. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Ka8rvrCJEKLroVskzayvo
A Slack-app incoming webhook is bound to the channel it was installed against and ignores the channel in the payload, so SLACK_CHANNEL alone cannot redirect traffic to #gh-wallet-eng. Point this workflow at its own SLACK_WEBHOOK_GH_ISSUES secret, holding a webhook bound to #gh-wallet-eng. This repo has no other Slack workflows, so this is the only Slack secret it needs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Ka8rvrCJEKLroVskzayvo
There was a problem hiding this comment.
Pull request overview
Adds issue-opened notifications for the #gh-wallet-eng Slack channel.
Changes:
- Adds an
issues: openedworkflow. - Formats issue details for Slack.
- Uses a repository webhook secret.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| SLACK_MESSAGE: | ||
| "<${{ github.event.issue.html_url }}|#${{ github.event.issue.number | ||
| }}: ${{ github.event.issue.title }}> — opened by ${{ | ||
| github.event.issue.user.login }}" |
There was a problem hiding this comment.
Valid finding — fixed in e417c70.
The step no longer builds a mrkdwn string at all. The title now goes into a Block Kit plain_text object, which Slack does not parse as mrkdwn, so link and mention syntax renders literally rather than being interpreted. The payload is built with jq --arg and handed over via payload-file-path, so untrusted text is never interpolated into YAML or JSON syntax either.
I kept the title rather than omitting it — it's the most useful part of the notification, and plain_text makes it safe to include structurally rather than by remembering to escape.
The plain_text title is deliberately not entity-escaped, since plain_text is not mrkdwn and < would render as the literal characters. The escaping is applied to the top-level text fallback instead, which is used for push notifications and is parsed — & first, so the entities introduced aren't re-escaped.
Verified against a hostile title (pwn> <!channel> <https://evil.example|Reset your Freighter key> & "quotes"): output is valid JSON, the fallback is correctly escaped, and the plain_text block carries the title verbatim.
Generated by Claude Code
| MSG_MINIMAL: true | ||
| SLACK_CHANNEL: gh-wallet-eng | ||
| SLACK_COLOR: "#70E1C8" | ||
| SLACK_ICON: https://github.com/stellar/freighter/blob/master/docs/static/images/logo.png?size=48 |
There was a problem hiding this comment.
Not applicable — the premise doesn't hold for this workspace, and the setting is gone regardless.
SLACK_USERNAME/SLACK_ICON demonstrably do take effect on the webhook these workflows use. A current release notification in #release posts as Freighter Administrative Assistant (B09QQ8J4CKU) with the configured identity, from the same rtCamp + SLACK_USERNAME setup this PR was copied from:
Freighter v5.44.0 has been submitted to app stores for review!
So the general rule about app-model incoming webhooks ignoring payload identity overrides isn't what's happening here. Worth noting it's also the same comment that introduced the app-model premise two threads earlier, without checking it.
That said, e417c70 replaces the rtCamp action with slackapi/slack-github-action, so SLACK_USERNAME/SLACK_ICON no longer exist in this file. Message identity now comes from the Slack app the webhook belongs to, which is the behaviour you described — so this resolves either way.
Generated by Claude Code
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Slack Notification | ||
| uses: rtCamp/action-slack-notify@07cbdbfd6c6190970778d8f98f11d073b2932aae #v2.3.3 |
There was a problem hiding this comment.
Confirmed and fixed in e417c70.
I checked action.yml at the pinned SHA. The framing is slightly off — it's using: composite, not a Docker action — but the substance is right: it shells out to docker://ghcr.io/rtcamp/action-slack-notify:v2.3.3, a mutable tag, so the SHA pin did not pin the code that actually executes.
Replaced with slackapi/slack-github-action, pinned to dcb1066f776dd043e64d0e8ba94ca15cc7e1875d (v4.0.0). That action is runs: using: node24 — a plain JavaScript action with no container indirection — so the SHA pin covers the executed code. It's also first-party Slack rather than a third party.
Worth recording for anyone reading this later: the same gap exists in the five rtCamp Slack steps in stellar/freighter. Those are triggered by our own release process rather than by untrusted input, so they're lower priority, but migrating them is a reasonable follow-up.
Generated by Claude Code
|
iOS Simulator preview build is ready: https://github.com/stellar/freighter-mobile/releases/tag/untagged-18a738158491912057aa (SDF collaborators only — install instructions in the release description) |
Addresses review feedback on the notification step. Issue titles are attacker-controlled, and the previous SLACK_MESSAGE put the title inside Slack's <url|label> syntax unescaped. A title could terminate the link and inject an arbitrary hyperlink into the channel. Switch to slackapi/slack-github-action and put the title in a Block Kit plain_text object, which Slack does not parse as mrkdwn, so link and mention syntax renders literally. The payload is built with `jq --arg` and passed as a file, so untrusted text is never interpolated into YAML or JSON syntax. The top-level fallback text, which IS parsed as mrkdwn, escapes &, < and > instead. This also avoids introducing a supply-chain gap to this repo: rtCamp's action is a composite that runs docker://ghcr.io/rtcamp/action-slack-notify at a mutable tag, so a SHA pin would not have pinned the executed code. slackapi's action is node24, so the SHA pin covers what actually runs. Drop the unused repository token permissions while here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Ka8rvrCJEKLroVskzayvo
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Ka8rvrCJEKLroVskzayvo
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Ka8rvrCJEKLroVskzayvo
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Ka8rvrCJEKLroVskzayvo
What
Adds
.github/workflows/notify-slack-new-issue.yml. Whenever an issue is opened in this repo, it posts a message to #gh-wallet-eng with the issue number, title, author, and a link.Triggers on
openedonly — not close, reopen, or edit — and notifies for every new issue regardless of who filed it. A companion workflow is added instellar/freighter(stellar/freighter#2951).Why
New issues should surface in Slack without anyone having to watch GitHub notifications.
This is a workflow rather than the GitHub Slack app's
/github subscribebecause that feature notifies on opened, closed, and reopened events with no filtering by event type or author. Under it, triaging an issue re-notifies the channel.How it works
slackapi/slack-github-action, pinned by commit SHA. It is a Node action, so the pin covers the code that executes.jq --argand passed viapayload-file-path, so no value is interpolated into YAML or JSON syntax.plain_textobject. Slack does not parse that as mrkdwn, so link and mention syntax in a title renders literally rather than taking effect. The fallback text, which is mrkdwn-parsed, escapes&,<, and>.permissions: {}— the job does not use the repository token.Requirements
A
SLACK_WEBHOOK_GH_ISSUESsecret on this repo, holding an incoming webhook bound to#gh-wallet-eng. This must exist before merge or the job fails. It is deliberately separate fromstellar/freighter'sSLACK_WEBHOOK, so a workflow driven by untrusted input does not share that webhook.Verification
The payload build and Slack delivery were exercised against the real secret on a branch build, using the same jq program and action pin as this workflow. The message posted to
#gh-wallet-engcorrectly, with markup in the title rendered as literal text rather than an active link.The
issues: [opened]trigger itself is not exercised until this reaches the default branch, since GitHub runs event-triggered workflows only from there.Limitations
Checklist
PR structure
Testing
Release