File size: 2,469 Bytes
a79668c
f8b17b7
007ba8f
7700a48
 
007ba8f
f8b17b7
 
007ba8f
94348a4
007ba8f
a79668c
 
 
 
007ba8f
 
 
f8b17b7
007ba8f
 
 
 
 
 
 
f8b17b7
 
007ba8f
94348a4
 
a79668c
 
 
 
007ba8f
 
 
f8b17b7
007ba8f
 
 
 
 
 
 
f8b17b7
7700a48
007ba8f
7700a48
 
 
 
 
 
 
 
 
007ba8f
7700a48
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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)
        ),
    }