Skip to content

Commit a4a2186

Browse files
committed
add Ruff linting to CI
1 parent f86dcde commit a4a2186

11 files changed

Lines changed: 52 additions & 60 deletions

File tree

.github/workflows/tests.yaml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,17 @@ jobs:
1111
runs-on: ubuntu-latest
1212

1313
steps:
14+
- name: Install test dependencies
15+
run: |
16+
python -m pip install --upgrade pip
17+
pip install pytest ruff
18+
19+
- name: Run Ruff
20+
run: ruff check .
21+
22+
- name: Run tests
23+
run: pytest
24+
1425
- name: Check out repository
1526
uses: actions/checkout@v4
1627

@@ -25,4 +36,5 @@ jobs:
2536
pip install pytest
2637
2738
- name: Run tests
28-
run: pytest
39+
run: pytest
40+

cli.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,16 @@
1313
)
1414

1515
# Define required positional argument for the target directory
16-
parser.add_argument(
17-
"path",
18-
help="Path to the directory to scan"
19-
)
16+
parser.add_argument("path", help="Path to the directory to scan")
2017

2118
# Enable machine-readable JSON output when present
22-
parser.add_argument(
23-
"--json",
24-
action="store_true",
25-
help="Output findings as JSON"
26-
)
19+
parser.add_argument("--json", action="store_true", help="Output findings as JSON")
2720

2821
# Optionally filter displayed findings by severity level
2922
parser.add_argument(
3023
"--severity",
3124
choices=["LOW", "MEDIUM", "HIGH"],
32-
help="Only show findings matching the selected severity"
25+
help="Only show findings matching the selected severity",
3326
)
3427

3528
# Parse command-line arguments
@@ -39,4 +32,3 @@
3932
input_path = args.path
4033
use_json = args.json
4134
chosen_severity = args.severity
42-

detectors/find_secrets.py

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,35 +9,37 @@
99
REGEX_INFO = {
1010
"AWS Access Key": {
1111
"value_pattern": re.compile(r"(AKIA[0-9A-Z]{16})"),
12-
"severity": "HIGH"
12+
"severity": "HIGH",
1313
},
1414
"Password": {
1515
"var_patterns": [
1616
re.compile(r"password", re.IGNORECASE),
1717
re.compile(r"pwd", re.IGNORECASE),
18-
re.compile(r"passwd", re.IGNORECASE)
18+
re.compile(r"passwd", re.IGNORECASE),
1919
],
2020
"min_length": 4,
21-
"severity": "HIGH"
21+
"severity": "HIGH",
2222
},
2323
"API Key": {
2424
"var_patterns": [
2525
re.compile(r"api_key", re.IGNORECASE),
26-
re.compile(r"apikey", re.IGNORECASE)
26+
re.compile(r"apikey", re.IGNORECASE),
2727
],
28-
"min_length": 4,
29-
"severity": "HIGH"
28+
"min_length": 4,
29+
"severity": "HIGH",
3030
},
3131
"Token": {
3232
"var_patterns": [re.compile(r"token", re.IGNORECASE)],
3333
"min_length": 4,
34-
"severity": "MEDIUM"
34+
"severity": "MEDIUM",
3535
},
3636
"Secret": {
37-
"var_patterns": [re.compile(r"secret", re.IGNORECASE),],
37+
"var_patterns": [
38+
re.compile(r"secret", re.IGNORECASE),
39+
],
3840
"min_length": 4,
39-
"severity": "MEDIUM"
40-
}
41+
"severity": "MEDIUM",
42+
},
4143
}
4244

4345

@@ -57,7 +59,7 @@ def parse_ast(file):
5759
tree = ast.parse(file)
5860
except SyntaxError:
5961
return None
60-
62+
6163
return tree
6264

6365

@@ -71,7 +73,7 @@ def get_assignments(tree):
7173
Yields:
7274
ast.Assign: Assignment nodes found during traversal.
7375
"""
74-
for node in ast.walk(tree):
76+
for node in ast.walk(tree):
7577
if isinstance(node, ast.Assign):
7678
yield node
7779

@@ -87,10 +89,7 @@ def extract_node_value(node):
8789
str | None: String value if valid, otherwise None.
8890
"""
8991
val = node.value
90-
if not(
91-
isinstance(val, ast.Constant)
92-
and isinstance(val.value, str)
93-
):
92+
if not (isinstance(val, ast.Constant) and isinstance(val.value, str)):
9493
return None
9594
return val.value
9695

@@ -159,7 +158,7 @@ def detect_from_parts(var_name, val):
159158
# Match suspicious variable names and enforce minimum value length
160159
if "var_patterns" in data:
161160
for pattern in data["var_patterns"]:
162-
match = pattern.search(var_name)
161+
match = pattern.search(var_name)
163162
if match and len(val) >= data["min_length"]:
164163
findings.append((rule, data["severity"], val))
165164

@@ -204,5 +203,3 @@ def detect_ast_secrets(code):
204203
findings.append((line_number, pattern_name, severity, value))
205204

206205
return findings
207-
208-

main.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,3 @@
3636
except FileNotFoundError as e:
3737
# Display a user-friendly error message for invalid paths
3838
print(f"[ERROR] {e}")
39-
40-

scanner.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from detectors.find_secrets import detect_ast_secrets
33
import json
44

5+
56
def check_path(input_path):
67
"""
78
Validate that the provided path exists and is a directory.
@@ -20,9 +21,7 @@ def check_path(input_path):
2021
if path.is_dir():
2122
return path
2223

23-
raise FileNotFoundError(
24-
f"'{input_path}' does not exist or is not a directory!"
25-
)
24+
raise FileNotFoundError(f"'{input_path}' does not exist or is not a directory!")
2625

2726

2827
def list_python_files(path):
@@ -69,7 +68,6 @@ def scan(files):
6968
findings = []
7069

7170
for file in files:
72-
7371
# Read file content safely before passing it to the AST detector
7472
with open(file, "r", encoding="utf-8", errors="ignore") as f:
7573
content = f.read()
@@ -150,17 +148,13 @@ def output(filtered_findings, use_json, files):
150148
return
151149

152150
print(f"Scanning {len(files)} Python files...")
153-
151+
154152
if not filtered_findings:
155153
print("\nNo vulnerabilities found.")
156154
else:
157155
print("\n--- Findings ---\n")
158156

159-
for line_number, file, rule_name, severity, value in filtered_findings:
160-
print(
161-
f"[{severity}] "
162-
f"{file}:{line_number} "
163-
f"{rule_name}{value}"
164-
)
157+
for line_number, file, rule_name, severity, value in filtered_findings:
158+
print(f"[{severity}] {file}:{line_number} {rule_name}{value}")
165159

166-
print(f"\nTotal findings: {len(filtered_findings)}")
160+
print(f"\nTotal findings: {len(filtered_findings)}")

test_dirs/edge_repo/edge_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
password = "hjkl"
1+
password = "hjkl"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
print("hellohello")
1+
print("hellohello")

test_dirs/test_repo/hello.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
print("hello")
2-
print("world")
2+
print("world")

test_dirs/test_repo/open_vulns.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
api_key = "123"
2-
password="abc"
2+
password = "abc"
33
token = "xyz"
44
aws_key = "AKIAEXAMPLE123456789"
55
token = "xyzttttggfdddf"
6-
api_key = "12dwdqwdqwdqw3"; token = "xyzgggggg"
7-
TOKEN="abc1234567890j"
8-
9-
6+
api_key = "12dwdqwdqwdqw3"
7+
token = "xyzgggggg" # noqa: E702
8+
TOKEN = "abc1234567890j"

tests/test_ast.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def test_ast_aws_access_key_with_api_key_variable():
6262

6363
# Ignore non-string assignments
6464
def test_ast_non_string():
65-
code = 'password = 123456'
65+
code = "password = 123456"
6666
result = detect_ast_secrets(code)
6767

6868
assert result == []
@@ -86,11 +86,11 @@ def test_ast_short_value():
8686

8787
# Validate multiple assignments and correct line numbers
8888
def test_ast_multiple_assignments():
89-
code = '''
89+
code = """
9090
password = "abcdef"
9191
api_key = "12345678"
9292
token = "qwerty123"
93-
'''
93+
"""
9494
result = detect_ast_secrets(code)
9595

9696
assert result == [
@@ -169,10 +169,10 @@ def test_ast_complex_password_value():
169169

170170
# Handle indented multiline code by normalizing indentation
171171
def test_ast_dedented_multiline_code():
172-
code = '''
172+
code = """
173173
password = "abcdef"
174174
username = "notsecret"
175-
'''
175+
"""
176176
result = detect_ast_secrets(code)
177177

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

0 commit comments

Comments
 (0)