diff --git a/lib/authorize_net/api/SensitiveDataFilter.rb b/lib/authorize_net/api/SensitiveDataFilter.rb
index 07c2301..db1eb24 100644
--- a/lib/authorize_net/api/SensitiveDataFilter.rb
+++ b/lib/authorize_net/api/SensitiveDataFilter.rb
@@ -19,7 +19,11 @@ def initialize
SensitiveTag.new("expirationDate", "", "XXX", false),
SensitiveTag.new("accountNumber", "(\\p{N}+)(\\p{N}{4})", "XXXX-\\2", false),
SensitiveTag.new("nameOnAccount", "", "XXX", false),
- SensitiveTag.new("transactionKey", "", "XXX", false)]).freeze
+ SensitiveTag.new("transactionKey", "", "XXX", false),
+ SensitiveTag.new("accessToken", "", "XXX", false),
+ SensitiveTag.new("connectedAccessToken", "", "XXX", false),
+ SensitiveTag.new("sessionToken", "", "XXX", false),
+ SensitiveTag.new("token", "", "XXX", false)]).freeze
@sensitiveStringRegexes = ["4\\p{N}{3}([\\ \\-]?)\\p{N}{4}\\1\\p{N}{4}\\1\\p{N}{4}",
"4\\p{N}{3}([\\ \\-]?)(?:\\p{N}{4}\\1){2}\\p{N}(?:\\p{N}{3})?",
"5[1-5]\\p{N}{2}([\\ \\-]?)\\p{N}{4}\\1\\p{N}{4}\\1\\p{N}{4}",
@@ -33,26 +37,24 @@ class SensitiveDataFilter < Logger::Formatter
@@sensitiveTagConfig = nil
@@tagPatterns = nil
@@tagReplacements = nil
+ @@tagValuePatterns = nil
@@cardPatterns = nil
def initialize
@@sensitiveTagConfig = SensitiveDataConfigType.new
@@cardPatterns = @@sensitiveTagConfig.sensitiveStringRegexes
- @@tagPatterns = Array.new(@@sensitiveTagConfig.sensitiveStringRegexes.length)
+ @@tagPatterns = Array.new(@@sensitiveTagConfig.sensitiveTags.length)
@@tagReplacements = Array.new(@@sensitiveTagConfig.sensitiveTags.length)
+ @@tagValuePatterns = Array.new(@@sensitiveTagConfig.sensitiveTags.length)
@@sensitiveTagConfig.sensitiveTags.each_with_index do |sensitiveTag, index|
tagName = sensitiveTag.tagName
replacement = sensitiveTag.replacement
+ contentPattern = sensitiveTag.pattern
- if sensitiveTag.pattern.nil? || sensitiveTag.pattern.empty?
- pattern = "(.*)"
- else
- pattern = sensitiveTag.pattern
- end
-
- @@tagPatterns[index] = "<"+tagName+">"+pattern+""+tagName+">"
- @@tagReplacements[index] = "<"+tagName+">"+replacement+""+tagName+">"
+ @@tagPatterns[index] = Regexp.new("(<(?:\\w+:)?#{Regexp.escape(tagName)}\\b[^>]*>)(.*?)((?:\\w+:)?#{Regexp.escape(tagName)}\\s*>)", Regexp::IGNORECASE | Regexp::MULTILINE)
+ @@tagReplacements[index] = replacement
+ @@tagValuePatterns[index] = contentPattern
end
end
@@ -72,8 +74,24 @@ def maskCreditCards(input)
def maskSensitiveXmlString(input)
input = input.force_encoding("UTF-8")
- @@tagPatterns.each_with_index do |item, index|
- input = input.gsub(/#{item}/,@@tagReplacements[index])
+ # Structurally mask the whole merchantAuthentication element to avoid leaking credentials by tag miss.
+ input = input.gsub(/(<(?:\w+:)?merchantAuthentication\b[^>]*>)(.*?)(<\/(?:\w+:)?merchantAuthentication\s*>)/im, "\\1XXX\\3")
+
+ @@tagPatterns.each_with_index do |item, index|
+ replacement = @@tagReplacements[index]
+ valuePattern = @@tagValuePatterns[index]
+ input = input.gsub(item) do
+ openTag = $1
+ value = $2
+ closeTag = $3
+
+ if valuePattern.nil? || valuePattern.empty?
+ "#{openTag}#{replacement}#{closeTag}"
+ else
+ maskedValue = value.gsub(/#{valuePattern}/, replacement)
+ "#{openTag}#{maskedValue}#{closeTag}"
+ end
+ end
end
return input
end
diff --git a/spec/api_spec.rb b/spec/api_spec.rb
index 0306aa4..8047eba 100644
--- a/spec/api_spec.rb
+++ b/spec/api_spec.rb
@@ -559,6 +559,45 @@
expect(response.transactions).not_to eq nil
end
+ it "should mask sensitive token elements in request XML" do
+ filter = SensitiveDataFilter.new
+ xml = "oauth-access-tokenrequest-connected-tokenrequest-session-tokenrequest-hosted-page-tokenmerchant-loginrequest-extra-access-tokenrequest-extra-session-tokenrequest-extra-tokenrequest-extra-connected-token"
+
+ masked = filter.maskSensitiveXmlString(xml)
+
+ expect(masked).to include("XXX")
+ expect(masked).to include("XXX")
+ expect(masked).to include("XXX")
+ expect(masked).to include("XXX")
+ expect(masked).to include("XXX")
+ expect(masked).not_to include("oauth-access-token")
+ expect(masked).not_to include("request-connected-token")
+ expect(masked).not_to include("request-session-token")
+ expect(masked).not_to include("request-hosted-page-token")
+ expect(masked).not_to include("request-extra-access-token")
+ expect(masked).not_to include("request-extra-session-token")
+ expect(masked).not_to include("request-extra-token")
+ expect(masked).not_to include("request-extra-connected-token")
+ end
+
+ it "should mask sensitive token elements in response XML" do
+ filter = SensitiveDataFilter.new
+ xml = "response-access-tokenresponse-connected-tokenresponse-session-tokenresponse-hosted-page-tokenresponse-hosted-page-token-ns"
+
+ masked = filter.maskSensitiveXmlString(xml)
+
+ expect(masked).to include("XXX")
+ expect(masked).to include("XXX")
+ expect(masked).to include("XXX")
+ expect(masked).to include("XXX")
+ expect(masked).to include("XXX")
+ expect(masked).not_to include("response-access-token")
+ expect(masked).not_to include("response-connected-token")
+ expect(masked).not_to include("response-session-token")
+ expect(masked).not_to include("response-hosted-page-token")
+ expect(masked).not_to include("response-hosted-page-token-ns")
+ end
+
def get_actual(expected, className, topElement)
xmlText = @transaction.serialize(expected, topElement)
className.from_xml(xmlText)