pan 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +46 -0
  4. data/lib/pan.rb +29 -0
  5. data/spec/pan_spec.rb +97 -0
  6. metadata +64 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e439926c079be533eaae81268a515e263efe6cfa
4
+ data.tar.gz: 3eeaac383f04043f7703d6c2e4044dcd218fceeb
5
+ SHA512:
6
+ metadata.gz: f14cf72b8f7b604e3d6411127767a7fa3df37771648c6f4048156e70a6fed3e4037eca0f7bed22b48a1ee95a23797327fc1b0427192e195b2cb97d964434321a
7
+ data.tar.gz: 93b04840f500cd10cb5f15d6dbb6c7ce65480c8e91a5fefaa7b9458752bc057697e408dc8866f4a8e1de2cd30559631197a7262470d3c468c6d3f7a4a1a39801
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Clearhaus
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,46 @@
1
+ # pan
2
+
3
+ Cares about PANs ([Primary Account Number](https://en.wikipedia.org/wiki/Payment_card_number)).
4
+
5
+ ## Examples
6
+
7
+ ```ruby
8
+ require 'pan'
9
+
10
+ pan = '1234567890123456'
11
+
12
+ Pan.mask(pan)
13
+ #=> '123456******3456'
14
+
15
+ pan
16
+ #=> '1234567890123456'
17
+
18
+ Pan.truncate(pan)
19
+ #=> '123456******3456'
20
+
21
+ pan
22
+ #=> '123456******3456'
23
+ ```
24
+
25
+ (Yes, that's the difference between masking and truncating a PAN in PCI DSS
26
+ terminology.)
27
+
28
+ You may decide how much to mask (or truncate) and the character(s) that the
29
+ digits are replaced with:
30
+
31
+ ```ruby
32
+ Pan.template = [0, 'x', 4]
33
+
34
+ Pan.mask('1234567890123456')
35
+ #=> 'xxxxxxxxxxxx3456'
36
+ ```
37
+
38
+ ```ruby
39
+ pan = '1234567890123456'
40
+
41
+ Pan.mask(pan, template: [4, 'X', 4])
42
+ #=> '1234XXXXXXXX3456'
43
+
44
+ Pan.truncate(pan, template: [6, '*', 4]); pan
45
+ #=> '123456******3456'
46
+ ```
@@ -0,0 +1,29 @@
1
+ class Pan
2
+ VERSION = '1.0.0'
3
+
4
+ singleton_class.class_eval do
5
+ attr_accessor :template
6
+ end
7
+
8
+ DEFAULT_TEMPLATE = [6, '*', 4]
9
+ self.template = DEFAULT_TEMPLATE
10
+
11
+ class Error < RuntimeError; end
12
+
13
+ def self.mask(pan, template: self.template)
14
+ fail Pan::Error, 'invalid template' unless template.first >= 0 and template.last >= 0 and template[1].is_a?(String)
15
+ fail Pan::Error, 'invalid pan' unless pan.is_a?(String)
16
+
17
+ mask_length = pan.length - template.first - template.last
18
+
19
+ return pan if mask_length <= 0
20
+
21
+ pan.slice(0, template.first) \
22
+ + (template[1] * mask_length) \
23
+ + pan.slice(-template.last, template.last)
24
+ end
25
+
26
+ def self.truncate(pan, template: self.template)
27
+ pan.replace(self.mask(pan, template: template))
28
+ end
29
+ end
@@ -0,0 +1,97 @@
1
+ require_relative '../lib/pan.rb'
2
+
3
+ describe 'Pan#mask' do
4
+ it 'default template' do
5
+ expect(Pan.mask('')).to eq ''
6
+ expect(Pan.mask('1')).to eq '1'
7
+ expect(Pan.mask('12')).to eq '12'
8
+ expect(Pan.mask('123')).to eq '123'
9
+ expect(Pan.mask('1234')).to eq '1234'
10
+ expect(Pan.mask('12345')).to eq '12345'
11
+ expect(Pan.mask('123456')).to eq '123456'
12
+ expect(Pan.mask('1234567')).to eq '1234567'
13
+ expect(Pan.mask('12345678')).to eq '12345678'
14
+ expect(Pan.mask('123456789')).to eq '123456789'
15
+ expect(Pan.mask('1234567890')).to eq '1234567890'
16
+ expect(Pan.mask('12345678901')).to eq '123456*8901'
17
+ expect(Pan.mask('123456789012')).to eq '123456**9012'
18
+ expect(Pan.mask('1234567890123')).to eq '123456***0123'
19
+ expect(Pan.mask('12345678901234')).to eq '123456****1234'
20
+ expect(Pan.mask('123456789012345')).to eq '123456*****2345'
21
+ expect(Pan.mask('1234567890123456')).to eq '123456******3456'
22
+ expect(Pan.mask('12345678901234567')).to eq '123456*******4567'
23
+ expect(Pan.mask('123456789012345678')).to eq '123456********5678'
24
+ expect(Pan.mask('1234567890123456789')).to eq '123456*********6789'
25
+ expect(Pan.mask('12345678901234567890')).to eq '123456**********7890'
26
+ end
27
+
28
+ it 'per mask template' do
29
+ expect(Pan.mask('1234', template: [1,'-',1])).to eq '1--4'
30
+ end
31
+
32
+ it 'long replacement string' do
33
+ expect(Pan.mask('1234', template: [1,'abc',1])).to eq '1abcabc4'
34
+ end
35
+
36
+ context 'front/end corner cases' do
37
+ let (:pan) { '12345' }
38
+
39
+ it 'some in front, some in end' do
40
+ expect(Pan.mask(pan, template: [2,'-',2])).to eq '12-45'
41
+ end
42
+
43
+ it 'none in front, some in end' do
44
+ expect(Pan.mask(pan, template: [0,'-',2])).to eq '---45'
45
+ end
46
+
47
+ it 'some in front, none in end' do
48
+ expect(Pan.mask(pan, template: [2,'-',0])).to eq '12---'
49
+ end
50
+
51
+ it 'none in front, none in end' do
52
+ expect(Pan.mask(pan, template: [0,'-',0])).to eq '-----'
53
+ end
54
+ end
55
+
56
+ context 'pan corner cases' do
57
+ it 'empty pan' do
58
+ expect(Pan.mask('', template: [1,'x',1])).to eq ''
59
+ expect(Pan.mask('', template: [0,'x',0])).to eq ''
60
+ end
61
+
62
+ it 'nil pan fail' do
63
+ expect{Pan.mask(nil)}.to raise_error(Pan::Error, /invalid pan/)
64
+ end
65
+
66
+ it 'integer pan fail' do
67
+ expect{Pan.mask(1234567890123456)}.to raise_error(Pan::Error, /invalid pan/)
68
+ end
69
+ end
70
+
71
+ it 'fails on invalid template' do
72
+ expect{Pan.mask('', template: [-1,'x',0])}.to raise_error(Pan::Error, /invalid template/)
73
+ end
74
+
75
+ it 'custom global template' do
76
+ previous_template, Pan.template = Pan.template, [2,'X',2]
77
+
78
+ expect(Pan.mask('1234567890123456')).to eq '12XXXXXXXXXXXX56'
79
+
80
+ Pan.template = previous_template
81
+ end
82
+
83
+ it 'does not changes the object' do
84
+ pan = '1234567890123456'
85
+ expect(Pan.mask(pan)).to eq '123456******3456'
86
+ expect(pan).to eq pan
87
+ end
88
+ end
89
+
90
+ describe 'Pan#truncate' do
91
+ it 'uses #mask and changes the object' do
92
+ pan = '1234'
93
+ expect(Pan).to receive(:mask).with(pan, template: Pan::DEFAULT_TEMPLATE).and_return('1**4')
94
+ Pan.truncate(pan)
95
+ expect(pan).to eq '1**4'
96
+ end
97
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pan
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Casper Thomsen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-06-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 2.0.0
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 2.0.0
27
+ description: |2
28
+ A credit card number AKA Primary Account Number (PAN) often needs to be
29
+ masked or truncated, that is, have "enough" digits made unreadable.
30
+ email:
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - LICENSE
36
+ - README.md
37
+ - lib/pan.rb
38
+ - spec/pan_spec.rb
39
+ homepage: http://github.com/clearhaus/pan
40
+ licenses:
41
+ - MIT
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 2.4.6
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: Cares about PANs (credit card numbers)
63
+ test_files: []
64
+ has_rdoc: