diff --git a/app.py b/app.py index de92339..7eb6611 100644 --- a/app.py +++ b/app.py @@ -138,6 +138,68 @@ def is_public_target(url: str) -> bool: return True +# --------------------------------------------------------------------------- # +# Rohreport lesbar aufbereiten: nach Severity gruppieren, Feldnamen +# vereinheitlichen (SkillSpector nennt Detail-Felder je Version leicht anders). +# --------------------------------------------------------------------------- # +SEVERITY_ORDER = ["CRITICAL", "HIGH", "MEDIUM", "LOW", "INFO"] +SEVERITY_LABELS_DE = { + "CRITICAL": "Kritisch", + "HIGH": "Hoch", + "MEDIUM": "Mittel", + "LOW": "Niedrig", + "INFO": "Info", +} + + +def normalize_issues(raw_issues: list) -> dict: + grouped: dict[str, list] = {} + for issue in raw_issues or []: + severity = str(issue.get("severity", "INFO")).upper() + location = issue.get("location") or {} + confidence = issue.get("confidence") + if isinstance(confidence, (int, float)) and confidence <= 1: + confidence_display = f"{round(confidence * 100)}%" + else: + confidence_display = confidence + grouped.setdefault(severity, []).append( + { + "id": issue.get("id") or issue.get("rule_id") or "?", + "category": issue.get("category") or issue.get("type") or "", + "message": ( + issue.get("finding") + or issue.get("message") + or issue.get("description") + or issue.get("title") + or "" + ), + "explanation": issue.get("explanation") or issue.get("rationale") or "", + "file": location.get("file") or location.get("path") or "", + "line": location.get("start_line") or location.get("line") or "", + "confidence": confidence_display, + } + ) + ordered = {lvl: grouped[lvl] for lvl in SEVERITY_ORDER if lvl in grouped} + for lvl, items in grouped.items(): + if lvl not in ordered: + ordered[lvl] = items + return ordered + + +def normalize_components(raw_components: list) -> list: + normalized = [] + for c in raw_components or []: + normalized.append( + { + "file": c.get("path") or c.get("file") or c.get("name") or "-", + "type": c.get("type") or c.get("kind") or "-", + "lines": c.get("lines") or c.get("line_count") or "-", + "executable": bool(c.get("executable")), + } + ) + return normalized + + # --------------------------------------------------------------------------- # # Design im Stil von software.obraasch.de: dunkler Header, orange Akzente, # helle Cards. @@ -209,6 +271,19 @@ pre{ padding:14px 16px;font-weight:600; } .notice{color:var(--ink-soft);font-size:.85em;margin-top:8px} +.sev-heading{margin:0 0 14px;font-size:1.05em;font-weight:800;padding-bottom:8px;border-bottom:1px solid var(--border)} +.sev-CRITICAL{color:#c62828}.sev-HIGH{color:#d9601a}.sev-MEDIUM{color:#b5720a}.sev-LOW{color:#5b6470}.sev-INFO{color:#5b6470} +.issue{padding:14px 0;border-bottom:1px solid var(--border)} +.issue:last-child{border-bottom:none;padding-bottom:0} +.issue-head{display:flex;justify-content:space-between;align-items:baseline;gap:12px;font-size:.8em;color:var(--ink-soft);font-weight:700;text-transform:uppercase;letter-spacing:.3px} +.issue-msg{margin:6px 0 4px;font-weight:600} +.issue-loc{margin:0 0 4px;font-family:monospace;font-size:.85em;color:var(--ink-soft)} +.issue-expl{margin:4px 0 0;font-size:.9em;color:var(--ink-soft)} +.comp-table{width:100%;border-collapse:collapse;font-size:.88em} +.comp-table th{text-align:left;color:var(--ink-soft);font-weight:700;padding:6px 8px;border-bottom:1px solid var(--border)} +.comp-table td{padding:6px 8px;border-bottom:1px solid var(--border);font-family:monospace} +details summary{cursor:pointer;font-weight:700;color:var(--ink-soft)} +details pre{margin-top:14px} footer{ max-width:760px;margin:0 auto;padding:0 24px 40px;color:#9aa2ab;font-size:.85em; } @@ -245,8 +320,49 @@ a{color:var(--orange)} {{ result.risk_assessment.score }}/100 {{ result.risk_assessment.recommendation }} -
{{ result_json }}
+ {% if not issues_by_severity %}
+ Keine Funde – das Ergebnis unten (Rohdaten) bestätigt das im Detail.
+ {% endif %} + + {% for severity, items in issues_by_severity.items() %} +{{ issue.message }}
+ {% if issue.file %}{{ issue.file }}{% if issue.line %}:{{ issue.line }}{% endif %}
{% endif %} + {% if issue.explanation %}{{ issue.explanation }}
{% endif %} +| Datei | Typ | Zeilen | Ausführbar |
|---|---|---|---|
| {{ c.file }} | +{{ c.type }} | +{{ c.lines }} | +{{ 'ja' if c.executable else 'nein' }} | +
{{ result_json }}
+ {{ error }}
@@ -329,10 +445,14 @@ def index(): if report_path.exists(): result = json.loads(report_path.read_text()) + raw_issues = result.get("issues") or result.get("findings") or [] return render_template_string( PAGE, allow_llm=ALLOW_LLM, result=result, + issues_by_severity=normalize_issues(raw_issues), + components=normalize_components(result.get("components", [])), + severity_labels=SEVERITY_LABELS_DE, result_json=json.dumps(result, indent=2, ensure_ascii=False), error=None, )