Skip to content

MT-22022: Add webhook signature verification helper#110

Open
Rabsztok wants to merge 3 commits into
mainfrom
MT-22022-webhook-signature-verification
Open

MT-22022: Add webhook signature verification helper#110
Rabsztok wants to merge 3 commits into
mainfrom
MT-22022-webhook-signature-verification

Conversation

@Rabsztok
Copy link
Copy Markdown

@Rabsztok Rabsztok commented May 20, 2026

Motivation

Follow-up to mailtrap-ruby#108 review comment: expose a helper so users don't have to re-implement Mailtrap's HMAC-SHA256 webhook signature check on every receiver.

Changes

  • Mailtrap::Webhooks.verify_signature(payload:, signature:, signing_secret:)true/false. HMAC-SHA256 over the raw body, constant-time compare via OpenSSL.fixed_length_secure_compare. Returns false (never raises) on empty / wrong-length / non-hex / non-string inputs.
  • spec/mailtrap/webhooks_spec.rb pins a cross-SDK fixture (payload + signing_secret + expected digest) shared verbatim across all six official Mailtrap SDKs to guarantee byte-for-byte parity.
  • examples/webhooks_signature_verification.rb — runnable usage snippet.
  • README — new "Verifying webhook signatures" subsection.

How to test

CI runs the full spec suite and lint. Manually:

bundle exec ruby examples/webhooks_signature_verification.rb

The script should exit 0 with no output.

Companion PRs

Coordinated rollout across all six official SDKs (same algorithm, same shared fixture):

Summary by CodeRabbit

  • New Features

    • Added webhook signature verification capability to validate the authenticity and integrity of incoming webhooks from Mailtrap.
  • Documentation

    • Updated README with webhook signature verification example and improved formatting throughout development and compatibility sections.
  • Tests

    • Added comprehensive test suite to verify webhook signature validation handles valid payloads and rejects invalid cases.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4d84fdb9-88ca-4c98-a488-500182dac8ae

📥 Commits

Reviewing files that changed from the base of the PR and between db3db34 and 44da3ff.

📒 Files selected for processing (2)
  • README.md
  • examples/webhooks_signature_verification.ru
✅ Files skipped from review due to trivial changes (1)
  • README.md

📝 Walkthrough

Walkthrough

This PR introduces webhook signature verification for the Mailtrap Ruby gem via HMAC-SHA256 validation, adding the Mailtrap::Webhooks.verify_signature method, test suite, working Rack example, and documentation.

Changes

Webhook Signature Verification

Layer / File(s) Summary
Webhook signature verification module and gem loading
lib/mailtrap/webhooks.rb, lib/mailtrap.rb
Mailtrap::Webhooks module defines SIGNATURE_HEX_LENGTH and implements verify_signature using HMAC-SHA256 hexdigest with constant-time comparison; loaded from gem entrypoint via require_relative.
Signature verification test suite
spec/mailtrap/webhooks_spec.rb
RSpec suite defines cross-SDK fixture (payload, signing_secret, expected_signature) and validates verify_signature returns true for valid combinations and false for wrong secret, tampered payload, invalid length, non-hex content, empty inputs, and includes cross-SDK fixture integrity check.
Rack example and README updates
examples/webhooks_signature_verification.ru, README.md
Adds executable Rack app demonstrating environment-based secret loading, POST filtering, raw body extraction, and signature verification with 401/200 responses. README adds "Verifying webhook signatures" example link, adjusts "Sending Domains API" formatting, rewraps development section, and removes trailing whitespace.

Sequence Diagram

sequenceDiagram
  participant Receiver as Webhook Receiver
  participant Verify as Mailtrap::Webhooks
  participant OpenSSL as OpenSSL::HMAC
  participant Compare as OpenSSL.fixed_length_secure_compare
  Receiver->>Verify: verify_signature(payload, signature, signing_secret)
  alt Invalid inputs
    Verify-->>Receiver: false
  else Valid inputs
    Verify->>OpenSSL: hexdigest(signing_secret, payload)
    OpenSSL-->>Verify: expected_hex_digest
    Verify->>Compare: fixed_length_secure_compare(expected_hex_digest, signature)
    Compare-->>Verify: true or false
    Verify-->>Receiver: boolean result
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • IgorDobryn
  • mklocek
  • DagonWat

Poem

🐰 A webhook hops in, signed with secrets so keen,
HMAC-SHA256, the clearest we've seen,
Constant-time compare keeps timing attacks at bay,
Cross-SDK fixtures guard truth every day,
Ruby gems shine bright with verification's play! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a webhook signature verification helper to the Mailtrap gem.
Description check ✅ Passed The description is comprehensive and covers motivation, detailed changes, testing instructions, and companion PRs, though it deviates from the template structure.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch MT-22022-webhook-signature-verification

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/webhooks_signature_verification.rb`:
- Around line 1-2: Add explicit requires for Rack and JSON at the top of the
example: include require 'rack' and require 'json' alongside the existing
require 'mailtrap' so that Rack::Request and JSON.parse are defined when running
the standalone script (this affects the usage sites of Rack::Request and
JSON.parse in the file).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f503201f-688f-4346-89a4-536e35b0c132

📥 Commits

Reviewing files that changed from the base of the PR and between 1102ed9 and 2c0513e.

📒 Files selected for processing (5)
  • README.md
  • examples/webhooks_signature_verification.rb
  • lib/mailtrap.rb
  • lib/mailtrap/webhooks.rb
  • spec/mailtrap/webhooks_spec.rb

Comment thread examples/webhooks_signature_verification.rb Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
spec/mailtrap/webhooks_spec.rb (1)

24-153: ⚡ Quick win

Add explicit non-string input cases to pin the public contract.

The suite verifies many malformed values, but it does not currently assert the documented false behavior for non-string inputs (nil, numeric, etc.).

✅ Suggested test addition
   describe '.verify_signature' do
@@
     context 'with an empty payload but a non-empty signature' do
       it 'returns false' do
@@
       end
     end
+
+    context 'with non-string inputs' do
+      it 'returns false without raising' do
+        expect(
+          described_class.verify_signature(
+            payload: 123,
+            signature: fixture_expected_signature,
+            signing_secret: fixture_signing_secret
+          )
+        ).to be false
+
+        expect(
+          described_class.verify_signature(
+            payload: fixture_payload,
+            signature: nil,
+            signing_secret: fixture_signing_secret
+          )
+        ).to be false
+      end
+    end
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@spec/mailtrap/webhooks_spec.rb` around lines 24 - 153, Add tests exercising
non-string inputs for the public contract of verify_signature: create a new
context(s) that calls described_class.verify_signature with payload and/or
signing_secret set to nil, numeric (e.g. 12345), and a non-string type like an
Array, using fixture_expected_signature (and also an empty or nil signature) and
assert the method returns false (and does not raise). Reference the method
verify_signature and the constant SIGNATURE_HEX_LENGTH only to keep behavior
consistent with existing malformed-signature tests; ensure each case mirrors the
pattern used in other contexts (call verify_signature then expect(result).to be
false).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/webhooks_signature_verification.rb`:
- Around line 5-6: The hard-coded hex-like secret assigned to signing_secret
looks like a real credential and can trigger secret scanners; replace that
literal with an explicit non-secret placeholder (e.g.,
'your_signing_secret_here' or 'REPLACE_WITH_SIGNING_SECRET') and update any
accompanying comment to indicate it must be set from a safe source (env var or
secret manager) before computing signature with OpenSSL::HMAC.hexdigest using
signing_secret and payload (refer to signing_secret, signature, payload).

---

Nitpick comments:
In `@spec/mailtrap/webhooks_spec.rb`:
- Around line 24-153: Add tests exercising non-string inputs for the public
contract of verify_signature: create a new context(s) that calls
described_class.verify_signature with payload and/or signing_secret set to nil,
numeric (e.g. 12345), and a non-string type like an Array, using
fixture_expected_signature (and also an empty or nil signature) and assert the
method returns false (and does not raise). Reference the method verify_signature
and the constant SIGNATURE_HEX_LENGTH only to keep behavior consistent with
existing malformed-signature tests; ensure each case mirrors the pattern used in
other contexts (call verify_signature then expect(result).to be false).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fa6cb163-7069-448c-a84c-5202f9998a3e

📥 Commits

Reviewing files that changed from the base of the PR and between 2c0513e and db3db34.

📒 Files selected for processing (5)
  • README.md
  • examples/webhooks_signature_verification.rb
  • lib/mailtrap.rb
  • lib/mailtrap/webhooks.rb
  • spec/mailtrap/webhooks_spec.rb
✅ Files skipped from review due to trivial changes (1)
  • lib/mailtrap.rb

Comment thread examples/webhooks_signature_verification.rb Outdated
@Rabsztok Rabsztok requested review from IgorDobryn and mklocek May 21, 2026 08:30
Comment thread README.md Outdated
→ _Sending Domains_ → _Your domain_ → _SMTP/API Settings_ to find the SMTP
configuration example.

### Verifying webhook signatures
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as I commented in .NET SDK PR - I don't think it's such a core functionality that it must be mentioned in general README

Comment thread examples/webhooks_signature_verification.rb Outdated
Copy link
Copy Markdown
Contributor

@IgorDobryn IgorDobryn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments above makes sense

@Rabsztok Rabsztok requested a review from mklocek May 26, 2026 05:23
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.

3 participants