Spaces:
Running
Running
| 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) | |
| ), | |
| } |