From ba0e3d8e0bbfd864686cd19c02c755fde2d3c7e2 Mon Sep 17 00:00:00 2001 From: zumayaaustin Date: Wed, 8 Jul 2026 23:16:47 +0000 Subject: [PATCH] Resolve skill name via directory match to break path-injection taint (CodeQL) Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- server.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/server.py b/server.py index a76e4f5..b05f7d0 100644 --- a/server.py +++ b/server.py @@ -133,17 +133,19 @@ def load_json_file(path: Path, default=_MISSING): except (json.JSONDecodeError, OSError, UnicodeDecodeError) as e: raise HTTPException(500, f"Failed to read {path.name}: {e}") -SKILL_NAME_RE = re.compile(r"^[A-Za-z0-9_-]+$") - def skill_dir(name: str) -> Path: - """Resolve a user-supplied skill name to its directory, rejecting path traversal.""" - if not SKILL_NAME_RE.fullmatch(name or ""): - raise HTTPException(404, "Skill not found") - base = (BASE_DIR / "skills").resolve() - candidate = (base / name).resolve() - if candidate.parent != base: - raise HTTPException(404, "Skill not found") - return candidate + """Resolve a user-supplied skill name to its directory. + + The name is matched against the actual directory entries rather than used to + build a path, so traversal input (``..``, ``/``) can never escape the skills + directory. + """ + base = BASE_DIR / "skills" + if base.exists(): + for entry in base.iterdir(): + if entry.is_dir() and entry.name == name: + return entry + raise HTTPException(404, "Skill not found") def list_dir(path: Path): if not path.exists():