Skip to content

Commit 4ab6009

Browse files
XIANJUN ZHUjustineyster
XIANJUN ZHU
authored andcommitted
feat: enhance cloudant (Yelp#219)
* feat: enhance cloudant * address comment * fix broken buid
1 parent 99f2b42 commit 4ab6009

File tree

2 files changed

+70
-47
lines changed

2 files changed

+70
-47
lines changed

detect_secrets/plugins/cloudant.py

+24-15
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,11 @@ class CloudantDetector(RegexBasedDetector):
1111
secret_type = 'Cloudant Credentials'
1212

1313
# opt means optional
14-
opt_quote = r'(?:"|\'|)'
15-
opt_dashes = r'(?:--|)'
16-
opt_dot = r'(?:\.|)'
1714
dot = r'\.'
18-
cl_account = r'[0-9a-z\-\_]+'
15+
cl_account = r'[\w\-]+'
1916
cl = r'(?:cloudant|cl|clou)'
20-
opt_dash_undrscr = r'(?:_|-|)'
2117
opt_api = r'(?:api|)'
2218
cl_key_or_pass = opt_api + r'(?:key|pwd|pw|password|pass|token)'
23-
opt_space = r'(?: |)'
24-
assignment = r'(?:=|:|:=|=>)'
2519
cl_pw = r'([0-9a-f]{64})'
2620
cl_api_key = r'([a-z]{24})'
2721
colon = r'\:'
@@ -67,7 +61,7 @@ class CloudantDetector(RegexBasedDetector):
6761

6862
def verify(self, token, content, potential_secret=None):
6963

70-
hosts = find_host(content)
64+
hosts = find_account(content)
7165
if not hosts:
7266
return VerifiedResult.UNVERIFIED
7367

@@ -77,20 +71,35 @@ def verify(self, token, content, potential_secret=None):
7771
return VerifiedResult.VERIFIED_FALSE
7872

7973

80-
def find_host(content):
74+
def find_account(content):
8175
opt_hostname_keyword = r'(?:hostname|host|username|id|user|userid|user-id|user-name|' \
82-
'name|user_id|user_name|uname)'
83-
hostname = r'(\w(?:\w|_|-)+)'
76+
'name|user_id|user_name|uname|account)'
77+
account = r'(\w[\w\-]*)'
78+
opt_basic_auth = r'(?:[\w\-:%]*\@)?'
8479

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

9199
return [
92100
match
93101
for line in content.splitlines()
102+
for regex in regexes
94103
for match in regex.findall(line)
95104
]
96105

tests/plugins/cloudant_test.py

+46-32
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
from detect_secrets.core.constants import VerifiedResult
77
from detect_secrets.core.potential_secret import PotentialSecret
88
from detect_secrets.plugins.cloudant import CloudantDetector
9-
from detect_secrets.plugins.cloudant import find_host
9+
from detect_secrets.plugins.cloudant import find_account
1010

11-
CL_HOST = 'testy_test' # also called user
11+
CL_ACCOUNT = 'testy_-test' # also called user
1212
# only detecting 64 hex CL generated password
1313
CL_PW = 'abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234'
1414

@@ -22,33 +22,33 @@ class TestCloudantDetector:
2222
'payload, should_flag',
2323
[
2424
(
25-
'https://{cl_host}:{cl_pw}@{cl_host}.cloudant.com"'.format(
26-
cl_host=CL_HOST, cl_pw=CL_PW,
25+
'https://{cl_account}:{cl_pw}@{cl_account}.cloudant.com"'.format(
26+
cl_account=CL_ACCOUNT, cl_pw=CL_PW,
2727
), True,
2828
),
2929
(
30-
'https://{cl_host}:{cl_pw}@{cl_host}.cloudant.com/_api/v2/'.format(
31-
cl_host=CL_HOST, cl_pw=CL_PW,
30+
'https://{cl_account}:{cl_pw}@{cl_account}.cloudant.com/_api/v2/'.format(
31+
cl_account=CL_ACCOUNT, cl_pw=CL_PW,
3232
), True,
3333
),
3434
(
35-
'https://{cl_host}:{cl_pw}@{cl_host}.cloudant.com/_api/v2/'.format(
36-
cl_host=CL_HOST, cl_pw=CL_PW,
35+
'https://{cl_account}:{cl_pw}@{cl_account}.cloudant.com/_api/v2/'.format(
36+
cl_account=CL_ACCOUNT, cl_pw=CL_PW,
3737
), True,
3838
),
3939
(
40-
'https://{cl_host}:{cl_pw}@{cl_host}.cloudant.com'.format(
41-
cl_host=CL_HOST, cl_pw=CL_PW,
40+
'https://{cl_account}:{cl_pw}@{cl_account}.cloudant.com'.format(
41+
cl_account=CL_ACCOUNT, cl_pw=CL_PW,
4242
), True,
4343
),
4444
(
45-
'https://{cl_host}:{cl_api_key}@{cl_host}.cloudant.com'.format(
46-
cl_host=CL_HOST, cl_api_key=CL_API_KEY,
45+
'https://{cl_account}:{cl_api_key}@{cl_account}.cloudant.com'.format(
46+
cl_account=CL_ACCOUNT, cl_api_key=CL_API_KEY,
4747
), True,
4848
),
4949
(
50-
'https://{cl_host}:{cl_pw}.cloudant.com'.format(
51-
cl_host=CL_HOST, cl_pw=CL_PW,
50+
'https://{cl_account}:{cl_pw}.cloudant.com'.format(
51+
cl_account=CL_ACCOUNT, cl_pw=CL_PW,
5252
), False,
5353
),
5454
('cloudant_password=\'{cl_pw}\''.format(cl_pw=CL_PW), True),
@@ -68,8 +68,8 @@ def test_analyze_string(self, payload, should_flag):
6868

6969
@responses.activate
7070
def test_verify_invalid_secret(self):
71-
cl_api_url = 'https://{cl_host}:{cl_pw}@{cl_host}.cloudant.com'.format(
72-
cl_host=CL_HOST, cl_pw=CL_PW,
71+
cl_api_url = 'https://{cl_account}:{cl_pw}@{cl_account}.cloudant.com'.format(
72+
cl_account=CL_ACCOUNT, cl_pw=CL_PW,
7373
)
7474
responses.add(
7575
responses.GET, cl_api_url,
@@ -78,13 +78,13 @@ def test_verify_invalid_secret(self):
7878

7979
assert CloudantDetector().verify(
8080
CL_PW,
81-
'cloudant_host={}'.format(CL_HOST),
81+
'cloudant_host={}'.format(CL_ACCOUNT),
8282
) == VerifiedResult.VERIFIED_FALSE
8383

8484
@responses.activate
8585
def test_verify_valid_secret(self):
86-
cl_api_url = 'https://{cl_host}:{cl_pw}@{cl_host}.cloudant.com'.format(
87-
cl_host=CL_HOST, cl_pw=CL_PW,
86+
cl_api_url = 'https://{cl_account}:{cl_pw}@{cl_account}.cloudant.com'.format(
87+
cl_account=CL_ACCOUNT, cl_pw=CL_PW,
8888
)
8989
responses.add(
9090
responses.GET, cl_api_url,
@@ -93,22 +93,22 @@ def test_verify_valid_secret(self):
9393
potential_secret = PotentialSecret('test cloudant', 'test filename', CL_PW)
9494
assert CloudantDetector().verify(
9595
CL_PW,
96-
'cloudant_host={}'.format(CL_HOST),
96+
'cloudant_host={}'.format(CL_ACCOUNT),
9797
potential_secret,
9898
) == VerifiedResult.VERIFIED_TRUE
99-
assert potential_secret.other_factors['hostname'] == CL_HOST
99+
assert potential_secret.other_factors['hostname'] == CL_ACCOUNT
100100

101101
@responses.activate
102102
def test_verify_unverified_secret(self):
103103
assert CloudantDetector().verify(
104104
CL_PW,
105-
'cloudant_host={}'.format(CL_HOST),
105+
'cloudant_host={}'.format(CL_ACCOUNT),
106106
) == VerifiedResult.UNVERIFIED
107107

108108
def test_verify_no_secret(self):
109109
assert CloudantDetector().verify(
110110
CL_PW,
111-
'no_un={}'.format(CL_HOST),
111+
'no_un={}'.format(CL_ACCOUNT),
112112
) == VerifiedResult.UNVERIFIED
113113

114114
@pytest.mark.parametrize(
@@ -118,19 +118,19 @@ def test_verify_no_secret(self):
118118
textwrap.dedent("""
119119
--cloudant-hostname = {}
120120
""")[1:-1].format(
121-
CL_HOST,
121+
CL_ACCOUNT,
122122
),
123-
[CL_HOST],
123+
[CL_ACCOUNT],
124124
),
125125
126126
# With quotes
127127
(
128128
textwrap.dedent("""
129-
cl_host = "{}"
129+
cl_account = "{}"
130130
""")[1:-1].format(
131-
CL_HOST,
131+
CL_ACCOUNT,
132132
),
133-
[CL_HOST],
133+
[CL_ACCOUNT],
134134
),
135135
136136
# multiple candidates
@@ -141,19 +141,33 @@ def test_verify_no_secret(self):
141141
CLOUDANT_USERID = '{}'
142142
cloudant-uname: {}
143143
""")[1:-1].format(
144-
CL_HOST,
144+
CL_ACCOUNT,
145145
'test2_testy_test',
146146
'test3-testy-testy',
147147
'notanemail',
148148
),
149149
[
150-
CL_HOST,
150+
CL_ACCOUNT,
151151
'test2_testy_test',
152152
'test3-testy-testy',
153153
'notanemail',
154154
],
155155
),
156+
157+
# In URL
158+
(
159+
'https://{cl_account}:{cl_api_key}@{cl_account}.cloudant.com'.format(
160+
cl_account=CL_ACCOUNT, cl_api_key=CL_API_KEY,
161+
),
162+
[CL_ACCOUNT],
163+
),
164+
(
165+
'https://{cl_account}.cloudant.com'.format(
166+
cl_account=CL_ACCOUNT,
167+
),
168+
[CL_ACCOUNT],
169+
),
156170
),
157171
)
158-
def test_find_host(self, content, expected_output):
159-
assert find_host(content) == expected_output
172+
def test_find_account(self, content, expected_output):
173+
assert find_account(content) == expected_output

0 commit comments

Comments
 (0)