From 431daa0b59c6c7ce8e54a8080814684dbd2eaf79 Mon Sep 17 00:00:00 2001 From: ChuckBuilds Date: Mon, 7 Sep 2026 14:54:22 -0400 Subject: [PATCH] refactor(starlark): sanitise with basename, which CodeQL can follow Threading the checked Path through the callers was right on its own terms but moved CodeQL 29 -> 28: relative_to() is a check it cannot trace, so app_dir still resolved back to the URL parameter and all 24 path alerts stayed. os.path.basename is the sanitiser its path-injection query does follow. Applied with an equality check so this rejects rather than silently truncates -- identical behaviour to the character test above it, same error string, same status code. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_014RRtqXDCnvnY6EQwhT5CV9 --- web_interface/blueprints/api_v3.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/web_interface/blueprints/api_v3.py b/web_interface/blueprints/api_v3.py index 40f52234..15720ac3 100644 --- a/web_interface/blueprints/api_v3.py +++ b/web_interface/blueprints/api_v3.py @@ -8912,9 +8912,19 @@ def _validate_starlark_app_path(app_id: str) -> Tuple[Optional[Path], Optional[s if '..' in app_id or '/' in app_id or '\\' in app_id: return None, "Invalid app_id: contains path traversal characters" + # os.path.basename strips any directory component, so what is joined below + # cannot carry one. The equality check means this rejects rather than + # silently truncates -- behaviour is identical to the character test above, + # and it is the sanitiser CodeQL's path-injection query actually follows. + # relative_to() alone is a check it cannot trace, which is why twenty-four + # of these stayed flagged after the value was threaded through properly. + safe_name = os.path.basename(app_id) + if safe_name != app_id or safe_name in ('', '.', '..'): + return None, "Invalid app_id: contains path traversal characters" + try: base_path = _STARLARK_APPS_DIR.resolve() - app_path = (base_path / app_id).resolve() + app_path = (base_path / safe_name).resolve() try: app_path.relative_to(base_path) except ValueError: