Dateien nach "/" hochladen

This commit is contained in:
Oliver Braasch committed 2026-09-22 13:09:33 +02:00
1 parent 978527baaa
commit c53a1b86c5
1 file changed
+98 -29
+98 -29
View File
@@ -93,7 +93,6 @@ def requires_auth(f):
# Rate-Limiting (pro Client-IP, in-memory – reicht für einen einzelnen Prozess)
# --------------------------------------------------------------------------- #
def client_ip() -> str:
# Wenn hinter einem Reverse Proxy: X-Forwarded-For des Proxys vertrauen.
fwd = request.headers.get("X-Forwarded-For")
return fwd.split(",")[0].strip() if fwd else request.remote_addr or "unknown"
@@ -139,57 +138,127 @@ def is_public_target(url: str) -> bool:
return True
# --------------------------------------------------------------------------- #
# Design im Stil von software.obraasch.de: dunkler Header, orange Akzente,
# helle Cards.
# --------------------------------------------------------------------------- #
PAGE = """
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SkillSpector</title>
<style>
body{font-family:sans-serif;max-width:820px;margin:40px auto;padding:0 16px;background:#111;color:#eee}
input,button{width:100%;padding:9px;margin:6px 0;box-sizing:border-box;font-size:1em}
input[type=checkbox]{width:auto;display:inline}
button{background:#2ecc71;color:#000;border:none;cursor:pointer;font-weight:bold;padding:10px}
pre{background:#000;padding:12px;overflow-x:auto;white-space:pre-wrap;border-radius:6px}
.score{font-size:1.8em;font-weight:bold;margin:12px 0}
.SAFE{color:#4caf50}.CAUTION{color:#ffb300}.DO_NOT_INSTALL{color:#f44336}
label{display:block;margin-top:14px;font-size:.9em;color:#bbb}
.err{color:#f44336;font-weight:bold}
.notice{font-size:.85em;color:#999;margin-top:24px;border-top:1px solid #333;padding-top:12px}
hr{border-color:#333}
a{color:#6cf}
:root{
--orange:#f2731e;
--orange-dark:#d9601a;
--bg:#eef1f4;
--card:#ffffff;
--ink:#1a1d21;
--ink-soft:#5b6470;
--border:#e2e6ea;
}
*{box-sizing:border-box}
body{
margin:0;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;
background:var(--bg);color:var(--ink);
}
header{
background:#0d0e10;padding:28px 24px;
}
.header-inner{max-width:760px;margin:0 auto}
.logo{font-size:1.9em;font-weight:800;letter-spacing:.5px;margin:0}
.logo .accent{color:var(--orange)}
.subtitle{color:#9aa2ab;margin:6px 0 0;font-size:.95em}
main{max-width:760px;margin:32px auto;padding:0 24px 48px}
.card{
background:var(--card);border:1px solid var(--border);border-radius:14px;
padding:28px;margin-bottom:24px;box-shadow:0 1px 2px rgba(0,0,0,.03);
}
label{display:block;margin-top:16px;font-size:.85em;color:var(--ink-soft);font-weight:600}
input[type=text],input[type=file]{
width:100%;padding:11px 12px;margin-top:6px;border:1px solid var(--border);
border-radius:9px;font-size:.95em;background:#fbfcfd;
}
input[type=text]:focus{outline:2px solid var(--orange);border-color:var(--orange)}
.checkbox-row{display:flex;align-items:flex-start;gap:8px;margin-top:16px}
.checkbox-row input{width:auto;margin-top:2px}
.checkbox-row label{margin:0;font-weight:400;color:var(--ink-soft)}
button{
margin-top:20px;padding:12px 20px;border:none;border-radius:9px;
background:var(--orange);color:#fff;font-weight:700;font-size:.95em;
cursor:pointer;transition:background .15s;
}
button:hover{background:var(--orange-dark)}
.badge{
display:inline-block;padding:4px 12px;border-radius:999px;font-size:.8em;
font-weight:700;border:1px solid transparent;
}
.badge.SAFE{background:#e8f7ee;color:#1e8e4a;border-color:#c6ebd3}
.badge.CAUTION{background:#fff4e0;color:#b5720a;border-color:#ffe3ad}
.badge.DO_NOT_INSTALL{background:#fdecec;color:#c62828;border-color:#f9cccc}
.score-row{display:flex;align-items:center;gap:12px;margin-bottom:16px}
.score-num{font-size:1.9em;font-weight:800}
pre{
background:#14161a;color:#d7dbe0;padding:16px;border-radius:10px;
overflow-x:auto;white-space:pre-wrap;font-size:.82em;line-height:1.5;
}
.err{
background:#fdecec;color:#c62828;border:1px solid #f9cccc;border-radius:10px;
padding:14px 16px;font-weight:600;
}
.notice{color:var(--ink-soft);font-size:.85em;margin-top:8px}
footer{
max-width:760px;margin:0 auto;padding:0 24px 40px;color:#9aa2ab;font-size:.85em;
}
a{color:var(--orange)}
</style>
</head>
<body>
<h1>🔍 SkillSpector</h1>
<p>Scannt einen Skill (Git-Repo oder ZIP) auf Sicherheitsrisiken, <strong>bevor</strong> er installiert wird.
Basiert auf <a href="https://github.com/NVIDIA/SkillSpector" target="_blank">NVIDIA SkillSpector</a> (Apache-2.0).</p>
<header>
<div class="header-inner">
<p class="logo">SKILL<span class="accent">SPECTOR</span></p>
<p class="subtitle">Sicherheits-Scanner für AI-Agent-Skills – vor dem Installieren prüfen</p>
</div>
</header>
<main>
<div class="card">
<form method="post" enctype="multipart/form-data">
<label>Git-Repository-URL (nur öffentlich erreichbares http/https)</label>
<label>Git-Repository-URL</label>
<input type="text" name="git_url" placeholder="https://git.obraasch.de/olli1986/mein-skill">
<label>...oder ZIP-Datei hochladen (max. 50&nbsp;MB)</label>
<input type="file" name="zip_file" accept=".zip">
{% if allow_llm %}
<label><input type="checkbox" name="use_llm"> LLM-Analyse aktivieren (sendet Dateiinhalte des Skills an den konfigurierten KI-Anbieter – genauer, aber nicht mehr rein lokal)</label>
<div class="checkbox-row">
<input type="checkbox" name="use_llm" id="use_llm">
<label for="use_llm">LLM-Analyse aktivieren (sendet Dateiinhalte an den konfigurierten KI-Anbieter)</label>
</div>
{% endif %}
<button type="submit">Scan starten</button>
</form>
<hr>
</div>
{% if result %}
<h2>Ergebnis</h2>
<p class="score {{ result.risk_assessment.recommendation }}">
{{ result.risk_assessment.score }}/100 — {{ result.risk_assessment.severity }} — {{ result.risk_assessment.recommendation }}
</p>
<div class="card">
<div class="score-row">
<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>
</div>
{% elif error %}
<div class="card">
<p class="err">{{ error }}</p>
</div>
{% endif %}
<p class="notice">
Statische Analyse läuft immer lokal. {% if allow_llm %}Bei aktivierter LLM-Analyse werden
Dateiinhalte an einen externen KI-Anbieter übertragen.{% endif %}
Der gescannte Skill wird zu keinem Zeitpunkt ausgeführt. Kein Ergebnis wird dauerhaft
gespeichert – es erscheint nur auf dieser Seite.
</p>
</main>
<footer>
Läuft auf <a href="https://github.com/NVIDIA/SkillSpector" target="_blank">NVIDIA SkillSpector</a>
(Apache-2.0). Statische Analyse läuft immer lokal
{% if allow_llm %}– bei aktivierter LLM-Analyse werden Dateiinhalte extern übertragen{% endif %}.
Der gescannte Skill wird nie ausgeführt, kein Ergebnis wird dauerhaft gespeichert.
</footer>
</body>
</html>
"""