Dateien nach "/" hochladen
This commit is contained in:
1 parent
978527baaa
commit
c53a1b86c5
1 file changed
+105
-36
@@ -93,7 +93,6 @@ def requires_auth(f):
|
|||||||
# Rate-Limiting (pro Client-IP, in-memory – reicht für einen einzelnen Prozess)
|
# Rate-Limiting (pro Client-IP, in-memory – reicht für einen einzelnen Prozess)
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
def client_ip() -> str:
|
def client_ip() -> str:
|
||||||
# Wenn hinter einem Reverse Proxy: X-Forwarded-For des Proxys vertrauen.
|
|
||||||
fwd = request.headers.get("X-Forwarded-For")
|
fwd = request.headers.get("X-Forwarded-For")
|
||||||
return fwd.split(",")[0].strip() if fwd else request.remote_addr or "unknown"
|
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
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Design im Stil von software.obraasch.de: dunkler Header, orange Akzente,
|
||||||
|
# helle Cards.
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
PAGE = """
|
PAGE = """
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>SkillSpector</title>
|
<title>SkillSpector</title>
|
||||||
<style>
|
<style>
|
||||||
body{font-family:sans-serif;max-width:820px;margin:40px auto;padding:0 16px;background:#111;color:#eee}
|
:root{
|
||||||
input,button{width:100%;padding:9px;margin:6px 0;box-sizing:border-box;font-size:1em}
|
--orange:#f2731e;
|
||||||
input[type=checkbox]{width:auto;display:inline}
|
--orange-dark:#d9601a;
|
||||||
button{background:#2ecc71;color:#000;border:none;cursor:pointer;font-weight:bold;padding:10px}
|
--bg:#eef1f4;
|
||||||
pre{background:#000;padding:12px;overflow-x:auto;white-space:pre-wrap;border-radius:6px}
|
--card:#ffffff;
|
||||||
.score{font-size:1.8em;font-weight:bold;margin:12px 0}
|
--ink:#1a1d21;
|
||||||
.SAFE{color:#4caf50}.CAUTION{color:#ffb300}.DO_NOT_INSTALL{color:#f44336}
|
--ink-soft:#5b6470;
|
||||||
label{display:block;margin-top:14px;font-size:.9em;color:#bbb}
|
--border:#e2e6ea;
|
||||||
.err{color:#f44336;font-weight:bold}
|
}
|
||||||
.notice{font-size:.85em;color:#999;margin-top:24px;border-top:1px solid #333;padding-top:12px}
|
*{box-sizing:border-box}
|
||||||
hr{border-color:#333}
|
body{
|
||||||
a{color:#6cf}
|
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>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>🔍 SkillSpector</h1>
|
<header>
|
||||||
<p>Scannt einen Skill (Git-Repo oder ZIP) auf Sicherheitsrisiken, <strong>bevor</strong> er installiert wird.
|
<div class="header-inner">
|
||||||
Basiert auf <a href="https://github.com/NVIDIA/SkillSpector" target="_blank">NVIDIA SkillSpector</a> (Apache-2.0).</p>
|
<p class="logo">SKILL<span class="accent">SPECTOR</span></p>
|
||||||
<form method="post" enctype="multipart/form-data">
|
<p class="subtitle">Sicherheits-Scanner für AI-Agent-Skills – vor dem Installieren prüfen</p>
|
||||||
<label>Git-Repository-URL (nur öffentlich erreichbares http/https)</label>
|
</div>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<div class="card">
|
||||||
|
<form method="post" enctype="multipart/form-data">
|
||||||
|
<label>Git-Repository-URL</label>
|
||||||
<input type="text" name="git_url" placeholder="https://git.obraasch.de/olli1986/mein-skill">
|
<input type="text" name="git_url" placeholder="https://git.obraasch.de/olli1986/mein-skill">
|
||||||
<label>...oder ZIP-Datei hochladen (max. 50 MB)</label>
|
<label>...oder ZIP-Datei hochladen (max. 50 MB)</label>
|
||||||
<input type="file" name="zip_file" accept=".zip">
|
<input type="file" name="zip_file" accept=".zip">
|
||||||
{% if allow_llm %}
|
{% 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 %}
|
{% endif %}
|
||||||
<button type="submit">Scan starten</button>
|
<button type="submit">Scan starten</button>
|
||||||
</form>
|
</form>
|
||||||
<hr>
|
</div>
|
||||||
{% if result %}
|
|
||||||
<h2>Ergebnis</h2>
|
{% if result %}
|
||||||
<p class="score {{ result.risk_assessment.recommendation }}">
|
<div class="card">
|
||||||
{{ result.risk_assessment.score }}/100 — {{ result.risk_assessment.severity }} — {{ result.risk_assessment.recommendation }}
|
<div class="score-row">
|
||||||
</p>
|
<span class="score-num">{{ result.risk_assessment.score }}/100</span>
|
||||||
<pre>{{ result_json }}</pre>
|
<span class="badge {{ result.risk_assessment.recommendation }}">{{ result.risk_assessment.recommendation }}</span>
|
||||||
{% elif error %}
|
</div>
|
||||||
<p class="err">{{ error }}</p>
|
<pre>{{ result_json }}</pre>
|
||||||
{% endif %}
|
</div>
|
||||||
<p class="notice">
|
{% elif error %}
|
||||||
Statische Analyse läuft immer lokal. {% if allow_llm %}Bei aktivierter LLM-Analyse werden
|
<div class="card">
|
||||||
Dateiinhalte an einen externen KI-Anbieter übertragen.{% endif %}
|
<p class="err">{{ error }}</p>
|
||||||
Der gescannte Skill wird zu keinem Zeitpunkt ausgeführt. Kein Ergebnis wird dauerhaft
|
</div>
|
||||||
gespeichert – es erscheint nur auf dieser Seite.
|
{% endif %}
|
||||||
</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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in new issue
Block a user