ui: Support UTF-8 label names in matchers#5089
Conversation
76d9b69 to
cc5f608
Compare
|
LGTM but the reservedChar definition is missing some of the whitespaces in https://pkg.go.dev/unicode#IsSpace |
|
Thanks @TheMeier! Good catch - I've added the missing Latin-1 whitespace characters from |
ddb03d1 to
5234076
Compare
📝 WalkthroughWalkthroughReplaced ASCII-only variable character predicate with a deny-list that disallows whitespace and specific syntax characters, allowing UTF-8 characters (including dots) in label names. Added Unicode space detection and a syntaxChars set. Tests expanded to cover dotted, hyphenated, UTF-8 label keys and comma-separated filter parsing. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment Tip You can validate your CodeRabbit configuration file in your editor.If your editor has YAML language server, you can enable auto-completion and validation by adding |
Good point @SoloJacobs, I evolved the implementation by introducing isSpace and covering all the characters in the Golang equivalent. Thanks |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
ui/app/tests/Filter.elm (1)
29-58: Cover the last operator branch and one Unicode-whitespace rejection case.The new suite hits
=,!=, and=~, but!~is still uncovered, and the hand-maintained whitespace blacklist has no direct regression test yet. One dotted-key!~case plus a NBSP failure case would keep both edges from drifting.Suggested test additions
, test "should parse dotted label name with regex match" <| \() -> Expect.equal (Just (Matcher "host.name" RegexMatch "server-.*")) (Utils.Filter.parseMatcher "host.name=~\"server-.*\"") + , test "should parse dotted label name with not regex match" <| + \() -> + Expect.equal + (Just (Matcher "host.name" NotRegexMatch "server-.*")) + (Utils.Filter.parseMatcher "host.name!~\"server-.*\"") , test "should parse dotted label name with not equal" <| \() -> Expect.equal (Just (Matcher "host.type" NotEq "test")) (Utils.Filter.parseMatcher "host.type!=\"test\"") @@ , test "should parse UTF-8 label name" <| \() -> Expect.equal (Just (Matcher "Προμηθεύς" Eq "alerts")) (Utils.Filter.parseMatcher "Προμηθεύς=\"alerts\"") + , test "should reject NBSP in label name" <| + \() -> + let + nbsp = + String.fromChar (Char.fromCode 0xA0) + in + Expect.equal + Nothing + (Utils.Filter.parseMatcher ("host" ++ nbsp ++ "name=\"server-01\""))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ui/app/tests/Filter.elm` around lines 29 - 58, Add tests exercising the uncovered not-regex operator and the Unicode-whitespace rejection: add a case asserting Utils.Filter.parseMatcher returns Just (Matcher "<dotted-key>" RegexNotMatch "<pattern>") (or the code's NotRegex/NotRegexMatch variant—use the exact constructor used elsewhere) for a dotted key using the "!~" operator (e.g., "host.name!~\"server-.*\""), and add a negative test that parsing fails (returns Nothing) when the input contains a non-breaking space (U+00A0) inside the label or around the operator (e.g., "label\u00A0=\"value\"" or similar) to ensure the hand-maintained whitespace blacklist rejects NBSP; reference Utils.Filter.parseMatcher and the Matcher constructor to locate where to add these tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@ui/app/tests/Filter.elm`:
- Around line 29-58: Add tests exercising the uncovered not-regex operator and
the Unicode-whitespace rejection: add a case asserting Utils.Filter.parseMatcher
returns Just (Matcher "<dotted-key>" RegexNotMatch "<pattern>") (or the code's
NotRegex/NotRegexMatch variant—use the exact constructor used elsewhere) for a
dotted key using the "!~" operator (e.g., "host.name!~\"server-.*\""), and add a
negative test that parsing fails (returns Nothing) when the input contains a
non-breaking space (U+00A0) inside the label or around the operator (e.g.,
"label\u00A0=\"value\"" or similar) to ensure the hand-maintained whitespace
blacklist rejects NBSP; reference Utils.Filter.parseMatcher and the Matcher
constructor to locate where to add these tests.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c47cb22c-b3bc-4e15-abad-2da67a8c76d4
📒 Files selected for processing (3)
ui/app/script.jsui/app/src/Utils/Filter.elmui/app/tests/Filter.elm
There was a problem hiding this comment.
🧹 Nitpick comments (1)
ui/app/tests/Filter.elm (1)
125-146: AddparseFilterparity tests for hyphenated and UTF-8 keys.
parseFilternow has good dotted-key coverage, but adding filter-level tests for hyphenated/UTF-8 keys (and NBSP rejection) would better protect the full parser pipeline.✅ Suggested test additions
parseFilter : Test parseFilter = describe "parseFilter" [ test "should parse filter with dotted label names" <| \() -> Expect.equal (Just [ Matcher "host.name" Eq "server-01" , Matcher "service.namespace" Eq "myapp" ] ) (Utils.Filter.parseFilter "{host.name=\"server-01\", service.namespace=\"myapp\"}") , test "should parse filter with mixed label names" <| \() -> Expect.equal (Just [ Matcher "alertname" Eq "MyAlert" , Matcher "host.name" Eq "server-01" ] ) (Utils.Filter.parseFilter "{alertname=\"MyAlert\", host.name=\"server-01\"}") + , test "should parse filter with hyphenated and UTF-8 label names" <| + \() -> + Expect.equal + (Just + [ Matcher "cluster-id" Eq "prod-us-east" + , Matcher "Προμηθεύς" Eq "alerts" + ] + ) + (Utils.Filter.parseFilter "{cluster-id=\"prod-us-east\", Προμηθεύς=\"alerts\"}") + , test "should reject NBSP in filter label name" <| + \() -> + let + nbsp = + String.fromChar (Char.fromCode 0xA0) + in + Expect.equal + Nothing + (Utils.Filter.parseFilter ("{host" ++ nbsp ++ "name=\"server-01\"}")) ]🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ui/app/tests/Filter.elm` around lines 125 - 146, Add parity tests to parseFilter to cover hyphenated and UTF-8 keys and ensure NBSP rejection: extend the parseFilter Test (using Utils.Filter.parseFilter, Matcher and Eq) with cases that assert parsing succeeds for keys like "my-key" and UTF-8 keys (e.g., "médecin" or non-ASCII characters) returning the expected Matcher list, and add a negative test that passes a filter containing a non-breaking space (NBSP) in a key and asserts the result is Nothing; keep the same test structure and Expect.equal comparisons as the existing dotted-key cases.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@ui/app/tests/Filter.elm`:
- Around line 125-146: Add parity tests to parseFilter to cover hyphenated and
UTF-8 keys and ensure NBSP rejection: extend the parseFilter Test (using
Utils.Filter.parseFilter, Matcher and Eq) with cases that assert parsing
succeeds for keys like "my-key" and UTF-8 keys (e.g., "médecin" or non-ASCII
characters) returning the expected Matcher list, and add a negative test that
passes a filter containing a non-breaking space (NBSP) in a key and asserts the
result is Nothing; keep the same test structure and Expect.equal comparisons as
the existing dotted-key cases.
Change the Elm UI parser to accept any UTF-8 character in label names
except reserved characters used in matcher syntax. This enables proper
handling of:
- Dotted label names (OpenTelemetry semantic conventions): host.name,
service.namespace
- Hyphenated label names (Kubernetes): cluster-id, app-name
- Non-ASCII UTF-8 characters: Prometheus in Greek (Προμηθεύς)
The isVarChar function now rejects only reserved characters per the
Prometheus documentation: { } ! = ~ , \ " ' ` and whitespace.
All other UTF-8 characters are allowed.
Fixes prometheus#5042
Signed-off-by: Victor Bogo <victor@37signals.com>
Align reserved characters with Go's unicode.IsSpace for Latin-1. This adds vertical tab, form feed, NEL, and NBSP which were missing. See: https://pkg.go.dev/unicode#IsSpace Signed-off-by: Victor Bogo <victor@37signals.com>
…e characters and use that alongside the syntaxChars list to detect any reserved character Signed-off-by: Victor Bogo <victor@37signals.com>
Signed-off-by: Victor Bogo <victor@37signals.com>
Signed-off-by: Victor Bogo <victor@37signals.com>
fa82fd2 to
8a1428d
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
ui/app/tests/Filter.elm (1)
125-146: Consider adding one mixed-operatorparseFiltercase.
parseFiltertests currently exercise onlyEq. Adding one comma-separated case with=~,!~, and!=would lock in full operator behavior for multi-matcher filters.Suggested test addition
parseFilter : Test parseFilter = describe "parseFilter" [ test "should parse filter with dotted label names" <| \() -> Expect.equal (Just [ Matcher "host.name" Eq "server-01" , Matcher "service.namespace" Eq "myapp" ] ) (Utils.Filter.parseFilter "{host.name=\"server-01\", service.namespace=\"myapp\"}") , test "should parse filter with mixed label names" <| \() -> Expect.equal (Just [ Matcher "alertname" Eq "MyAlert" , Matcher "host.name" Eq "server-01" ] ) (Utils.Filter.parseFilter "{alertname=\"MyAlert\", host.name=\"server-01\"}") + , test "should parse filter with dotted labels and mixed operators" <| + \() -> + Expect.equal + (Just + [ Matcher "host.name" RegexMatch "server-.*" + , Matcher "service.namespace" NotRegexMatch "dev-.*" + , Matcher "cluster-id" NotEq "staging" + ] + ) + (Utils.Filter.parseFilter "{host.name=~\"server-.*\", service.namespace!~\"dev-.*\", cluster-id!=\"staging\"}") ]🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ui/app/tests/Filter.elm` around lines 125 - 146, Add a new test case inside the parseFilter describe block to validate mixed operators for multi-matcher filters: call Utils.Filter.parseFilter with a comma-separated input that uses =~, !~ and != (in addition to =) and assert it returns Just with Matchers using the appropriate operators (e.g., Matcher "labelA" ReMatches "...", Matcher "labelB" NotReMatches "...", Matcher "labelC" NotEq "..." etc.). Place the case alongside the existing tests in the parseFilter Test value so it verifies operator parsing for multi-matcher inputs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@ui/app/tests/Filter.elm`:
- Around line 125-146: Add a new test case inside the parseFilter describe block
to validate mixed operators for multi-matcher filters: call
Utils.Filter.parseFilter with a comma-separated input that uses =~, !~ and !=
(in addition to =) and assert it returns Just with Matchers using the
appropriate operators (e.g., Matcher "labelA" ReMatches "...", Matcher "labelB"
NotReMatches "...", Matcher "labelC" NotEq "..." etc.). Place the case alongside
the existing tests in the parseFilter Test value so it verifies operator parsing
for multi-matcher inputs.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1aa65b87-022b-4650-82a7-38f46006e99d
📒 Files selected for processing (3)
ui/app/script.jsui/app/src/Utils/Filter.elmui/app/tests/Filter.elm
|
Thanks @SoloJacobs, do you have any idea on when a next release might be live? |
|
I keep this file, up to date: https://github.com/prometheus/alertmanager/blob/main/RELEASE.md (It often changes, but no such plans, yet). |
* [CHANGE] `go get github.com/prometheus/alertmanager/ui` will now fail as compiled UI assets are no longer checked into the repository. Downstream builds that rely on these assets being present in the source tree must now build the UI from source. prometheus#5113 * [CHANGE] The '--enable-feature=auto-gomaxprocs' option is deprecated and will be removed in v0.33. This flag currently has no effect and can be safely removed from any startup scripts. prometheus#5090 * [CHANGE] Update internal function signatures across multiple packages. This affects any project that integrates `Alertmanager` code. * [ENHANCEMENT] Add static asset caching. prometheus#5113 * [ENHANCEMENT] Reduce memory allocations through pre-sizing collections and batch allocation. prometheus#5020 * [ENHANCEMENT] Replace help with documentation in navigation bar. prometheus#4943 * [ENHANCEMENT] docs(ha): Update high availability documentation. prometheus#5136 * [ENHANCEMENT] docs: Add `auth_secret_file` for smtp in document. prometheus#5036 * [ENHANCEMENT] docs: Add description for global `telegram_bot_token`. prometheus#5114 * [ENHANCEMENT] docs: Add note about notifier timeouts. prometheus#5077 * [ENHANCEMENT] docs: Fix `force_implicit_tls` config field name. prometheus#5030 * [ENHANCEMENT] docs: Link community supported integrations. prometheus#4978 * [ENHANCEMENT] docs: Remove duplicate header. prometheus#5034 * [ENHANCEMENT] docs: Update mutual tls reference in high availability documentation. prometheus#5120 * [ENHANCEMENT] tracing: Use noop spans when tracing disabled. prometheus#5118 * [FEATURE] Add silence annotations. prometheus#4965 * [FEATURE] Add silence logging option. prometheus#4163 * [FEATURE] Add support for multiple matcher set silences. prometheus#4957 * [FEATURE] Add the reason for notifying in dedup stage. prometheus#4971 * [FEATURE] mattermost: Flatten attachments into top-level config. prometheus#5009 * [FEATURE] mattermost: Support global webhook url. prometheus#4998 * [FEATURE] slack: Add default color from template. prometheus#5014 * [FEATURE] slack: Allow receiver to edit existing messages. prometheus#5007 * [FEATURE] template: Add dict, map and append functions. prometheus#5093 * [FEATURE] webhook: Add full payload templating support for notifier. prometheus#5011 * [BUGFIX] config: Check for empty cluster tls client config. prometheus#5126 * [BUGFIX] config: Don't crash upon reading empty config for notifier. prometheus#4979 * [BUGFIX] config: Fix ipv6 address handling in hostport.string(). prometheus#5040 * [BUGFIX] mattermost: Omit empty text field in notifications. prometheus#4985 * [BUGFIX] telegram: Send fallback message when notification exceeds character limit. prometheus#5074 * [BUGFIX] ui: Fix escaping for matcher values with quotes. prometheus#4862 * [BUGFIX] ui: Handle special chars in silence regex-matchers. prometheus#4942 * [BUGFIX] ui: Support utf-8 label names in matchers. prometheus#5089 Signed-off-by: Solomon Jacobs <solomonjacobs@protonmail.com>
* [CHANGE] `go get github.com/prometheus/alertmanager/ui` will now fail as compiled UI assets are no longer checked into the repository. Downstream builds that rely on these assets being present in the source tree must now build the UI from source. #5113 * [CHANGE] The '--enable-feature=auto-gomaxprocs' option is deprecated and will be removed in v0.33. This flag currently has no effect and can be safely removed from any startup scripts. #5090 * [CHANGE] Update internal function signatures across multiple packages. This affects any project that integrates `Alertmanager` code. * [ENHANCEMENT] Add static asset caching. #5113 * [ENHANCEMENT] Reduce memory allocations through pre-sizing collections and batch allocation. #5020 * [ENHANCEMENT] Replace help with documentation in navigation bar. #4943 * [ENHANCEMENT] docs(ha): Update high availability documentation. #5136 * [ENHANCEMENT] docs: Add `auth_secret_file` for smtp in document. #5036 * [ENHANCEMENT] docs: Add description for global `telegram_bot_token`. #5114 * [ENHANCEMENT] docs: Add note about notifier timeouts. #5077 * [ENHANCEMENT] docs: Fix `force_implicit_tls` config field name. #5030 * [ENHANCEMENT] docs: Link community supported integrations. #4978 * [ENHANCEMENT] docs: Remove duplicate header. #5034 * [ENHANCEMENT] docs: Update mutual tls reference in high availability documentation. #5120 * [ENHANCEMENT] tracing: Use noop spans when tracing disabled. #5118 * [FEATURE] Add silence annotations. #4965 * [FEATURE] Add silence logging option. #4163 * [FEATURE] Add support for multiple matcher set silences. #4957 * [FEATURE] Add the reason for notifying in dedup stage. #4971 * [FEATURE] mattermost: Flatten attachments into top-level config. #5009 * [FEATURE] mattermost: Support global webhook url. #4998 * [FEATURE] slack: Add default color from template. #5014 * [FEATURE] slack: Allow receiver to edit existing messages. #5007 * [FEATURE] template: Add dict, map and append functions. #5093 * [FEATURE] webhook: Add full payload templating support for notifier. #5011 * [BUGFIX] config: Check for empty cluster tls client config. #5126 * [BUGFIX] config: Don't crash upon reading empty config for notifier. #4979 * [BUGFIX] config: Fix ipv6 address handling in hostport.string(). #5040 * [BUGFIX] mattermost: Omit empty text field in notifications. #4985 * [BUGFIX] telegram: Send fallback message when notification exceeds character limit. #5074 * [BUGFIX] ui: Fix escaping for matcher values with quotes. #4862 * [BUGFIX] ui: Handle special chars in silence regex-matchers. #4942 * [BUGFIX] ui: Support utf-8 label names in matchers. #5089 Signed-off-by: Solomon Jacobs <solomonjacobs@protonmail.com>

Fixes #5042
Summary
The Elm UI parser now accepts any UTF-8 character in label names except reserved characters used in matcher syntax. This enables proper handling of:
host.name,service.namespacecluster-id,app-nameΠρομηθεύς(Prometheus in Greek)Changes
The
isVarCharfunction was changed from a whitelist approach ([a-zA-Z_0-9]) to a blacklist approach that rejects only reserved characters per the Prometheus documentation:{ } ! = ~ , \ " '`` and whitespace.Testing
Added 8 new test cases covering:
, !)Local tests using the compiled version


Which user-facing changes does this PR introduce?
Summary by CodeRabbit
Bug Fixes
Tests