-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(brevo): make sure webhooks come from brevo (#2563)
* make sure webhook come from brevo * minor improvement * refacto brevo tests * fix linter * prevent flaky
- Loading branch information
1 parent
36a944d
commit 0b24fef
Showing
14 changed files
with
111 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
require "ipaddr" | ||
|
||
module Brevo::IpWhitelistConcern | ||
extend ActiveSupport::Concern | ||
|
||
# IP list comes from | ||
# https://help.brevo.com/hc/en-us/articles/15127404548498-Brevo-IP-ranges-List-of-publicly-exposed-services#h_01HENC062K8KJKJE7BJNYMPM77 | ||
IP_WHITELIST_RANGE = "1.179.112.0/20".freeze | ||
|
||
included do | ||
before_action :ensure_ip_comes_from_brevo_ips | ||
end | ||
|
||
private | ||
|
||
def ensure_ip_comes_from_brevo_ips | ||
# In case Brevo decides to use some other IP range without notice | ||
# we need a quick way to skip this check | ||
return if ENV["DISABLE_BREVO_IP_WHITELIST"].present? | ||
|
||
return if IPAddr.new(IP_WHITELIST_RANGE).include?(request.remote_ip) | ||
|
||
Sentry.capture_message("Brevo Webhook received with following non whitelisted IP", { | ||
extra: { | ||
ip: request.remote_ip | ||
} | ||
}) | ||
head :forbidden | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
module BrevoIpHelper | ||
def stub_brevo_webhook_ip(remote_ip = "1.179.112.1") | ||
[ | ||
Brevo::MailWebhooksController, | ||
Brevo::SmsWebhooksController | ||
].each do |controller| | ||
allow_any_instance_of(controller).to receive(:request).and_wrap_original do |original_request| | ||
original_request.call.tap do |request| | ||
allow(request).to receive(:remote_ip).and_return(remote_ip) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
|
||
RSpec.shared_context "with ip whitelist", shared_context: :metadata do |ip| | ||
let(:remote_ip) { ip || "1.179.112.1" } | ||
|
||
before do | ||
stub_brevo_webhook_ip(remote_ip) | ||
end | ||
end | ||
|
||
RSpec.shared_examples "returns 403 for non-whitelisted IP" do |ip| | ||
include_context "with ip whitelist", ip | ||
|
||
context "when called with non-matching IP" do | ||
it "returns 403" do | ||
expect(Sentry).to( | ||
receive(:capture_message).with("Brevo Webhook received with following non whitelisted IP", { | ||
extra: { ip: ip } | ||
}) | ||
) | ||
post :create, params:, as: :json | ||
expect(response).to have_http_status(:forbidden) | ||
end | ||
end | ||
|
||
context "when whitelisting is disabled" do | ||
before do | ||
ENV["DISABLE_BREVO_IP_WHITELIST"] = "true" | ||
end | ||
|
||
after do | ||
ENV["DISABLE_BREVO_IP_WHITELIST"] = nil | ||
end | ||
|
||
it "skips whitelisting" do | ||
expect(Sentry).not_to( | ||
receive(:capture_message).with("Brevo Webhook received with following non whitelisted IP", { | ||
extra: { ip: ip } | ||
}) | ||
) | ||
post :create, params:, as: :json | ||
expect(response).not_to have_http_status(:forbidden) | ||
end | ||
end | ||
end |