@@ -3,7 +3,6 @@ package couchbase
3
3
import (
4
4
"context"
5
5
"fmt"
6
- "strings"
7
6
"time"
8
7
"unicode"
9
8
@@ -23,35 +22,18 @@ type Scanner struct {
23
22
var _ detectors.Detector = (* Scanner )(nil )
24
23
25
24
var (
26
-
27
25
// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
28
- connectionStringPat = regexp .MustCompile (`\bcb\.[a-z0-9]+\.cloud\.couchbase\.com\b` )
29
- usernamePat = `?()/\+=\s\n`
30
- passwordPat = `^<>;.*&|£\n\s`
31
- // passwordPat = regexp.MustCompile(`(?i)(?:pass|pwd)(?:.|[\n\r]){0,15}(\b[^<>;.*&|£\n\s]{8,100}$)`)
32
- // passwordPat = regexp.MustCompile(`(?im)(?:pass|pwd)\S{0,40}?[:=\s]{1,3}[ '"=]{0,1}([^:?()/\+=\s\n]{4,40})\b`)
26
+ connectionStringPat = regexp .MustCompile (`\b(cb\.[a-z0-9]+\.cloud\.couchbase\.com)\b` )
27
+ usernamePat = common .UsernameRegexCheck (`?()/\+=\s\n` )
28
+ passwordPat = common .PasswordRegexCheck (`^<>;.*&|£\n\s` )
33
29
)
34
30
35
- func meetsCouchbasePasswordRequirements (password string ) (string , bool ) {
36
- var hasLower , hasUpper , hasNumber , hasSpecialChar bool
37
- for _ , char := range password {
38
- switch {
39
- case unicode .IsLower (char ):
40
- hasLower = true
41
- case unicode .IsUpper (char ):
42
- hasUpper = true
43
- case unicode .IsNumber (char ):
44
- hasNumber = true
45
- case unicode .IsPunct (char ) || unicode .IsSymbol (char ):
46
- hasSpecialChar = true
47
- }
48
-
49
- if hasLower && hasUpper && hasNumber && hasSpecialChar {
50
- return password , true
51
- }
52
- }
31
+ func (s Scanner ) Type () detectorspb.DetectorType {
32
+ return detectorspb .DetectorType_Couchbase
33
+ }
53
34
54
- return "" , false
35
+ func (s Scanner ) Description () string {
36
+ return "Couchbase is a distributed NoSQL cloud database. Couchbase credentials can be used to access and modify data within the Couchbase database."
55
37
}
56
38
57
39
// Keywords are used for efficiently pre-filtering chunks.
@@ -64,77 +46,37 @@ func (s Scanner) Keywords() []string {
64
46
func (s Scanner ) FromData (ctx context.Context , verify bool , data []byte ) (results []detectors.Result , err error ) {
65
47
dataStr := string (data )
66
48
67
- connectionStringMatches := connectionStringPat . FindAllStringSubmatch ( dataStr , - 1 )
49
+ var uniqueConnStrings , uniqueUsernames , uniquePasswords = make ( map [ string ] struct {}), make ( map [ string ] struct {}), make ( map [ string ] struct {} )
68
50
69
- // prepend 'couchbases://' to the connection string as the connection
70
- // string format is couchbases://cb.stuff.cloud.couchbase.com but the
71
- // cb.stuff.cloud.couchbase.com may be separated from the couchbases:// in codebases.
72
- for i , connectionStringMatch := range connectionStringMatches {
73
- connectionStringMatches [i ][0 ] = "couchbases://" + connectionStringMatch [0 ]
51
+ for _ , match := range connectionStringPat .FindAllStringSubmatch (dataStr , - 1 ) {
52
+ uniqueConnStrings ["couchbases://" + match [1 ]] = struct {}{}
74
53
}
75
54
76
- usernameRegexState := common .UsernameRegexCheck (usernamePat )
77
- usernameMatches := usernameRegexState .Matches (data )
78
-
79
- passwordRegexState := common .PasswordRegexCheck (passwordPat )
80
- passwordMatches := passwordRegexState .Matches (data )
81
-
82
- for _ , connectionStringMatch := range connectionStringMatches {
83
- resConnectionStringMatch := strings .TrimSpace (connectionStringMatch [0 ])
84
-
85
- for _ , resUsernameMatch := range usernameMatches {
55
+ for _ , match := range usernamePat .Matches (data ) {
56
+ uniqueUsernames [match ] = struct {}{}
57
+ }
86
58
87
- for _ , resPasswordMatch := range passwordMatches {
88
- _ , metPasswordRequirements := meetsCouchbasePasswordRequirements (resPasswordMatch )
59
+ for _ , match := range passwordPat .Matches (data ) {
60
+ uniquePasswords [match ] = struct {}{}
61
+ }
89
62
90
- if ! metPasswordRequirements {
63
+ for connString := range uniqueConnStrings {
64
+ for username := range uniqueUsernames {
65
+ for password := range uniquePasswords {
66
+ if ! isValidCouchbasePassword (password ) {
91
67
continue
92
68
}
93
69
94
70
s1 := detectors.Result {
95
71
DetectorType : detectorspb .DetectorType_Couchbase ,
96
- Raw : []byte (fmt . Sprintf ( " %s:%s@%s" , resUsernameMatch , resPasswordMatch , resConnectionStringMatch ) ),
72
+ Raw : fmt . Appendf ( []byte ("" ), " %s:%s@%s" , username , password , connString ),
97
73
}
98
74
99
75
if verify {
100
-
101
- options := gocb.ClusterOptions {
102
- Authenticator : gocb.PasswordAuthenticator {
103
- Username : resUsernameMatch ,
104
- Password : resPasswordMatch ,
105
- },
106
- }
107
-
108
- // Sets a pre-configured profile called "wan-development" to help avoid latency issues
109
- // when accessing Capella from a different Wide Area Network
110
- // or Availability Zone (e.g. your laptop).
111
- if err := options .ApplyProfile (gocb .ClusterConfigProfileWanDevelopment ); err != nil {
112
- continue
113
- }
114
-
115
- // Initialize the Connection
116
- cluster , err := gocb .Connect (resConnectionStringMatch , options )
117
- if err != nil {
118
- continue
119
- }
120
-
121
- // We'll ping the KV nodes in our cluster.
122
- pings , err := cluster .Ping (& gocb.PingOptions {
123
- Timeout : time .Second * 5 ,
124
- })
125
-
126
- if err != nil {
127
- continue
128
- }
129
-
130
- for _ , ping := range pings .Services {
131
- for _ , pingEndpoint := range ping {
132
- if pingEndpoint .State == gocb .PingStateOk {
133
- s1 .Verified = true
134
- break
135
- }
136
- }
137
- }
76
+ isVerified , verificationErr := verifyCouchBase (username , password , connString )
77
+ s1 .Verified = isVerified
78
+ s1 .SetVerificationError (verificationErr )
79
+ s1 .SetPrimarySecretValue (connString )
138
80
}
139
81
140
82
results = append (results , s1 )
@@ -144,10 +86,62 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
144
86
return results , nil
145
87
}
146
88
147
- func (s Scanner ) Type () detectorspb.DetectorType {
148
- return detectorspb .DetectorType_Couchbase
89
+ func verifyCouchBase (username , password , connString string ) (bool , error ) {
90
+ options := gocb.ClusterOptions {
91
+ Authenticator : gocb.PasswordAuthenticator {
92
+ Username : username ,
93
+ Password : password ,
94
+ },
95
+ }
96
+
97
+ // Sets a pre-configured profile called "wan-development" to help avoid latency issues
98
+ // when accessing Capella from a different Wide Area Network
99
+ // or Availability Zone (e.g. your laptop).
100
+ if err := options .ApplyProfile (gocb .ClusterConfigProfileWanDevelopment ); err != nil {
101
+ return false , err
102
+ }
103
+
104
+ // Initialize the Connection
105
+ cluster , err := gocb .Connect (connString , options )
106
+ if err != nil {
107
+ return false , err
108
+ }
109
+
110
+ // We'll ping the KV nodes in our cluster.
111
+ pings , err := cluster .Ping (& gocb.PingOptions {
112
+ Timeout : time .Second * 5 ,
113
+ })
114
+
115
+ if err != nil {
116
+ return false , err
117
+ }
118
+
119
+ for _ , ping := range pings .Services {
120
+ for _ , pingEndpoint := range ping {
121
+ if pingEndpoint .State == gocb .PingStateOk {
122
+ return true , nil
123
+ }
124
+ }
125
+ }
126
+
127
+ return false , nil
149
128
}
150
129
151
- func (s Scanner ) Description () string {
152
- return "Couchbase is a distributed NoSQL cloud database. Couchbase credentials can be used to access and modify data within the Couchbase database."
130
+ func isValidCouchbasePassword (password string ) bool {
131
+ var hasLower , hasUpper , hasNumber , hasSpecialChar bool
132
+
133
+ for _ , r := range password {
134
+ switch {
135
+ case unicode .IsLower (r ):
136
+ hasLower = true
137
+ case unicode .IsUpper (r ):
138
+ hasUpper = true
139
+ case unicode .IsNumber (r ):
140
+ hasNumber = true
141
+ case unicode .IsPunct (r ), unicode .IsSymbol (r ):
142
+ hasSpecialChar = true
143
+ }
144
+ }
145
+
146
+ return hasLower && hasUpper && hasNumber && hasSpecialChar
153
147
}
0 commit comments