Skills-View: Text-Endpoint matcht jetzt Kategorie-Skills ({skill_id:path})

Kategorie-Skills (z. B. autonomous-ai-agents/hermes-agent) haben einen
Schraegstrich in der ID. Die Route /api/eigenleben/skill/{skill_id} nahm nur
EIN Pfadsegment -> Request fiel in den SPA-Fallback (index.html, Status 200),
die UI zeigte "(Konnte den Skill-Text nicht laden.)". Live bewiesen:
skill/wartung = JSON, skill/autonomous-ai-agents%2Fhermes-agent = HTML.

Fix: :path-Konverter in der Route; dazu im Service "."/".."-Segmente
explizit abweisen (das Zeichen-Set der bestehenden Validierung liesse
".." als Segment durch, bisher irrelevant, mit :path sauber dicht).
Backend-only, kein dist-Rebuild noetig.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-07-15 15:07:44 +02:00
parent 5d0a61c38e
commit 0f13a4f18d
2 changed files with 10 additions and 1 deletions
+6 -1
View File
@@ -12,7 +12,12 @@ def get_overview() -> dict:
return eigenleben.overview() return eigenleben.overview()
@router.get("/eigenleben/skill/{skill_id}") # :path — Kategorie-Skills haben einen Schrägstrich in der ID (autonomous-ai-agents/
# hermes-agent). Als normales {skill_id}-Segment matcht die Route dann nicht, der Request
# fällt in den SPA-Fallback (index.html, Status 200) und die UI zeigt „Konnte den
# Skill-Text nicht laden". Die Pfad-Validierung (max. eine Ebene, keine . /..) macht
# services.eigenleben.skill_text.
@router.get("/eigenleben/skill/{skill_id:path}")
def get_skill(skill_id: str) -> dict: def get_skill(skill_id: str) -> dict:
text = eigenleben.skill_text(skill_id) text = eigenleben.skill_text(skill_id)
if text is None: if text is None:
+4
View File
@@ -157,6 +157,10 @@ def skill_text(skill_id: str) -> str | None:
"""Voller SKILL.md-Text („wie funktioniert der Skill") — max. Kategorie/Name, keine Pfad-Tricks.""" """Voller SKILL.md-Text („wie funktioniert der Skill") — max. Kategorie/Name, keine Pfad-Tricks."""
if not re.fullmatch(r"[\w.-]+(/[\w.-]+)?", skill_id or ""): if not re.fullmatch(r"[\w.-]+(/[\w.-]+)?", skill_id or ""):
return None return None
# Das Zeichen-Set oben erlaubt „." und „.." als Segment — seit die Route ein
# :path-Parameter ist, hier explizit raus (kein Klettern über SKILLS_DIR hinaus).
if any(seg in (".", "..") for seg in skill_id.split("/")):
return None
md = SKILLS_DIR / skill_id / "SKILL.md" md = SKILLS_DIR / skill_id / "SKILL.md"
if not md.is_file(): if not md.is_file():
return None return None