1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
package detector
import (
"regexp"
"strings"
)
var ccRE = regexp.MustCompile(
`\d{4}[ \-]\d{4}[ \-]\d{4}[ \-]\d{4}|\d{4}[ \-]\d{6}[ \-]\d{5}|\d{4}[ \-]\d{6}[ \-]\d{4}|\d{13,19}`,
)
func luhnValid(digits string) bool {
rev := []rune(digits)
for i, j := 0, len(rev)-1; i < j; i, j = i+1, j-1 {
rev[i], rev[j] = rev[j], rev[i]
}
total := 0
for i, ch := range rev {
n := int(ch - '0')
if i%2 == 1 {
n *= 2
if n > 9 {
n -= 9
}
}
total += n
}
return total%10 == 0
}
func isDigit(b byte) bool { return b >= '0' && b <= '9' }
func detectCreditCard(text string) []Finding {
var out []Finding
for _, loc := range ccRE.FindAllStringIndex(text, -1) {
if loc[0] > 0 && isDigit(text[loc[0]-1]) {
continue
}
if loc[1] < len(text) && isDigit(text[loc[1]]) {
continue
}
raw := text[loc[0]:loc[1]]
isFormatted := strings.ContainsAny(raw, " -")
digits := strings.Map(func(r rune) rune {
if r >= '0' && r <= '9' {
return r
}
return -1
}, raw)
luhnOK := luhnValid(digits)
var confidence float64
if isFormatted {
if luhnOK {
confidence = 1.0
} else {
confidence = 0.6
}
} else {
if !luhnOK {
continue
}
confidence = 0.9
}
out = append(out, Finding{
Type: PiiCreditCard, Start: loc[0], End: loc[1],
Text: raw, Confidence: confidence,
})
}
return out
}
|