dont allow outbound mms outside of NANP

Phillip Davis created

Change summary

Gemfile                                            |  1 
sgx-bwmsgsv2.rb                                    | 10 +
test/property/rantly_extensions/data_extensions.rb | 54 +++++++++
test/property/test_non_nanp_oob.rb                 | 92 ++++++++++++++++
4 files changed, 156 insertions(+), 1 deletion(-)

Detailed changes

Gemfile 🔗

@@ -44,4 +44,5 @@ group(:test) do
 	gem 'webmock'
 	# DSL wrapper around Rantly generators
 	gem "jennifer", github: "phdavis1027/jennifer"
+	gem "regexp-examples", "~> 1.6"
 end

sgx-bwmsgsv2.rb 🔗

@@ -401,7 +401,15 @@ module SGXbwmsgsv2
 		puts "MMSOOB: found a url node - checking if to make MMS..."
 
 		body = s.respond_to?(:body) ? s.body : ''
-		EM::HttpRequest.new(un.text, tls: { verify_peer: true }).ahead.then { |http|
+
+		if num_dest.is_a?(String) && num_dest !~ /^\+?1/
+			unless body.include?(un.text)
+				s.body = body.empty? ? un.text : "#{body}\n#{un.text}"
+			end
+			return to_catapult(s, nil, num_dest, user_id, token, secret, usern)
+		end
+
+		EM::HttpRequest.new(un.text, tls: {verify_peer: true}).ahead.then { |http|
 			# If content is too large, or MIME type is not supported, place the link inside the body and do not send MMS.
 			if http.response_header["CONTENT_LENGTH"].to_i > 3500000 ||
 			   !MMS_MIME_TYPES.include?(http.response_header["CONTENT_TYPE"])

test/property/rantly_extensions/data_extensions.rb 🔗

@@ -1,8 +1,62 @@
 # frozen_string_literal: true
 
+require "regexp-examples"
+
 class Rantly
 	module Data
 		module Extensions
+			REGEXP_EXAMPLES_OPTS = {
+				max_group_results: 10,
+				max_repeater_variance: 10
+			}.freeze
+
+			# @see https://github.com/mnestorov/regex-patterns
+			# @return [String]
+			FRENCH_PHONE = /\+33[1-9]\d{8}/.freeze
+			# @return [String]
+			GERMAN_PHONE = /\+49[1-9]\d{3,12}/.freeze
+			# @return [String]
+			UK_PHONE = /\+44[1-9]\d{9,10}/.freeze
+			# @return [String]
+			SPANISH_PHONE = /\+34[6-9]\d{8}/.freeze
+			# @return [String]
+			ITALIAN_PHONE = /\+39[0-9]{9,10}/.freeze
+
+			# @return [String]
+			def french_phone
+				FRENCH_PHONE.random_example(**REGEXP_EXAMPLES_OPTS)
+			end
+
+			# @return [String]
+			def german_phone
+				GERMAN_PHONE.random_example(**REGEXP_EXAMPLES_OPTS)
+			end
+
+			# @return [String]
+			def uk_phone
+				UK_PHONE.random_example(**REGEXP_EXAMPLES_OPTS)
+			end
+
+			# @return [String]
+			def spanish_phone
+				SPANISH_PHONE.random_example(**REGEXP_EXAMPLES_OPTS)
+			end
+
+			# @return [String]
+			def italian_phone
+				ITALIAN_PHONE.random_example(**REGEXP_EXAMPLES_OPTS)
+			end
+
+			# @return [String]
+			def non_nanp_phone
+				send(
+					choose(
+						:french_phone, :german_phone, :uk_phone,
+						:spanish_phone, :italian_phone
+					)
+				)
+			end
+
 			# @note https://stackoverflow.com/questions/6478875/regular-expression-matching-e-164-formatted-phone-numbers
 			# @return [String]
 			def nanpa_phone

test/property/test_non_nanp_oob.rb 🔗

@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+require "test_helper"
+require_relative "../../sgx-bwmsgsv2"
+require "rantly/minitest_extensions"
+require_relative "rantly_extensions/data_extensions"
+
+class NonNanpOobPropertyTest < Minitest::Test
+	BW_MESSAGES_URL =
+		"https://messaging.bandwidth.com/api/v2/users/account/messages"
+
+	def build_oob_message(dest, body, oob_url)
+		m = Blather::Stanza::Message.new("#{dest}@component", body)
+		m.from = "test@example.com/res"
+
+		x = Nokogiri::XML::Node.new("x", m.document)
+		ns = x.add_namespace(nil, "jabber:x:oob")
+		url_node = Nokogiri::XML::Node.new("url", m.document)
+		url_node.namespace = ns
+		url_node.content = oob_url
+		x.add_child(url_node)
+		m.add_child(x)
+		m
+	end
+
+	def test_non_nanp_oob_sends_url_as_text_not_mms
+		property_of {
+			dest = non_nanp_phone
+			body = choose("", sized(range(5, 40)) { string(:alnum) })
+			guard(!body.downcase.match?(BADWORDS))
+			oob_path = sized(range(3, 12)) { string(:alnum) }
+			oob_url = "https://example.com/media/#{oob_path}.jpg"
+			[dest, body, oob_url]
+		}.check { |dest, body, oob_url|
+			reset_stanzas!
+			reset_redis!
+
+			stub_request(:post, BW_MESSAGES_URL).to_return(
+				status: 201, body: JSON.dump(id: "bw-msg-fallback")
+			)
+			bw_req = stub_request(:post, BW_MESSAGES_URL).with(
+				body: hash_including(
+					text: /#{Regexp.escape(oob_url)}/
+				)
+			).to_return(
+				status: 201, body: JSON.dump(id: "bw-msg-non-nanp")
+			)
+
+			process_stanza(build_oob_message(dest, body, oob_url))
+
+			assert_not_requested :head, oob_url
+			assert_requested bw_req
+		}
+	end
+	em :test_non_nanp_oob_sends_url_as_text_not_mms
+
+	def test_nanp_oob_attempts_mms
+		property_of {
+			dest = nanpa_phone
+			body = sized(range(5, 40)) { string(:alnum) }
+			guard(!body.downcase.match?(BADWORDS))
+			oob_path = sized(range(3, 12)) { string(:alnum) }
+			oob_url = "https://example.com/media/#{oob_path}.jpg"
+			[dest, body, oob_url]
+		}.check { |dest, body, oob_url|
+			reset_stanzas!
+			reset_redis!
+
+			stub_request(:post, BW_MESSAGES_URL).to_return(
+				status: 201, body: JSON.dump(id: "bw-msg-fallback")
+			)
+			bw_req = stub_request(:post, BW_MESSAGES_URL).with(
+				body: hash_including(media: oob_url)
+			).to_return(
+				status: 201, body: JSON.dump(id: "bw-msg-nanp")
+			)
+			stub_request(:head, oob_url).to_return(
+				status: 200,
+				headers: {
+					"Content-Length" => "500",
+					"Content-Type" => "image/jpeg"
+				}
+			)
+
+			process_stanza(build_oob_message(dest, body, oob_url))
+
+			assert_requested :head, oob_url
+			assert_requested bw_req
+		}
+	end
+	em :test_nanp_oob_attempts_mms
+end