mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-01 16:58:06 +00:00
fix(dev-server): lexical containment check on resolved plugin dirs
CodeQL doesn't recognize the interprocedural allowlist as a path-injection barrier; add the canonical one — normalize (without following symlinks, since dev plugins are commonly symlinked into plugins/) and require the result to stay inside the search dir. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FqzC1nzTWL4kaqgMaQZFam
This commit is contained in:
+13
-4
@@ -113,8 +113,10 @@ def discover_plugins() -> List[Dict[str, Any]]:
|
|||||||
def find_plugin_dir(plugin_id: str) -> Optional[Path]:
|
def find_plugin_dir(plugin_id: str) -> Optional[Path]:
|
||||||
"""Find a plugin directory by ID.
|
"""Find a plugin directory by ID.
|
||||||
|
|
||||||
plugin_id comes from request input; the allowlist match keeps it from
|
plugin_id comes from request input: it must pass an allowlist match,
|
||||||
ever naming a path outside the plugin search dirs.
|
and the resulting directory is normalized and required to live inside
|
||||||
|
one of the plugin search dirs, so a crafted id can never name a path
|
||||||
|
outside them.
|
||||||
"""
|
"""
|
||||||
if not isinstance(plugin_id, str) or not _SAFE_PLUGIN_ID_RE.match(plugin_id):
|
if not isinstance(plugin_id, str) or not _SAFE_PLUGIN_ID_RE.match(plugin_id):
|
||||||
return None
|
return None
|
||||||
@@ -124,8 +126,15 @@ def find_plugin_dir(plugin_id: str) -> Optional[Path]:
|
|||||||
if not search_dir.exists():
|
if not search_dir.exists():
|
||||||
continue
|
continue
|
||||||
result = loader.find_plugin_directory(plugin_id, search_dir)
|
result = loader.find_plugin_directory(plugin_id, search_dir)
|
||||||
if result:
|
if not result:
|
||||||
return Path(result)
|
continue
|
||||||
|
# Normalize WITHOUT following symlinks (dev plugins are often
|
||||||
|
# symlinked into plugins/) and require lexical containment in the
|
||||||
|
# search dir, so no id can ever name a path outside it.
|
||||||
|
result_abs = os.path.abspath(str(result))
|
||||||
|
root_abs = os.path.abspath(str(search_dir))
|
||||||
|
if os.path.commonpath([result_abs, root_abs]) == root_abs:
|
||||||
|
return Path(result_abs)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user