Skip to content

Commit 6614060

Browse files
committed
tests: update and expand testing for classification and detect_ast_secrets functions
1 parent e8d6b9a commit 6614060

2 files changed

Lines changed: 208 additions & 14 deletions

File tree

tests/test_ast.py

Lines changed: 96 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,90 @@
11
from detectors.find_secrets import detect_ast_secrets
22

3-
# Basic detection of a simple variable assignment
3+
4+
# Detect a basic hardcoded password assignment
45
def test_ast_basic_password():
56
code = 'password = "abcdef"'
67
result = detect_ast_secrets(code)
78

89
assert result == [(1, "Password", "HIGH", "abcdef")]
910

1011

11-
# Detect secrets assigned via object attributes (e.g., self.password)
12+
# Detect password assigned through a simple object attribute
1213
def test_ast_attribute_password():
1314
code = 'self.password = "abcdef"'
1415
result = detect_ast_secrets(code)
1516

1617
assert result == [(1, "Password", "HIGH", "abcdef")]
1718

1819

19-
# Detect API key pattern from attribute-based assignment
20+
# Detect API key assigned through an attribute
2021
def test_ast_api_key():
2122
code = 'config.api_key = "12345678"'
2223
result = detect_ast_secrets(code)
2324

2425
assert result == [(1, "API Key", "HIGH", "12345678")]
2526

2627

27-
# Detect token with correct severity classification
28+
# Detect token with correct MEDIUM severity
2829
def test_ast_token():
2930
code = 'user.token = "qwerty123"'
3031
result = detect_ast_secrets(code)
3132

3233
assert result == [(1, "Token", "MEDIUM", "qwerty123")]
3334

3435

35-
# Ensure non-string assignments are ignored
36+
# Detect generic secret assignment
37+
def test_ast_secret():
38+
code = 'client_secret = "abcdef"'
39+
result = detect_ast_secrets(code)
40+
41+
assert result == [(1, "Secret", "MEDIUM", "abcdef")]
42+
43+
44+
# Detect AWS access key by value, regardless of variable name
45+
def test_ast_aws_access_key_value():
46+
code = 'random_var = "AKIAEXAMPLE123456789"'
47+
result = detect_ast_secrets(code)
48+
49+
assert result == [(1, "AWS Access Key", "HIGH", "AKIAEXAMPLE123456789")]
50+
51+
52+
# Detect multiple classifications when both value and variable name match
53+
def test_ast_aws_access_key_with_api_key_variable():
54+
code = 'api_key = "AKIAEXAMPLE123456789"'
55+
result = detect_ast_secrets(code)
56+
57+
assert result == [
58+
(1, "AWS Access Key", "HIGH", "AKIAEXAMPLE123456789"),
59+
(1, "API Key", "HIGH", "AKIAEXAMPLE123456789"),
60+
]
61+
62+
63+
# Ignore non-string assignments
3664
def test_ast_non_string():
3765
code = 'password = 123456'
3866
result = detect_ast_secrets(code)
3967

4068
assert result == []
4169

4270

43-
# Ensure unrelated variable names are not flagged
71+
# Ignore unrelated variable names with string values
4472
def test_ast_irrelevant_variable():
4573
code = 'username = "abcdef"'
4674
result = detect_ast_secrets(code)
4775

4876
assert result == []
4977

5078

51-
# Validate detection across multiple assignments with correct line numbers
79+
# Ignore short values below the rule threshold
80+
def test_ast_short_value():
81+
code = 'password = "abc"'
82+
result = detect_ast_secrets(code)
83+
84+
assert result == []
85+
86+
87+
# Validate multiple assignments and correct line numbers
5288
def test_ast_multiple_assignments():
5389
code = '''
5490
password = "abcdef"
@@ -60,37 +96,83 @@ def test_ast_multiple_assignments():
6096
assert result == [
6197
(2, "Password", "HIGH", "abcdef"),
6298
(3, "API Key", "HIGH", "12345678"),
63-
(4, "Token", "MEDIUM", "qwerty123")
99+
(4, "Token", "MEDIUM", "qwerty123"),
64100
]
65101

66102

67-
# Ensure syntax errors are handled gracefully without crashing
103+
# Handle syntax errors gracefully without crashing
68104
def test_ast_syntax_error():
69-
code = 'password = "abc'
105+
code = 'password = "abc'
70106
result = detect_ast_secrets(code)
71107

72108
assert result == []
73109

74110

75-
# Ensure case-insensitive variable matching
111+
# Detect uppercase variable names through case-insensitive rules
76112
def test_ast_uppercase_variable():
77113
code = 'PASSWORD = "abcdef"'
78114
result = detect_ast_secrets(code)
79115

80116
assert result == [(1, "Password", "HIGH", "abcdef")]
81117

82118

83-
# Ensure multiple assignment targets are handled correctly
119+
# Process multiple assignment targets correctly
84120
def test_ast_multiple_targets():
85121
code = 'a = password = "abcdef"'
86122
result = detect_ast_secrets(code)
87123

88124
assert result == [(1, "Password", "HIGH", "abcdef")]
89125

90126

91-
# Validate detection within deeply nested attribute chains
127+
# Process multiple sensitive targets assigned the same value
128+
def test_ast_multiple_sensitive_targets():
129+
code = 'password = token = "abcdef"'
130+
result = detect_ast_secrets(code)
131+
132+
assert result == [
133+
(1, "Password", "HIGH", "abcdef"),
134+
(1, "Token", "MEDIUM", "abcdef"),
135+
]
136+
137+
138+
# Detect secrets in deeply nested attribute chains
92139
def test_ast_nested_attribute():
93140
code = 'self.config.db.password = "abcdef"'
94141
result = detect_ast_secrets(code)
95142

96-
assert result == [(1, "Password", "HIGH", "abcdef")]
143+
assert result == [(1, "Password", "HIGH", "abcdef")]
144+
145+
146+
# Detect API keys in deeply nested attribute chains
147+
def test_ast_deep_nested_api_key():
148+
code = 'settings.auth.credentials.api_key = "12345678"'
149+
result = detect_ast_secrets(code)
150+
151+
assert result == [(1, "API Key", "HIGH", "12345678")]
152+
153+
154+
# Ignore unsupported assignment targets such as subscript assignments
155+
def test_ast_unsupported_subscript_target():
156+
code = 'config["password"] = "abcdef"'
157+
result = detect_ast_secrets(code)
158+
159+
assert result == []
160+
161+
162+
# Preserve complex string values with special characters
163+
def test_ast_complex_password_value():
164+
code = 'password = "abc_def-123#$%^&*()"'
165+
result = detect_ast_secrets(code)
166+
167+
assert result == [(1, "Password", "HIGH", "abc_def-123#$%^&*()")]
168+
169+
170+
# Handle indented multiline code by normalizing indentation
171+
def test_ast_dedented_multiline_code():
172+
code = '''
173+
password = "abcdef"
174+
username = "notsecret"
175+
'''
176+
result = detect_ast_secrets(code)
177+
178+
assert result == [(2, "Password", "HIGH", "abcdef")]

tests/test_detect_from_parts.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
from detectors.find_secrets import detect_from_parts
2+
3+
4+
# Detect password variable with valid value length
5+
def test_password_match():
6+
result = detect_from_parts("password", "abcdef")
7+
assert result == [("Password", "HIGH", "abcdef")]
8+
9+
10+
# Detect password alias: pwd
11+
def test_pwd_alias_match():
12+
result = detect_from_parts("pwd", "abcdef")
13+
assert result == [("Password", "HIGH", "abcdef")]
14+
15+
16+
# Detect password alias: passwd
17+
def test_passwd_alias_match():
18+
result = detect_from_parts("passwd", "abcdef")
19+
assert result == [("Password", "HIGH", "abcdef")]
20+
21+
22+
# Ensure similar but unrelated variable names are not flagged
23+
def test_no_match():
24+
result = detect_from_parts("username", "abcdef")
25+
assert result is None
26+
27+
28+
# Ensure values below minimum length threshold are ignored
29+
def test_short_password_value():
30+
result = detect_from_parts("password", "abc")
31+
assert result is None
32+
33+
34+
# Ensure values exactly at minimum length are accepted
35+
def test_min_length_boundary_match():
36+
result = detect_from_parts("password", "abcd")
37+
assert result == [("Password", "HIGH", "abcd")]
38+
39+
40+
# Ensure case-insensitive variable matching
41+
def test_uppercase_variable_match():
42+
result = detect_from_parts("PASSWORD", "abcdef")
43+
assert result == [("Password", "HIGH", "abcdef")]
44+
45+
46+
# Detect API key variable with underscore
47+
def test_api_key_match():
48+
result = detect_from_parts("api_key", "1234abcd")
49+
assert result == [("API Key", "HIGH", "1234abcd")]
50+
51+
52+
# Detect API key variable without underscore
53+
def test_apikey_match():
54+
result = detect_from_parts("apikey", "1234abcd")
55+
assert result == [("API Key", "HIGH", "1234abcd")]
56+
57+
58+
# Detect token with medium severity
59+
def test_token_match():
60+
result = detect_from_parts("token", "qwerty")
61+
assert result == [("Token", "MEDIUM", "qwerty")]
62+
63+
64+
# Detect secret with medium severity
65+
def test_secret_match():
66+
result = detect_from_parts("secret", "abcdef")
67+
assert result == [("Secret", "MEDIUM", "abcdef")]
68+
69+
70+
# Detect secret when embedded in a longer variable name
71+
def test_secret_embedded_variable_match():
72+
result = detect_from_parts("client_secret", "abcdef")
73+
assert result == [("Secret", "MEDIUM", "abcdef")]
74+
75+
76+
# Detect AWS access key by value, regardless of variable name
77+
def test_aws_access_key_value_match():
78+
result = detect_from_parts("random_var", "AKIAEXAMPLE123456789")
79+
assert result == [("AWS Access Key", "HIGH", "AKIAEXAMPLE123456789")]
80+
81+
82+
# Ensure lowercase AWS-style value does not match case-sensitive AWS pattern
83+
def test_lowercase_aws_access_key_no_match():
84+
result = detect_from_parts("random_var", "akiaexample123456789")
85+
assert result is None
86+
87+
88+
# Ensure AWS access key can produce multiple classifications when variable also matches
89+
def test_aws_access_key_with_api_key_variable():
90+
result = detect_from_parts("api_key", "AKIAEXAMPLE123456789")
91+
assert result == [
92+
("AWS Access Key", "HIGH", "AKIAEXAMPLE123456789"),
93+
("API Key", "HIGH", "AKIAEXAMPLE123456789"),
94+
]
95+
96+
97+
# Ensure complex values with special characters are preserved
98+
def test_complex_password_value_match():
99+
result = detect_from_parts("password", "abc_def-123#$%^&*()")
100+
assert result == [("Password", "HIGH", "abc_def-123#$%^&*()")]
101+
102+
103+
# Ensure empty variable name does not trigger variable-based rules
104+
def test_empty_variable_name():
105+
result = detect_from_parts("", "abcdef")
106+
assert result is None
107+
108+
109+
# Ensure empty value does not trigger length-based rules
110+
def test_empty_value():
111+
result = detect_from_parts("password", "")
112+
assert result is None

0 commit comments

Comments
 (0)