@@ -3,8 +3,8 @@ package privacy
33import (
44 "context"
55 "fmt"
6+ "io"
67 "net/http"
7- "strings"
88
99 regexp "github.com/wasilibs/go-re2"
1010
@@ -20,68 +20,83 @@ type Scanner struct {
2020// Ensure the Scanner satisfies the interface at compile time.
2121var _ detectors.Detector = (* Scanner )(nil )
2222
23+ func (s Scanner ) Type () detectorspb.DetectorType {
24+ return detectorspb .DetectorType_Privacy
25+ }
26+
27+ func (s Scanner ) Description () string {
28+ return "Privacy provides virtual cards for secure online payments. Privacy API keys can be used to manage and create these virtual cards."
29+ }
30+
2331var (
24- defaultClient = common .SaneHttpClient ()
2532 // Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
2633 keyPat = regexp .MustCompile (detectors .PrefixRegex ([]string {"privacy" }) + `\b([a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12})\b` )
2734)
2835
2936// Keywords are used for efficiently pre-filtering chunks.
3037// Use identifiers in the secret preferably, or the provider name.
3138func (s Scanner ) Keywords () []string {
32- return []string {"privacy" }
39+ return []string {"privacy.com " }
3340}
3441
3542// FromData will find and optionally verify Privacy secrets in a given set of bytes.
3643func (s Scanner ) FromData (ctx context.Context , verify bool , data []byte ) (results []detectors.Result , err error ) {
3744 dataStr := string (data )
3845
39- matches := keyPat .FindAllStringSubmatch (dataStr , - 1 )
40-
41- for _ , match := range matches {
42- resMatch := strings .TrimSpace (match [1 ])
46+ uniqueMatches := make (map [string ]struct {})
47+ for _ , match := range keyPat .FindAllStringSubmatch (dataStr , - 1 ) {
48+ m := match [1 ]
49+ if detectors .StringShannonEntropy (m ) < 3 {
50+ continue
51+ }
52+ uniqueMatches [m ] = struct {}{}
53+ }
4354
44- s1 := detectors.Result {
55+ for key := range uniqueMatches {
56+ r := detectors.Result {
4557 DetectorType : detectorspb .DetectorType_Privacy ,
46- Raw : []byte (resMatch ),
58+ Raw : []byte (key ),
4759 }
4860
4961 if verify {
50- client := s .client
51- if client == nil {
52- client = defaultClient
53- }
54- req , err := http .NewRequestWithContext (ctx , "GET" , "https://api.privacy.com/v1/card?page=1&page_size=50" , nil )
55- if err != nil {
56- continue
57- }
58- req .Header .Set ("Authorization" , "api-key " + resMatch )
59- res , err := client .Do (req )
60- if err == nil {
61- defer res .Body .Close ()
62- if res .StatusCode >= 200 && res .StatusCode < 300 {
63- s1 .Verified = true
64- } else if res .StatusCode == 401 {
65- // The secret is determinately not verified (nothing to do)
66- } else {
67- err = fmt .Errorf ("unexpected HTTP response status %d" , res .StatusCode )
68- s1 .SetVerificationError (err , resMatch )
69- }
70- } else {
71- s1 .SetVerificationError (err , resMatch )
62+ if s .client == nil {
63+ s .client = common .SaneHttpClient ()
7264 }
65+
66+ isVerified , vErr := verifyMatch (ctx , s .client , key )
67+ r .Verified = isVerified
68+ r .SetVerificationError (vErr , key )
7369 }
7470
75- results = append (results , s1 )
71+ results = append (results , r )
7672 }
7773
7874 return results , nil
7975}
8076
81- func (s Scanner ) Type () detectorspb.DetectorType {
82- return detectorspb .DetectorType_Privacy
83- }
77+ func verifyMatch (ctx context.Context , client * http.Client , key string ) (bool , error ) {
78+ req , err := http .NewRequestWithContext (ctx , http .MethodGet , "https://api.privacy.com/v1/card?page=1&page_size=50" , nil )
79+ if err != nil {
80+ return false , err
81+ }
8482
85- func (s Scanner ) Description () string {
86- return "Privacy provides virtual cards for secure online payments. Privacy API keys can be used to manage and create these virtual cards."
83+ req .Header .Set ("Authorization" , "api-key " + key )
84+ res , err := client .Do (req )
85+ if err != nil {
86+ return false , err
87+ }
88+ defer func () {
89+ _ , _ = io .Copy (io .Discard , res .Body )
90+ _ = res .Body .Close ()
91+ }()
92+
93+ switch res .StatusCode {
94+ case http .StatusOK :
95+ return true , nil
96+ case http .StatusUnauthorized :
97+ return false , nil
98+ default :
99+ body , _ := io .ReadAll (res .Body )
100+ return false , fmt .Errorf ("unexpected HTTP response status %d, %q" , res .StatusCode , string (body ))
101+ }
87102}
0 commit comments