Dateien nach "/" hochladen

This commit is contained in:
Oliver Braasch committed 2026-09-22 23:14:52 +02:00
1 parent 2e525989f7
commit 929c3f8c95
1 file changed
+121 -1
+121 -1
View File
@@ -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)}
<span class="score-num">{{ result.risk_assessment.score }}/100</span>
<span class="badge {{ result.risk_assessment.recommendation }}">{{ result.risk_assessment.recommendation }}</span>
</div>
<pre>{{ result_json }}</pre>
{% if not issues_by_severity %}
<p class="notice">Keine Funde – das Ergebnis unten (Rohdaten) bestätigt das im Detail.</p>
{% endif %}
</div>
{% for severity, items in issues_by_severity.items() %}
<div class="card">
<h3 class="sev-heading sev-{{ severity }}">{{ severity_labels.get(severity, severity) }} ({{ items|length }})</h3>
{% for issue in items %}
<div class="issue">
<div class="issue-head">
<span>{{ issue.category }}</span>
{% if issue.confidence %}<span>Konfidenz: {{ issue.confidence }}</span>{% endif %}
</div>
<p class="issue-msg">{{ issue.message }}</p>
{% if issue.file %}<p class="issue-loc">{{ issue.file }}{% if issue.line %}:{{ issue.line }}{% endif %}</p>{% endif %}
{% if issue.explanation %}<p class="issue-expl">{{ issue.explanation }}</p>{% endif %}
</div>
{% endfor %}
</div>
{% endfor %}
{% if components %}
<div class="card">
<h3 class="sev-heading">Gescannte Komponenten ({{ components|length }})</h3>
<table class="comp-table">
<tr><th>Datei</th><th>Typ</th><th>Zeilen</th><th>Ausführbar</th></tr>
{% for c in components %}
<tr>
<td>{{ c.file }}</td>
<td>{{ c.type }}</td>
<td>{{ c.lines }}</td>
<td>{{ 'ja' if c.executable else 'nein' }}</td>
</tr>
{% endfor %}
</table>
</div>
{% endif %}
<details class="card">
<summary>Rohdaten (vollständiges JSON)</summary>
<pre>{{ result_json }}</pre>
</details>
{% elif error %}
<div class="card">
<p class="err">{{ error }}</p>
@@ -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,
)