feat: Add HMAC-SHA256 webhook signature validation for Slack#588
feat: Add HMAC-SHA256 webhook signature validation for Slack#588nickpismenkov merged 3 commits intomainfrom
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly improves the security posture for Slack webhooks by introducing HMAC-SHA256 signature validation. This mechanism ensures that all incoming Slack webhook requests are legitimate and have not been tampered with, by verifying their cryptographic signature against a pre-configured secret. The implementation is flexible, allowing secrets to be defined per channel via capabilities, and is thoroughly tested to handle various validation scenarios. Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces HMAC-SHA256 signature validation for Slack webhooks, enhancing security. The changes include adding the hmac dependency, updating channel capabilities to specify the signing secret, and implementing the verification logic in the WASM channel router. The implementation is well-tested. My review includes one suggestion to refactor duplicated code in src/extensions/manager.rs for improved performance and maintainability.
Note: Security Review is unavailable for this PR.
There was a problem hiding this comment.
Pull request overview
This PR adds HMAC-SHA256 webhook signature validation for Slack channels, complementing the existing Ed25519 signature verification for Discord. The implementation follows Slack's documented signing protocol (v0 versioned HMAC-SHA256 with timestamp-based replay protection) and integrates it consistently into the existing WASM channel webhook routing infrastructure.
Changes:
- Added
verify_slack_signaturefunction with HMAC-SHA256 computation, staleness checking, and constant-time comparison, alongside comprehensive unit tests. - Extended the WASM channel router, schema, loader, and capabilities to support declaring and registering HMAC signing secrets (
hmac_secret_name) for channels. - Refactored the
refresh_active_channelmethod inExtensionManagerto load the capabilities file once instead of reading it from disk multiple times.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/channels/wasm/signature.rs |
Added verify_slack_signature function and extensive test coverage for HMAC-SHA256 verification |
src/channels/wasm/router.rs |
Added hmac_secrets field, register/get methods, unregister cleanup, webhook handler HMAC verification block, and integration tests |
src/channels/wasm/schema.rs |
Added hmac_secret_name field to WebhookSchema and accessor on ChannelCapabilitiesFile |
src/channels/wasm/loader.rs |
Added hmac_secret_name() accessor on LoadedChannel |
src/main.rs |
Register HMAC secret during channel setup if declared in capabilities |
src/extensions/manager.rs |
Register/refresh HMAC secret during hot-activation and refresh flows; refactored capabilities file loading to a single read |
channels-src/slack/slack.capabilities.json |
Declared hmac_secret_name: "slack_signing_secret" in Slack's webhook config |
Cargo.toml / Cargo.lock |
Added explicit hmac = "0.12" dependency |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| assert!( | ||
| !verify_slack_signature(signing_secret, "-1", body, "v0=abc123", 0), |
There was a problem hiding this comment.
This test doesn't actually verify that negative timestamps are rejected. With now_secs = 0 and timestamp = "-1", the staleness check passes because (0 - (-1)).abs() = 1 ≤ 300. The test only passes because the signature "v0=abc123" doesn't match the computed HMAC — not because the negative timestamp was detected.
Compare with the analogous Discord test at line 387, which uses now_secs = TEST_TS = 1234567890, making the staleness check properly reject the -1 timestamp.
To properly test negative timestamp rejection, either use a now_secs value far from -1 (similar to the Discord test), or add explicit negative-timestamp rejection logic in verify_slack_signature.
| assert!( | |
| !verify_slack_signature(signing_secret, "-1", body, "v0=abc123", 0), | |
| let timestamp = "-1"; | |
| // Use a correctly signed message so that the failure is due to the timestamp, | |
| // not because the signature is invalid. | |
| let signature = sign_slack_message(signing_secret, timestamp, body); | |
| assert!( | |
| !verify_slack_signature(signing_secret, timestamp, body, &signature, SLACK_TEST_TS), |
| pub fn hmac_secret_name(&self) -> Option<&str> { | ||
| self.capabilities | ||
| .channel | ||
| .as_ref() | ||
| .and_then(|c| c.webhook.as_ref()) | ||
| .and_then(|w| w.hmac_secret_name.as_deref()) | ||
| } |
There was a problem hiding this comment.
The existing schema tests include thorough coverage for the signature_key_secret_name field (see test_webhook_schema_signature_key_secret_name, test_signature_key_secret_name_none_when_missing, and test_discord_capabilities_signature_key at lines 738-783). There are no corresponding tests for the new hmac_secret_name field. Consider adding:
- A test that parses
hmac_secret_namefrom capabilities JSON and verifies the accessor returns the expected value. - A test that verifies
hmac_secret_namereturnsNonewhen the field is absent. - A test that verifies the actual
slack.capabilities.jsondeclareshmac_secret_name: "slack_signing_secret"(similar totest_discord_capabilities_signature_keyon line 774).
No description provided.