ai-code-review-agent / app /tools /static_analyzer.py
Padmanav's picture
feat(security): add Semgrep scanner with python + secrets rulesets
7700a48
Raw
History Blame Contribute Delete
2.47 kB
import subprocess # nosec B404
from typing import Any
from app.tools.semgrep_scanner import run_semgrep
def run_ruff(local_path: str) -> dict[str, Any]:
result: dict[str, Any] = {"output": "", "issues": [], "success": False}
try:
process = subprocess.run( # nosec
["ruff", "check", ".", "--output-format=text"],
cwd=local_path,
capture_output=True,
text=True,
timeout=60,
)
result["output"] = process.stdout + process.stderr
result["issues"] = [
line for line in str(result["output"]).splitlines() if line.strip()
]
result["success"] = True
except Exception as e:
result["output"] = f"Ruff error: {str(e)}"
return result
def run_bandit(local_path: str) -> dict[str, Any]:
result: dict[str, Any] = {"output": "", "issues": [], "success": False}
try:
process = subprocess.run( # nosec
["bandit", "-r", ".", "-f", "txt", "-q"],
cwd=local_path,
capture_output=True,
text=True,
timeout=60,
)
result["output"] = process.stdout + process.stderr
result["issues"] = [
line for line in str(result["output"]).splitlines() if line.strip()
]
result["success"] = True
except Exception as e:
result["output"] = f"Bandit error: {str(e)}"
return result
def run_full_analysis(local_path: str) -> dict[str, Any]:
ruff_results = run_ruff(local_path)
bandit_results = run_bandit(local_path)
semgrep_result = run_semgrep(local_path)
# Normalise Semgrep findings into the same flat issues list format
# used by ruff/bandit so callers don't need to know the difference
semgrep_issues = [
f"[{f['severity'].upper()}] {f['rule_id']} {f['file']}:{f['line']}{f['message']}"
for f in semgrep_result.get("findings", [])
]
return {
"ruff": ruff_results,
"bandit": bandit_results,
"semgrep": {
"issues": semgrep_issues,
"findings": semgrep_result.get("findings", []), # structured form for downstream use
"success": semgrep_result.get("success", False),
"error": semgrep_result.get("error"),
},
"total_issues": (
len(ruff_results["issues"])
+ len(bandit_results["issues"])
+ len(semgrep_issues)
),
}