mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-01 08:48:05 +00:00
fix(dev-server): allowlist plugin_id before any path lookup
CodeQL (py/path-injection): plugin_id arrives in request input and flows
into filesystem paths via find_plugin_dir. Gate it with the same
^[a-zA-Z0-9_-]{1,64}$ allowlist the web UI's pages_v3 uses, at the
single choke point every route resolves through.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FqzC1nzTWL4kaqgMaQZFam
This commit is contained in:
+12
-1
@@ -16,6 +16,7 @@ Opens at http://localhost:5001
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import re
|
||||
import time
|
||||
import argparse
|
||||
import logging
|
||||
@@ -44,6 +45,10 @@ MAX_HEIGHT = 512
|
||||
MIN_WIDTH = 1
|
||||
MIN_HEIGHT = 1
|
||||
|
||||
# plugin_id arrives in request input and is used to build filesystem paths —
|
||||
# allowlist it (same pattern the web UI's pages_v3 uses)
|
||||
_SAFE_PLUGIN_ID_RE = re.compile(r'^[a-zA-Z0-9_-]{1,64}$')
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Plugin discovery
|
||||
@@ -106,7 +111,13 @@ def discover_plugins() -> List[Dict[str, Any]]:
|
||||
|
||||
|
||||
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
|
||||
ever naming a path outside the plugin search dirs.
|
||||
"""
|
||||
if not isinstance(plugin_id, str) or not _SAFE_PLUGIN_ID_RE.match(plugin_id):
|
||||
return None
|
||||
from src.plugin_system.plugin_loader import PluginLoader
|
||||
loader = PluginLoader()
|
||||
for search_dir in get_search_dirs():
|
||||
|
||||
Reference in New Issue
Block a user