@@ -3,8 +3,8 @@ package fibery
33import (
44 "context"
55 "fmt"
6+ "io"
67 "net/http"
7- "strings"
88 "time"
99
1010 regexp "github.com/wasilibs/go-re2"
@@ -14,66 +14,64 @@ import (
1414)
1515
1616type Scanner struct {
17+ client * http.Client
1718 detectors.DefaultMultiPartCredentialProvider
1819}
1920
2021// Ensure the Scanner satisfies the interface at compile time.
2122var _ detectors.Detector = (* Scanner )(nil )
2223
2324var (
24- client = detectors .DetectorHttpClientWithNoLocalAddresses
25+ defaultClient = detectors .DetectorHttpClientWithNoLocalAddresses
2526
2627 // Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
27- keyPat = regexp .MustCompile (detectors .PrefixRegex ([]string {"fibery" }) + `\b([0-9a-f]{8}.[0-9a-f]{35})\b` )
28- domainPat = regexp .MustCompile (detectors . PrefixRegex ([] string { "fibery" , "domain" }) + `\b([0-9A-Za-z]{2,40 })\b ` )
28+ keyPat = regexp .MustCompile (detectors .PrefixRegex ([]string {"fibery" }) + `\b([0-9a-f]{8}\ .[0-9a-f]{35})\b` )
29+ domainPat = regexp .MustCompile (`(?:https?:\/\/)?([a-zA-Z0-9-]{1,63 })\.fibery\.io(?:\/.*)? ` )
2930)
3031
3132// Keywords are used for efficiently pre-filtering chunks.
3233// Use identifiers in the secret preferably, or the provider name.
3334func (s Scanner ) Keywords () []string {
34- return []string {"fibery" }
35+ return []string {". fibery.io " }
3536}
3637
3738// Description returns a description for the result being detected
3839func (s Scanner ) Description () string {
3940 return "Fibery is a work management platform that combines various tools for project management, knowledge management, and software development. Fibery API tokens can be used to access and modify data within a Fibery workspace."
4041}
4142
43+ func (s Scanner ) getClient () * http.Client {
44+ if s .client != nil {
45+ return s .client
46+ }
47+ return defaultClient
48+ }
49+
4250// FromData will find and optionally verify Fibery secrets in a given set of bytes.
4351func (s Scanner ) FromData (ctx context.Context , verify bool , data []byte ) (results []detectors.Result , err error ) {
4452 dataStr := string (data )
4553
46- matches := keyPat .FindAllStringSubmatch (dataStr , - 1 )
47- domainMatches := domainPat .FindAllStringSubmatch (dataStr , - 1 )
48-
49- for _ , match := range matches {
50- resMatch := strings .TrimSpace (match [1 ])
54+ uniqueSecrets := make (map [string ]struct {})
55+ uniqueDomains := make (map [string ]struct {})
5156
52- for _ , domainMatch := range domainMatches {
53-
54- resDomainMatch := strings .TrimSpace (domainMatch [1 ])
57+ for _ , match := range keyPat .FindAllStringSubmatch (dataStr , - 1 ) {
58+ uniqueSecrets [match [1 ]] = struct {}{}
59+ }
60+ for _ , match := range domainPat .FindAllStringSubmatch (dataStr , - 1 ) {
61+ uniqueDomains [match [1 ]] = struct {}{}
62+ }
5563
64+ for secret := range uniqueSecrets {
65+ for domain := range uniqueDomains {
5666 s1 := detectors.Result {
5767 DetectorType : detectorspb .DetectorType_Fibery ,
58- Raw : []byte (resMatch ),
68+ Raw : []byte (secret ),
5969 }
6070
6171 if verify {
62- timeout := 10 * time .Second
63- client .Timeout = timeout
64- req , err := http .NewRequestWithContext (ctx , "POST" , fmt .Sprintf ("https://%s.fibery.io/api/commands" , resDomainMatch ), nil )
65- if err != nil {
66- continue
67- }
68- req .Header .Add ("Content-Type" , "application/json" )
69- req .Header .Add ("Authorization" , fmt .Sprintf ("Token %s" , resMatch ))
70- res , err := client .Do (req )
71- if err == nil {
72- defer res .Body .Close ()
73- if res .StatusCode >= 200 && res .StatusCode < 300 {
74- s1 .Verified = true
75- }
76- }
72+ isVerified , verificationErr := verifyMatch (ctx , s .getClient (), secret , domain )
73+ s1 .Verified = isVerified
74+ s1 .SetVerificationError (verificationErr , secret , domain )
7775 }
7876
7977 results = append (results , s1 )
@@ -83,6 +81,37 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
8381 return results , nil
8482}
8583
84+ func verifyMatch (ctx context.Context , client * http.Client , secret , domain string ) (bool , error ) {
85+ timeout := 10 * time .Second
86+ client .Timeout = timeout
87+ url := fmt .Sprintf ("https://%s.fibery.io/api/commands" , domain )
88+ req , err := http .NewRequestWithContext (ctx , http .MethodPost , url , http .NoBody )
89+ if err != nil {
90+ return false , err
91+ }
92+
93+ req .Header .Add ("Content-Type" , "application/json" )
94+ req .Header .Add ("Authorization" , fmt .Sprintf ("Token %s" , secret ))
95+ res , err := client .Do (req )
96+ if err != nil {
97+ return false , err
98+ }
99+
100+ defer func () {
101+ _ , _ = io .Copy (io .Discard , res .Body )
102+ _ = res .Body .Close ()
103+ }()
104+
105+ switch res .StatusCode {
106+ case http .StatusOK :
107+ return true , nil
108+ case http .StatusUnauthorized :
109+ return false , nil
110+ default :
111+ return false , fmt .Errorf ("unexpected status code: %d" , res .StatusCode )
112+ }
113+ }
114+
86115func (s Scanner ) Type () detectorspb.DetectorType {
87116 return detectorspb .DetectorType_Fibery
88117}
0 commit comments