From d5bff4d90a9e0eaf9e7156fd58dd0088ea026e38 Mon Sep 17 00:00:00 2001 From: chinesepowered <22500229+chinesepowered@users.noreply.github.com> Date: Wed, 22 Jul 2026 09:44:29 -0700 Subject: [PATCH] fix(sdk-python): require canonical form in validate_session_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit uuid.UUID() accepts several non-canonical spellings — braced {...}, urn:uuid:..., and dash-less hex — so validate_session_id let them through after the RFC 4122 variant check. The value is then forwarded to the CLI verbatim as --session-id/--resume, producing a malformed session id downstream rather than a clear error at the SDK boundary. Reject anything whose canonical form differs from the input. Case is deliberately not part of the comparison: UUID() lowercases, and an all-uppercase spelling is still valid canonical input. --- .../src/qwen_code_sdk/validation.py | 11 ++++++ .../sdk-python/tests/unit/test_validation.py | 34 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/packages/sdk-python/src/qwen_code_sdk/validation.py b/packages/sdk-python/src/qwen_code_sdk/validation.py index 5e144da4d1e..b5bc0ac293e 100644 --- a/packages/sdk-python/src/qwen_code_sdk/validation.py +++ b/packages/sdk-python/src/qwen_code_sdk/validation.py @@ -226,3 +226,14 @@ def validate_session_id(value: str, param_name: str) -> None: raise ValidationError( f"Invalid {param_name}: {value!r}. UUID variant must be RFC 4122." ) + + # UUID() also accepts braced, urn:uuid: and dash-less spellings, but the + # value is forwarded to the CLI verbatim as --session-id/--resume, so + # anything but the canonical 8-4-4-4-12 form produces a malformed id + # downstream. Case is not part of canonical form: UUID() lowercases, and + # an all-uppercase spelling is still valid input. + if str(parsed) != value.lower(): + raise ValidationError( + f"Invalid {param_name}: {value!r}. Must be a UUID in canonical " + f"8-4-4-4-12 form (got the equivalent of {str(parsed)!r})." + ) diff --git a/packages/sdk-python/tests/unit/test_validation.py b/packages/sdk-python/tests/unit/test_validation.py index 10bdfd1d05c..efa6db3152e 100644 --- a/packages/sdk-python/tests/unit/test_validation.py +++ b/packages/sdk-python/tests/unit/test_validation.py @@ -40,6 +40,40 @@ def test_rejects_invalid_resume() -> None: validate_query_options(QueryOptions(resume="not-a-uuid")) +@pytest.mark.parametrize( + "value", + [ + "{12345678-1234-4234-8234-123456781234}", + "urn:uuid:12345678-1234-4234-8234-123456781234", + "12345678123442348234123456781234", + ], +) +def test_rejects_non_canonical_session_id(value: str) -> None: + # uuid.UUID() accepts these spellings, but the value is passed to the CLI + # verbatim as --session-id, so only the canonical 8-4-4-4-12 form works. + with pytest.raises(ValidationError, match="canonical"): + validate_query_options(QueryOptions(session_id=value)) + + +@pytest.mark.parametrize( + "value", + [ + "{12345678-1234-4234-8234-123456781234}", + "urn:uuid:12345678-1234-4234-8234-123456781234", + "12345678123442348234123456781234", + ], +) +def test_rejects_non_canonical_resume(value: str) -> None: + with pytest.raises(ValidationError, match="canonical"): + validate_query_options(QueryOptions(resume=value)) + + +def test_accepts_canonical_session_id_in_either_case() -> None: + # Case is not part of canonical form; an uppercase UUID is valid input. + validate_query_options(QueryOptions(session_id=VALID_UUID)) + validate_query_options(QueryOptions(session_id=VALID_UUID.upper())) + + def test_rejects_invalid_permission_mode() -> None: with pytest.raises(ValidationError, match="Invalid permission_mode"): validate_query_options(