From b35bf267e533d9853953f400e41d28f6efa5395d Mon Sep 17 00:00:00 2001 From: Abhishek B R Date: Wed, 30 Sep 2026 18:20:54 +0530 Subject: [PATCH] fix: reject tag keys that end in a newline TAG_KEY_REGEX.match lets a key like "env\n" through, because re's $ also matches just before a trailing newline. The TypeScript and Go SDKs drop that key. Use fullmatch so the whole key has to match. --- src/agentcat/modules/validation.py | 2 +- tests/test_tag_validation.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/agentcat/modules/validation.py b/src/agentcat/modules/validation.py index eb7af60..2c9aa9c 100644 --- a/src/agentcat/modules/validation.py +++ b/src/agentcat/modules/validation.py @@ -23,7 +23,7 @@ def validate_tags(tags: dict) -> Optional[dict]: valid: list[tuple[str, str]] = [] for key, value in tags.items(): - if not isinstance(key, str) or not key or not TAG_KEY_REGEX.match(key): + if not isinstance(key, str) or not key or not TAG_KEY_REGEX.fullmatch(key): write_to_log( f'Dropping invalid tag: "{key}" — key contains invalid characters or is empty' ) diff --git a/tests/test_tag_validation.py b/tests/test_tag_validation.py index 2c9c330..6a85de1 100644 --- a/tests/test_tag_validation.py +++ b/tests/test_tag_validation.py @@ -31,6 +31,12 @@ def test_drops_keys_with_invalid_characters(self, mock_log): assert validate_tags(tags) == {"valid_key": "value", "good.key": "value"} assert any("invalid!key" in call.args[0] for call in mock_log.call_args_list) + def test_drops_keys_with_trailing_newline(self, mock_log): + # re's `$` also matches just before a final "\n", so the key regex + # alone lets "env\n" through. TypeScript and Go both reject it. + tags = {"env\n": "production", "region": "us-east-1"} + assert validate_tags(tags) == {"region": "us-east-1"} + def test_drops_keys_longer_than_max(self, mock_log): long_key = "a" * (MAX_TAG_KEY_LENGTH + 1) tags = {long_key: "value", "short": "value"}