From 687640621ed20e1e388546fa7147cd65a9960a26 Mon Sep 17 00:00:00 2001 From: "SREDevOps.org Agent" Date: Wed, 21 Oct 2026 14:24:01 -0300 Subject: [PATCH] Forward the upstream query byte-for-byte (#8) filtered_query() round-tripped the query through parse_qsl/urlencode, which percent-encoded DSH's Vite-style combined-module URLs. The client asks for "plugins/??@deepseek-ai/dsh-client-modules/client.js&rev=..." - the raw query itself starts with "?" - so the gateway sent "%3F%40deepseek-ai..." and the upstream answered 404. The browser then failed the boot with: client-modules: HTML did not preload @deepseek-ai/dsh-client-modules/client.js Strip only token parameters and keep every other raw part untouched. Add regression tests for the combined-module URL and an encoded token key. Co-authored-by: SREDevOpsAgents --- .../gateway/gateway.py | 34 ++++++++++++++----- .../gateway/test_gateway.py | 11 ++++++ 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/examples/dsh-agents-as-a-service/gateway/gateway.py b/examples/dsh-agents-as-a-service/gateway/gateway.py index ccd0cc8..7429c86 100644 --- a/examples/dsh-agents-as-a-service/gateway/gateway.py +++ b/examples/dsh-agents-as-a-service/gateway/gateway.py @@ -43,7 +43,15 @@ import re import secrets import time from dataclasses import dataclass, field -from urllib.parse import parse_qs, parse_qsl, quote, urlencode, urlsplit, urlunsplit +from urllib.parse import ( + parse_qs, + parse_qsl, + quote, + unquote_plus, + urlencode, + urlsplit, + urlunsplit, +) import anyio import httpx @@ -254,13 +262,23 @@ def rewrite_location(location: str, user: str) -> str: def filtered_query(query_string: str) -> str: - """Drop ``?token=`` before a request is sent upstream.""" - pairs = [ - (key, value) - for key, value in parse_qsl(query_string, keep_blank_values=True) - if key != "token" - ] - return urlencode(pairs) + """Drop ``?token=`` before a request is sent upstream. + + The remaining query is forwarded byte-for-byte. DSH's combined-module + URLs look like ``plugins/??@deepseek-ai/dsh-client-modules/client.js`` — + the raw query itself begins with ``?`` and DSH routes it verbatim. + Re-encoding through ``parse_qsl``/``urlencode`` turns that into ``%3F…``, + the upstream answers 404, and the client boot dies with "HTML did not + preload @deepseek-ai/dsh-client-modules/client.js". + """ + if not query_string: + return "" + kept = [] + for part in query_string.split("&"): + if unquote_plus(part.split("=", 1)[0]) == "token": + continue + kept.append(part) + return "&".join(kept) def _env_int(name: str, default: int) -> int: diff --git a/examples/dsh-agents-as-a-service/gateway/test_gateway.py b/examples/dsh-agents-as-a-service/gateway/test_gateway.py index e8c6f89..898450c 100644 --- a/examples/dsh-agents-as-a-service/gateway/test_gateway.py +++ b/examples/dsh-agents-as-a-service/gateway/test_gateway.py @@ -389,6 +389,17 @@ def test_filtered_query_removes_token_only(): assert filtered_query("a=1&token=x&b=2&token=y") == "a=1&b=2" +def test_filtered_query_preserves_vite_combined_module_url(): + """DSH's ``plugins/??`` query must reach the upstream verbatim.""" + raw = "?@deepseek-ai/dsh-client-modules/client.js&rev=a1bf7d6a56f9" + assert filtered_query(raw) == raw + assert filtered_query(raw + "&token=x") == raw + + +def test_filtered_query_drops_an_encoded_token_parameter(): + assert filtered_query("a=1&to%6Ben=x&b=2") == "a=1&b=2" + + def test_token_matches_is_timing_safe_hash_check(): claim = { "metadata": {