Skip to content

Commit eee9307

Browse files
committed
tests: update tests to include reasons
1 parent 419c64d commit eee9307

3 files changed

Lines changed: 78 additions & 35 deletions

File tree

tests/test_apply_rules.py

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,33 @@
11
from detectors.rule_engine import apply_rules
22

33

4+
PASSWORD_REASON = (
5+
"variable name matched password/pwd/passwd pattern and value met minimum length"
6+
)
7+
API_KEY_REASON = (
8+
"variable name matched api_key/apikey pattern and value met minimum length"
9+
)
10+
TOKEN_REASON = "variable name matched token pattern and value met minimum length"
11+
SECRET_REASON = "variable name matched secret pattern and value met minimum length"
12+
AWS_REASON = "value matched AKIA-prefixed AWS access key pattern"
13+
14+
415
# Detect password variable with valid value length
516
def test_password_match():
617
result = apply_rules("password", "abcdef")
7-
assert result == [("Password", "HIGH", "abcdef")]
18+
assert result == [("Password", "HIGH", "abcdef", PASSWORD_REASON)]
819

920

1021
# Detect password alias: pwd
1122
def test_pwd_alias_match():
1223
result = apply_rules("pwd", "abcdef")
13-
assert result == [("Password", "HIGH", "abcdef")]
24+
assert result == [("Password", "HIGH", "abcdef", PASSWORD_REASON)]
1425

1526

1627
# Detect password alias: passwd
1728
def test_passwd_alias_match():
1829
result = apply_rules("passwd", "abcdef")
19-
assert result == [("Password", "HIGH", "abcdef")]
30+
assert result == [("Password", "HIGH", "abcdef", PASSWORD_REASON)]
2031

2132

2233
# Ensure similar but unrelated variable names are not flagged
@@ -34,49 +45,49 @@ def test_short_password_value():
3445
# Ensure values exactly at minimum length are accepted
3546
def test_min_length_boundary_match():
3647
result = apply_rules("password", "abcd")
37-
assert result == [("Password", "HIGH", "abcd")]
48+
assert result == [("Password", "HIGH", "abcd", PASSWORD_REASON)]
3849

3950

4051
# Ensure case-insensitive variable matching
4152
def test_uppercase_variable_match():
4253
result = apply_rules("PASSWORD", "abcdef")
43-
assert result == [("Password", "HIGH", "abcdef")]
54+
assert result == [("Password", "HIGH", "abcdef", PASSWORD_REASON)]
4455

4556

4657
# Detect API key variable with underscore
4758
def test_api_key_match():
4859
result = apply_rules("api_key", "1234abcd")
49-
assert result == [("API Key", "HIGH", "1234abcd")]
60+
assert result == [("API Key", "HIGH", "1234abcd", API_KEY_REASON)]
5061

5162

5263
# Detect API key variable without underscore
5364
def test_apikey_match():
5465
result = apply_rules("apikey", "1234abcd")
55-
assert result == [("API Key", "HIGH", "1234abcd")]
66+
assert result == [("API Key", "HIGH", "1234abcd", API_KEY_REASON)]
5667

5768

5869
# Detect token with medium severity
5970
def test_token_match():
6071
result = apply_rules("token", "qwerty")
61-
assert result == [("Token", "MEDIUM", "qwerty")]
72+
assert result == [("Token", "MEDIUM", "qwerty", TOKEN_REASON)]
6273

6374

6475
# Detect secret with medium severity
6576
def test_secret_match():
6677
result = apply_rules("secret", "abcdef")
67-
assert result == [("Secret", "MEDIUM", "abcdef")]
78+
assert result == [("Secret", "MEDIUM", "abcdef", SECRET_REASON)]
6879

6980

7081
# Detect secret when embedded in a longer variable name
7182
def test_secret_embedded_variable_match():
7283
result = apply_rules("client_secret", "abcdef")
73-
assert result == [("Secret", "MEDIUM", "abcdef")]
84+
assert result == [("Secret", "MEDIUM", "abcdef", SECRET_REASON)]
7485

7586

7687
# Detect AWS access key by value, regardless of variable name
7788
def test_aws_access_key_value_match():
7889
result = apply_rules("random_var", "AKIAEXAMPLE123456789")
79-
assert result == [("AWS Access Key", "HIGH", "AKIAEXAMPLE123456789")]
90+
assert result == [("AWS Access Key", "HIGH", "AKIAEXAMPLE123456789", AWS_REASON)]
8091

8192

8293
# Ensure lowercase AWS-style value does not match case-sensitive AWS pattern
@@ -89,15 +100,17 @@ def test_lowercase_aws_access_key_no_match():
89100
def test_aws_access_key_with_api_key_variable():
90101
result = apply_rules("api_key", "AKIAEXAMPLE123456789")
91102
assert result == [
92-
("AWS Access Key", "HIGH", "AKIAEXAMPLE123456789"),
93-
("API Key", "HIGH", "AKIAEXAMPLE123456789"),
103+
("AWS Access Key", "HIGH", "AKIAEXAMPLE123456789", AWS_REASON),
104+
("API Key", "HIGH", "AKIAEXAMPLE123456789", API_KEY_REASON),
94105
]
95106

96107

97108
# Ensure complex values with special characters are preserved
98109
def test_complex_password_value_match():
99110
result = apply_rules("password", "abc_def-123#$%^&*()")
100-
assert result == [("Password", "HIGH", "abc_def-123#$%^&*()")]
111+
assert result == [
112+
("Password", "HIGH", "abc_def-123#$%^&*()", PASSWORD_REASON)
113+
]
101114

102115

103116
# Ensure empty variable name does not trigger variable-based rules

tests/test_ast.py

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,63 @@
11
from detectors.find_secrets import detect_ast_secrets
22

33

4+
PASSWORD_REASON = (
5+
"variable name matched password/pwd/passwd pattern and value met minimum length"
6+
)
7+
API_KEY_REASON = (
8+
"variable name matched api_key/apikey pattern and value met minimum length"
9+
)
10+
TOKEN_REASON = "variable name matched token pattern and value met minimum length"
11+
SECRET_REASON = "variable name matched secret pattern and value met minimum length"
12+
AWS_REASON = "value matched AKIA-prefixed AWS access key pattern"
13+
14+
415
# Detect a basic hardcoded password assignment
516
def test_ast_basic_password():
617
code = 'password = "abcdef"'
718
result = detect_ast_secrets(code)
819

9-
assert result == [(1, "Password", "HIGH", "abcdef")]
20+
assert result == [(1, "Password", "HIGH", "abcdef", PASSWORD_REASON)]
1021

1122

1223
# Detect password assigned through a simple object attribute
1324
def test_ast_attribute_password():
1425
code = 'self.password = "abcdef"'
1526
result = detect_ast_secrets(code)
1627

17-
assert result == [(1, "Password", "HIGH", "abcdef")]
28+
assert result == [(1, "Password", "HIGH", "abcdef", PASSWORD_REASON)]
1829

1930

2031
# Detect API key assigned through an attribute
2132
def test_ast_api_key():
2233
code = 'config.api_key = "12345678"'
2334
result = detect_ast_secrets(code)
2435

25-
assert result == [(1, "API Key", "HIGH", "12345678")]
36+
assert result == [(1, "API Key", "HIGH", "12345678", API_KEY_REASON)]
2637

2738

2839
# Detect token with correct MEDIUM severity
2940
def test_ast_token():
3041
code = 'user.token = "qwerty123"'
3142
result = detect_ast_secrets(code)
3243

33-
assert result == [(1, "Token", "MEDIUM", "qwerty123")]
44+
assert result == [(1, "Token", "MEDIUM", "qwerty123", TOKEN_REASON)]
3445

3546

3647
# Detect generic secret assignment
3748
def test_ast_secret():
3849
code = 'client_secret = "abcdef"'
3950
result = detect_ast_secrets(code)
4051

41-
assert result == [(1, "Secret", "MEDIUM", "abcdef")]
52+
assert result == [(1, "Secret", "MEDIUM", "abcdef", SECRET_REASON)]
4253

4354

4455
# Detect AWS access key by value, regardless of variable name
4556
def test_ast_aws_access_key_value():
4657
code = 'random_var = "AKIAEXAMPLE123456789"'
4758
result = detect_ast_secrets(code)
4859

49-
assert result == [(1, "AWS Access Key", "HIGH", "AKIAEXAMPLE123456789")]
60+
assert result == [(1, "AWS Access Key", "HIGH", "AKIAEXAMPLE123456789", AWS_REASON)]
5061

5162

5263
# Detect multiple classifications when both value and variable name match
@@ -55,8 +66,8 @@ def test_ast_aws_access_key_with_api_key_variable():
5566
result = detect_ast_secrets(code)
5667

5768
assert result == [
58-
(1, "AWS Access Key", "HIGH", "AKIAEXAMPLE123456789"),
59-
(1, "API Key", "HIGH", "AKIAEXAMPLE123456789"),
69+
(1, "AWS Access Key", "HIGH", "AKIAEXAMPLE123456789", AWS_REASON),
70+
(1, "API Key", "HIGH", "AKIAEXAMPLE123456789", API_KEY_REASON),
6071
]
6172

6273

@@ -94,9 +105,9 @@ def test_ast_multiple_assignments():
94105
result = detect_ast_secrets(code)
95106

96107
assert result == [
97-
(2, "Password", "HIGH", "abcdef"),
98-
(3, "API Key", "HIGH", "12345678"),
99-
(4, "Token", "MEDIUM", "qwerty123"),
108+
(2, "Password", "HIGH", "abcdef", PASSWORD_REASON),
109+
(3, "API Key", "HIGH", "12345678", API_KEY_REASON),
110+
(4, "Token", "MEDIUM", "qwerty123", TOKEN_REASON),
100111
]
101112

102113

@@ -113,15 +124,15 @@ def test_ast_uppercase_variable():
113124
code = 'PASSWORD = "abcdef"'
114125
result = detect_ast_secrets(code)
115126

116-
assert result == [(1, "Password", "HIGH", "abcdef")]
127+
assert result == [(1, "Password", "HIGH", "abcdef", PASSWORD_REASON)]
117128

118129

119130
# Process multiple assignment targets correctly
120131
def test_ast_multiple_targets():
121132
code = 'a = password = "abcdef"'
122133
result = detect_ast_secrets(code)
123134

124-
assert result == [(1, "Password", "HIGH", "abcdef")]
135+
assert result == [(1, "Password", "HIGH", "abcdef", PASSWORD_REASON)]
125136

126137

127138
# Process multiple sensitive targets assigned the same value
@@ -130,8 +141,8 @@ def test_ast_multiple_sensitive_targets():
130141
result = detect_ast_secrets(code)
131142

132143
assert result == [
133-
(1, "Password", "HIGH", "abcdef"),
134-
(1, "Token", "MEDIUM", "abcdef"),
144+
(1, "Password", "HIGH", "abcdef", PASSWORD_REASON),
145+
(1, "Token", "MEDIUM", "abcdef", TOKEN_REASON),
135146
]
136147

137148

@@ -140,15 +151,15 @@ def test_ast_nested_attribute():
140151
code = 'self.config.db.password = "abcdef"'
141152
result = detect_ast_secrets(code)
142153

143-
assert result == [(1, "Password", "HIGH", "abcdef")]
154+
assert result == [(1, "Password", "HIGH", "abcdef", PASSWORD_REASON)]
144155

145156

146157
# Detect API keys in deeply nested attribute chains
147158
def test_ast_deep_nested_api_key():
148159
code = 'settings.auth.credentials.api_key = "12345678"'
149160
result = detect_ast_secrets(code)
150161

151-
assert result == [(1, "API Key", "HIGH", "12345678")]
162+
assert result == [(1, "API Key", "HIGH", "12345678", API_KEY_REASON)]
152163

153164

154165
# Ignore unsupported assignment targets such as subscript assignments
@@ -164,7 +175,9 @@ def test_ast_complex_password_value():
164175
code = 'password = "abc_def-123#$%^&*()"'
165176
result = detect_ast_secrets(code)
166177

167-
assert result == [(1, "Password", "HIGH", "abc_def-123#$%^&*()")]
178+
assert result == [
179+
(1, "Password", "HIGH", "abc_def-123#$%^&*()", PASSWORD_REASON)
180+
]
168181

169182

170183
# Handle indented multiline code by normalizing indentation
@@ -175,4 +188,4 @@ def test_ast_dedented_multiline_code():
175188
"""
176189
result = detect_ast_secrets(code)
177190

178-
assert result == [(2, "Password", "HIGH", "abcdef")]
191+
assert result == [(2, "Password", "HIGH", "abcdef", PASSWORD_REASON)]

tests/test_cli.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
import sys
44

55

6+
PASSWORD_REASON = (
7+
"variable name matched password/pwd/passwd pattern and value met minimum length"
8+
)
9+
TOKEN_REASON = "variable name matched token pattern and value met minimum length"
10+
11+
612
def run_cli(*args):
713
"""
814
Run the SentinelScan CLI with the provided arguments.
@@ -46,7 +52,7 @@ def test_cli_json_schema():
4652
assert isinstance(data, list)
4753
assert len(data) > 0
4854

49-
required_keys = {"line", "file", "rule", "severity", "value"}
55+
required_keys = {"line", "file", "rule", "severity", "value", "reason"}
5056

5157
for finding in data:
5258
assert required_keys.issubset(finding.keys())
@@ -68,6 +74,7 @@ def test_cli_json_field_types():
6874
assert isinstance(finding["rule"], str)
6975
assert isinstance(finding["severity"], str)
7076
assert isinstance(finding["value"], str)
77+
assert isinstance(finding["reason"], str)
7178

7279

7380
# Ensure JSON mode does not include human-readable CLI text
@@ -79,6 +86,7 @@ def test_cli_json_output_is_pure_json():
7986
assert "Scanning" not in result.stdout
8087
assert "--- Findings ---" not in result.stdout
8188
assert "Total findings" not in result.stdout
89+
assert "Reason:" not in result.stdout
8290

8391
data = parse_json_output(result)
8492
assert isinstance(data, list)
@@ -95,14 +103,15 @@ def test_cli_text_output_contains_expected_sections():
95103
assert "Total findings" in result.stdout
96104

97105

98-
# Ensure normal CLI output contains finding details
106+
# Ensure normal CLI output contains finding details and reasons
99107
def test_cli_text_output_contains_finding_details():
100108
result = run_cli("test_dirs")
101109

102110
assert result.returncode == 0
103111

104112
assert "[HIGH]" in result.stdout
105113
assert "→" in result.stdout
114+
assert "Reason:" in result.stdout
106115

107116

108117
# Ensure HIGH severity filtering works in JSON mode
@@ -115,6 +124,7 @@ def test_cli_json_severity_high_filter():
115124

116125
assert len(data) > 0
117126
assert all(finding["severity"] == "HIGH" for finding in data)
127+
assert all("reason" in finding for finding in data)
118128

119129

120130
# Ensure MEDIUM severity filtering works in JSON mode
@@ -127,6 +137,7 @@ def test_cli_json_severity_medium_filter():
127137

128138
assert len(data) > 0
129139
assert all(finding["severity"] == "MEDIUM" for finding in data)
140+
assert all("reason" in finding for finding in data)
130141

131142

132143
# Ensure HIGH severity filtering works in text mode
@@ -137,6 +148,7 @@ def test_cli_text_severity_high_filter():
137148

138149
assert "[HIGH]" in result.stdout
139150
assert "[MEDIUM]" not in result.stdout
151+
assert "Reason:" in result.stdout
140152

141153

142154
# Ensure MEDIUM severity filtering works in text mode
@@ -147,6 +159,7 @@ def test_cli_text_severity_medium_filter():
147159

148160
assert "[MEDIUM]" in result.stdout
149161
assert "[HIGH]" not in result.stdout
162+
assert "Reason:" in result.stdout
150163

151164

152165
# Ensure JSON output and severity filtering work together
@@ -159,6 +172,7 @@ def test_cli_json_and_severity_combined():
159172

160173
assert len(data) > 0
161174
assert all(finding["severity"] == "HIGH" for finding in data)
175+
assert all("reason" in finding for finding in data)
162176

163177

164178
# Ensure invalid severity values are rejected by argparse
@@ -186,6 +200,7 @@ def test_cli_no_findings_text_output(tmp_path):
186200
assert result.returncode == 0
187201

188202
assert "No vulnerabilities found." in result.stdout
203+
assert "Reason:" not in result.stdout
189204

190205

191206
# Ensure benign Python files produce an empty JSON list
@@ -220,6 +235,7 @@ def test_cli_detects_secret_in_temp_directory(tmp_path):
220235
"rule": "Password",
221236
"severity": "HIGH",
222237
"value": "abcdef",
238+
"reason": PASSWORD_REASON,
223239
}
224240
]
225241

@@ -248,3 +264,4 @@ def test_cli_text_filter_with_no_matching_findings(tmp_path):
248264
assert result.returncode == 0
249265

250266
assert "No vulnerabilities found." in result.stdout
267+
assert "Reason:" not in result.stdout

0 commit comments

Comments
 (0)