Skip to content

Commit 60547d8

Browse files
author
XIANJUN ZHU
committed
feat: enhance cloudant (Yelp#219)
* feat: enhance cloudant * address comment * fix broken buid
1 parent 84a33ff commit 60547d8

File tree

2 files changed

+70
-47
lines changed

2 files changed

+70
-47
lines changed

cloudant.py

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,11 @@ class CloudantDetector(RegexBasedDetector):
1313
secret_type = 'Cloudant Credentials'
1414

1515
# opt means optional
16-
opt_quote = r'(?:"|\'|)'
17-
opt_dashes = r'(?:--|)'
18-
opt_dot = r'(?:\.|)'
1916
dot = r'\.'
20-
cl_account = r'[0-9a-z\-\_]+'
17+
cl_account = r'[\w\-]+'
2118
cl = r'(?:cloudant|cl|clou)'
22-
opt_dash_undrscr = r'(?:_|-|)'
2319
opt_api = r'(?:api|)'
2420
cl_key_or_pass = opt_api + r'(?:key|pwd|pw|password|pass|token)'
25-
opt_space = r'(?: |)'
26-
assignment = r'(?:=|:|:=|=>)'
2721
cl_pw = r'([0-9a-f]{64})'
2822
cl_api_key = r'([a-z]{24})'
2923
colon = r'\:'
@@ -69,7 +63,7 @@ class CloudantDetector(RegexBasedDetector):
6963

7064
def verify(self, token, content, potential_secret=None):
7165

72-
hosts = find_host(content)
66+
hosts = find_account(content)
7367
if not hosts:
7468
return VerifiedResult.UNVERIFIED
7569

@@ -79,20 +73,35 @@ def verify(self, token, content, potential_secret=None):
7973
return VerifiedResult.VERIFIED_FALSE
8074

8175

82-
def find_host(content):
76+
def find_account(content):
8377
opt_hostname_keyword = r'(?:hostname|host|username|id|user|userid|user-id|user-name|' \
84-
'name|user_id|user_name|uname)'
85-
hostname = r'(\w(?:\w|_|-)+)'
78+
'name|user_id|user_name|uname|account)'
79+
account = r'(\w[\w\-]*)'
80+
opt_basic_auth = r'(?:[\w\-:%]*\@)?'
8681

87-
regex = RegexBasedDetector.assign_regex_generator(
88-
prefix_regex=CloudantDetector.cl,
89-
password_keyword_regex=opt_hostname_keyword,
90-
password_regex=hostname,
82+
regexes = (
83+
RegexBasedDetector.assign_regex_generator(
84+
prefix_regex=CloudantDetector.cl,
85+
password_keyword_regex=opt_hostname_keyword,
86+
password_regex=account,
87+
),
88+
re.compile(
89+
r'{http}{opt_basic_auth}{cl_account}{dot}{cloudant_api_url}'.format(
90+
http=CloudantDetector.http,
91+
opt_basic_auth=opt_basic_auth,
92+
cl_account=account,
93+
cl_api_key=CloudantDetector.cl_api_key,
94+
dot=CloudantDetector.dot,
95+
cloudant_api_url=CloudantDetector.cloudant_api_url,
96+
),
97+
flags=re.IGNORECASE,
98+
),
9199
)
92100

93101
return [
94102
match
95103
for line in content.splitlines()
104+
for regex in regexes
96105
for match in regex.findall(line)
97106
]
98107

cloudant_test.py

Lines changed: 46 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
from detect_secrets.core.constants import VerifiedResult
99
from detect_secrets.core.potential_secret import PotentialSecret
1010
from detect_secrets.plugins.cloudant import CloudantDetector
11-
from detect_secrets.plugins.cloudant import find_host
11+
from detect_secrets.plugins.cloudant import find_account
1212

13-
CL_HOST = 'testy_test' # also called user
13+
CL_ACCOUNT = 'testy_-test' # also called user
1414
# only detecting 64 hex CL generated password
1515
CL_PW = 'abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234'
1616

@@ -24,33 +24,33 @@ class TestCloudantDetector(object):
2424
'payload, should_flag',
2525
[
2626
(
27-
'https://{cl_host}:{cl_pw}@{cl_host}.cloudant.com"'.format(
28-
cl_host=CL_HOST, cl_pw=CL_PW,
27+
'https://{cl_account}:{cl_pw}@{cl_account}.cloudant.com"'.format(
28+
cl_account=CL_ACCOUNT, cl_pw=CL_PW,
2929
), True,
3030
),
3131
(
32-
'https://{cl_host}:{cl_pw}@{cl_host}.cloudant.com/_api/v2/'.format(
33-
cl_host=CL_HOST, cl_pw=CL_PW,
32+
'https://{cl_account}:{cl_pw}@{cl_account}.cloudant.com/_api/v2/'.format(
33+
cl_account=CL_ACCOUNT, cl_pw=CL_PW,
3434
), True,
3535
),
3636
(
37-
'https://{cl_host}:{cl_pw}@{cl_host}.cloudant.com/_api/v2/'.format(
38-
cl_host=CL_HOST, cl_pw=CL_PW,
37+
'https://{cl_account}:{cl_pw}@{cl_account}.cloudant.com/_api/v2/'.format(
38+
cl_account=CL_ACCOUNT, cl_pw=CL_PW,
3939
), True,
4040
),
4141
(
42-
'https://{cl_host}:{cl_pw}@{cl_host}.cloudant.com'.format(
43-
cl_host=CL_HOST, cl_pw=CL_PW,
42+
'https://{cl_account}:{cl_pw}@{cl_account}.cloudant.com'.format(
43+
cl_account=CL_ACCOUNT, cl_pw=CL_PW,
4444
), True,
4545
),
4646
(
47-
'https://{cl_host}:{cl_api_key}@{cl_host}.cloudant.com'.format(
48-
cl_host=CL_HOST, cl_api_key=CL_API_KEY,
47+
'https://{cl_account}:{cl_api_key}@{cl_account}.cloudant.com'.format(
48+
cl_account=CL_ACCOUNT, cl_api_key=CL_API_KEY,
4949
), True,
5050
),
5151
(
52-
'https://{cl_host}:{cl_pw}.cloudant.com'.format(
53-
cl_host=CL_HOST, cl_pw=CL_PW,
52+
'https://{cl_account}:{cl_pw}.cloudant.com'.format(
53+
cl_account=CL_ACCOUNT, cl_pw=CL_PW,
5454
), False,
5555
),
5656
('cloudant_password=\'{cl_pw}\''.format(cl_pw=CL_PW), True),
@@ -70,8 +70,8 @@ def test_analyze_string(self, payload, should_flag):
7070

7171
@responses.activate
7272
def test_verify_invalid_secret(self):
73-
cl_api_url = 'https://{cl_host}:{cl_pw}@{cl_host}.cloudant.com'.format(
74-
cl_host=CL_HOST, cl_pw=CL_PW,
73+
cl_api_url = 'https://{cl_account}:{cl_pw}@{cl_account}.cloudant.com'.format(
74+
cl_account=CL_ACCOUNT, cl_pw=CL_PW,
7575
)
7676
responses.add(
7777
responses.GET, cl_api_url,
@@ -80,13 +80,13 @@ def test_verify_invalid_secret(self):
8080

8181
assert CloudantDetector().verify(
8282
CL_PW,
83-
'cloudant_host={}'.format(CL_HOST),
83+
'cloudant_host={}'.format(CL_ACCOUNT),
8484
) == VerifiedResult.VERIFIED_FALSE
8585

8686
@responses.activate
8787
def test_verify_valid_secret(self):
88-
cl_api_url = 'https://{cl_host}:{cl_pw}@{cl_host}.cloudant.com'.format(
89-
cl_host=CL_HOST, cl_pw=CL_PW,
88+
cl_api_url = 'https://{cl_account}:{cl_pw}@{cl_account}.cloudant.com'.format(
89+
cl_account=CL_ACCOUNT, cl_pw=CL_PW,
9090
)
9191
responses.add(
9292
responses.GET, cl_api_url,
@@ -95,22 +95,22 @@ def test_verify_valid_secret(self):
9595
potential_secret = PotentialSecret('test cloudant', 'test filename', CL_PW)
9696
assert CloudantDetector().verify(
9797
CL_PW,
98-
'cloudant_host={}'.format(CL_HOST),
98+
'cloudant_host={}'.format(CL_ACCOUNT),
9999
potential_secret,
100100
) == VerifiedResult.VERIFIED_TRUE
101-
assert potential_secret.other_factors['hostname'] == CL_HOST
101+
assert potential_secret.other_factors['hostname'] == CL_ACCOUNT
102102

103103
@responses.activate
104104
def test_verify_unverified_secret(self):
105105
assert CloudantDetector().verify(
106106
CL_PW,
107-
'cloudant_host={}'.format(CL_HOST),
107+
'cloudant_host={}'.format(CL_ACCOUNT),
108108
) == VerifiedResult.UNVERIFIED
109109

110110
def test_verify_no_secret(self):
111111
assert CloudantDetector().verify(
112112
CL_PW,
113-
'no_un={}'.format(CL_HOST),
113+
'no_un={}'.format(CL_ACCOUNT),
114114
) == VerifiedResult.UNVERIFIED
115115

116116
@pytest.mark.parametrize(
@@ -120,19 +120,19 @@ def test_verify_no_secret(self):
120120
textwrap.dedent("""
121121
--cloudant-hostname = {}
122122
""")[1:-1].format(
123-
CL_HOST,
123+
CL_ACCOUNT,
124124
),
125-
[CL_HOST],
125+
[CL_ACCOUNT],
126126
),
127127
128128
# With quotes
129129
(
130130
textwrap.dedent("""
131-
cl_host = "{}"
131+
cl_account = "{}"
132132
""")[1:-1].format(
133-
CL_HOST,
133+
CL_ACCOUNT,
134134
),
135-
[CL_HOST],
135+
[CL_ACCOUNT],
136136
),
137137
138138
# multiple candidates
@@ -143,19 +143,33 @@ def test_verify_no_secret(self):
143143
CLOUDANT_USERID = '{}'
144144
cloudant-uname: {}
145145
""")[1:-1].format(
146-
CL_HOST,
146+
CL_ACCOUNT,
147147
'test2_testy_test',
148148
'test3-testy-testy',
149149
'notanemail',
150150
),
151151
[
152-
CL_HOST,
152+
CL_ACCOUNT,
153153
'test2_testy_test',
154154
'test3-testy-testy',
155155
'notanemail',
156156
],
157157
),
158+
159+
# In URL
160+
(
161+
'https://{cl_account}:{cl_api_key}@{cl_account}.cloudant.com'.format(
162+
cl_account=CL_ACCOUNT, cl_api_key=CL_API_KEY,
163+
),
164+
[CL_ACCOUNT],
165+
),
166+
(
167+
'https://{cl_account}.cloudant.com'.format(
168+
cl_account=CL_ACCOUNT,
169+
),
170+
[CL_ACCOUNT],
171+
),
158172
),
159173
)
160-
def test_find_host(self, content, expected_output):
161-
assert find_host(content) == expected_output
174+
def test_find_account(self, content, expected_output):
175+
assert find_account(content) == expected_output

0 commit comments

Comments
 (0)