From 149f1daa1e2e2afe603342dee051b24e96855459 Mon Sep 17 00:00:00 2001 From: Chuck Date: Sat, 11 Jul 2026 12:41:22 -0400 Subject: [PATCH] fix(dev-server): inline normpath containment barrier before render CodeQL doesn't credit the sanitization inside find_plugin_dir along this flow; apply its documented barrier (normpath + startswith against the allowed roots) inline in _parse_render_request, on the exact path that reaches the render/load sinks. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01FqzC1nzTWL4kaqgMaQZFam --- scripts/dev_server.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scripts/dev_server.py b/scripts/dev_server.py index 70f1fcf8..410d0ea6 100644 --- a/scripts/dev_server.py +++ b/scripts/dev_server.py @@ -264,6 +264,16 @@ def _parse_render_request(data): if not plugin_dir: raise LookupError(f'Plugin not found: {plugin_id}') + # Path-injection barrier: normalize (no symlink resolution — dev plugins + # are commonly symlinked into plugins/) and require the directory to sit + # inside one of the plugin search dirs before any file access. + normalized = os.path.normpath(str(plugin_dir)) + allowed_roots = [os.path.normpath(str(d)) for d in get_search_dirs()] + if not any(normalized == root or normalized.startswith(root + os.sep) + for root in allowed_roots): + raise LookupError(f'Plugin not found: {plugin_id}') + plugin_dir = Path(normalized) + manifest_path = plugin_dir / 'manifest.json' with open(manifest_path, 'r') as f: manifest = json.load(f)