Class: CreditCardSanitizer
- Inherits:
-
Object
- Object
- CreditCardSanitizer
- Defined in:
- lib/credit_card_sanitizer.rb
Defined Under Namespace
Classes: Candidate
Constant Summary collapse
- CARD_COMPANIES =
{ "visa" => /^4\d{12}(\d{3})?(\d{3})?$/, "master" => /^(5[1-5]\d{4}|677189|222[1-9]\d{2}|22[3-9]\d{3}|2[3-6]\d{4}|27[01]\d{3}|2720\d{2})\d{10}$/, "discover" => /^((6011\d{12})|(65[4-9]\d{13})|(64[4-9]\d{13})|(622(?:12[6-9]|1[3-9]\d|[2-8]\d{2}|9[01]\d|92[0-5])\d{10}))$/, "american_express" => /^3[47]\d{13}$/, "diners_club" => /^3(0[0-5]|[68]\d)\d{11}$/, "jcb" => /^35(28|29|[3-8]\d)\d{12}$/, "switch" => /^(6759\d{12}(\d{2,3})?|(4903|4905|4911|4936|6333|6759)\d{12}|(4903|4905|4911|4936|6333|6759)\d{14}|(4903|4905|4911|4936|6333|6759)\d{15}|564182\d{10}|564182\d{12}|564182\d{13}|633110\d{10}|633110\d{12}|633110\d{13})$/, "solo" => /^(6767\d{12}(\d{2,3})?|6334\d{12}|6334\d{14}|6334\d{15}|6767\d{14}|6767\d{15})$/, "dankort" => /^5019\d{12}$/, "maestro" => /^(5[06-8]\d{10,17}|6\d\d{10,17}|5018|5020|5038|5893|6304|6759|6761|6762|6763\d{8,15})$/, "forbrugsforeningen" => /^600722\d{10}$/, "laser" => /^(6304|6706|6709|6771(?!89))(\d{12,15}|\d{8}(\d{4}|\d{6,7})?)$/, "bc_global" => /^(6541|6556)\d{12}$/, "carte_blanche" => /^389\d{11}$/, "insta_payment" => /^63[7-9]\d{13}$/, "korean_local" => /^9\d{15}$/, "union_pay" => /^62\d{14,17}$/, "visa_master" => /^(4\d{12}(\d{3})?|5[1-5]\d{14})$/ }.freeze
- CARD_NUMBER_GROUPINGS =
{ "visa" => [[4, 4, 4, 4]], "master" => [[4, 4, 4, 4]], "discover" => [[4, 4, 4, 4]], "american_express" => [[4, 6, 5]], "diners_club" => [[4, 6, 4]], "jcb" => [[4, 4, 4, 4]], "switch" => [[4, 4, 4, 4]], "solo" => [[4, 4, 4, 4], [4, 4, 4, 4, 2], [4, 4, 4, 4, 3]], "dankort" => [[4, 4, 4, 4]], "maestro" => [[4], [5], [4, 4, 4, 4], [4, 4, 4, 4, 1], [4, 4, 4, 4, 2], [4, 4, 4, 4, 3]], "forbrugsforeningen" => [[4, 4, 4, 4]], "laser" => [[4, 4, 4, 4], [4, 4, 4, 4, 1], [4, 4, 4, 4, 2], [4, 4, 4, 4, 3]], "bc_global" => [[4, 4, 4, 4]], "carte_blanche" => [[4, 6, 4]], "insta_payment" => [[4, 4, 4, 4]], "korean_local" => [[4, 4, 4, 4]], "union_pay" => [[4, 4, 4, 4], [4, 4, 4, 4, 1], [4, 4, 4, 4, 2], [4, 4, 4, 4, 3]], "visa_master" => [[4, 4, 4, 4], [4, 4, 4, 4, 3]] }.freeze
- ACCEPTED_PREFIX =
/(?:cc|card|visa|amex)\z/i- ACCEPTED_POSTFIX =
/\Aex/i- ALPHANUMERIC =
/[[:alnum:]]/i- VALID_COMPANY_PREFIXES =
Regexp.union(*CARD_COMPANIES.values)
- EXPIRATION_DATE =
/\s(?:0?[1-9]|1[0-2])(?:\/|-)(?:\d{4}|\d{2})(?:\D|$)/- LINE_NOISE_CHAR =
/[^\w\n,()&.\/:;<>]/- LINE_NOISE =
/#{LINE_NOISE_CHAR}{,5}/- NONEMPTY_LINE_NOISE =
/#{LINE_NOISE_CHAR}{1,5}/- SCHEME_OR_PLUS =
/((?:+|\+|\/)|(?:[a-zA-Z][-+.a-zA-Z\d]{,9}):[^\s>]+)/- NUMBERS_WITH_LINE_NOISE =
/#{SCHEME_OR_PLUS}?\d(?:#{LINE_NOISE}\d){10,30}/- DEFAULT_OPTIONS =
{ replacement_token: "▇", expose_first: 6, expose_last: 4, use_groupings: false, exclude_tracking_numbers: false, parse_flanking: false }.freeze
Instance Attribute Summary collapse
-
#settings ⇒ Object
readonly
Returns the value of attribute settings.
Class Method Summary collapse
-
.parameter_filter(options = {}) ⇒ Object
A proc that can be used.
Instance Method Summary collapse
-
#initialize(options = {}) ⇒ CreditCardSanitizer
constructor
Create a new CreditCardSanitizer.
-
#sanitize!(text, options = {}) ⇒ Object
Finds credit card numbers and redacts digits from them.
Constructor Details
#initialize(options = {}) ⇒ CreditCardSanitizer
Create a new CreditCardSanitizer
Options
:replacement_character - the character that will replace digits for redaction. :expose_first - the number of leading digits that will not be redacted. :expose_last - the number of ending digits that will not be redacted. :use_groupings - require card number groupings to match to redact. :exclude_tracking_numbers - do not redact valid shipping company tracking numbers.
82 83 84 |
# File 'lib/credit_card_sanitizer.rb', line 82 def initialize( = {}) @settings = DEFAULT_OPTIONS.merge() end |
Instance Attribute Details
#settings ⇒ Object (readonly)
Returns the value of attribute settings.
68 69 70 |
# File 'lib/credit_card_sanitizer.rb', line 68 def settings @settings end |
Class Method Details
.parameter_filter(options = {}) ⇒ Object
A proc that can be used
text - the text containing potential credit card numbers
Examples
Rails.app.config.filter_parameters = [:password, CreditCardSanitizer.parameter_filter]
env = {
"action_dispatch.request.parameters" => {"credit_card_number" => "4111 1111 1111 1111", "password" => "123"},
"action_dispatch.parameter_filter" => Rails.app.config.filter_parameters
}
>> ActionDispatch::Request.new(env).filtered_parameters
=> {"credit_card_number" => "4111 11▇▇ ▇▇▇▇ 1111", "password" => "[FILTERED]"}
Returns a Proc that takes the key/value of the request parameter.
153 154 155 |
# File 'lib/credit_card_sanitizer.rb', line 153 def self.parameter_filter( = {}) proc { |_, value| new().sanitize!(value) if value.is_a?(String) } end |
Instance Method Details
#sanitize!(text, options = {}) ⇒ Object
Finds credit card numbers and redacts digits from them
text - the text containing potential credit card numbers
Examples
# If the text contains a credit card number:
sanitize!("4111 1111 1111 1111")
#=> "4111 11▇▇ ▇▇▇▇ 1111"
# If the text does not contain a credit card number:
sanitize!("I want all your credit card numbers!")
#=> nil
If options is false, returns nil if no redaction happened, else the full text after redaction.
If options is true, returns nil if no redaction happened, else an array of [old_text, new_text] indicating what substrings were redacted.
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/credit_card_sanitizer.rb', line 105 def sanitize!(text, = {}) = @settings.merge() text.force_encoding(Encoding::UTF_8) text.scrub!("�") changes = nil without_expiration(text) do text.gsub!(NUMBERS_WITH_LINE_NOISE) do |match| next match if $1 candidate = Candidate.new(match, match.tr("^0-9", ""), $`, $') if valid_context?(candidate, ) && valid_numbers?(candidate, ) redact_numbers(candidate, ).tap do |redacted_text| changes ||= [] changes << [candidate.text, redacted_text] end else match end end end if [:return_changes] changes else changes && text end end |