Skip to content

model: fix EscapeName incorrectly preserving colon in label names#889

Open
shankalpaLamichhane wants to merge 1 commit into
prometheus:mainfrom
shankalpaLamichhane:fix/escape-colon-in-labelnames
Open

model: fix EscapeName incorrectly preserving colon in label names#889
shankalpaLamichhane wants to merge 1 commit into
prometheus:mainfrom
shankalpaLamichhane:fix/escape-colon-in-labelnames

Conversation

@shankalpaLamichhane
Copy link
Copy Markdown

Fixes prometheus/prometheus#18380

Colons are only valid in metric names, not label names. EscapeName was using the same isValidLegacyRune for both contexts, causing colons to be preserved when escaping label names.

Added ValidationContext parameter to isValidLegacyRune and EscapeName so callers can specify metric or label context. EscapeMetricFamily updated to pass the correct context at each call site.

@roidelapluie @gotjosh

Colons are only valid in metric names, not label names. EscapeName was
using the same isValidLegacyRune for both contexts, causing colons to be
preserved when escaping label names.

Added ValidationContext parameter to isValidLegacyRune and EscapeName so
callers can specify metric or label context. EscapeMetricFamily updated to
pass the correct context at each call site.

Fixes prometheus/prometheus#18380

Signed-off-by: shankalpaLamichhane <shankalpa.lamichhane@gmail.com>
@krajorama krajorama requested review from aknuds1 and ywwg May 12, 2026 11:45
@bwplotka
Copy link
Copy Markdown
Member

Hello from the Bug Scrub

Please not mark this issue as Fixes (it will auto close the issue) - there's likely more to happen (e.g. import) to fully fix the issue.

Copy link
Copy Markdown
Member

@ywwg ywwg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for putting this together. I made some notes -- please make sure to find all the places we are incorrectly calling IsValidLegacyMetricName on labels and make sure the tests are covering those cases as well.

Comment thread model/metric.go

for _, l := range m.Label {
if l.GetName() == MetricNameLabel {
if l.Value == nil || IsValidLegacyMetricName(l.GetValue()) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these calls also need fixing

Comment thread model/metric.go
Comment on lines +574 to +575
// Colons are reserved for metric names (e.g. recording rules) only.
// They have never been valid in label names.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to talk about history in comments

Suggested change
// Colons are reserved for metric names (e.g. recording rules) only.
// They have never been valid in label names.
// Colons are reserved for metric names (e.g. recording rules) only.

Comment thread model/metric.go

func isValidLegacyRune(b rune, i int) bool {
return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || b == ':' || (b >= '0' && b <= '9' && i > 0)
func isValidLegacyRune(r rune, i int, ctx ValidationContext) bool {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ctx is go idiomatic for a context.Context, and it's confusing to use it for this simple enum value. this is more of a NameType than a validation context.

Comment thread model/metric.go
Comment on lines +89 to +97
// ValidationContext determines whether a name being validated or escaped
// is a metric name or a label name. Colons are only valid in metric names.
type ValidationContext int

const (
ContextMetric ValidationContext = iota
ContextLabel
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"context" is not quite the right word here, we want something that describes a concrete object, not the context that it exists in. How about:

Suggested change
// ValidationContext determines whether a name being validated or escaped
// is a metric name or a label name. Colons are only valid in metric names.
type ValidationContext int
const (
ContextMetric ValidationContext = iota
ContextLabel
)
// NameClass determines whether a name being validated or escaped
// is a metric name or a label name.
type NameClass int
const (
MetricClass NameClass = iota
LabelClass
)

A few more options: AttributeClass, NameType, ...

Comment thread model/metric_test.go

// TestEscapeNameLabelContext verifies that colons are correctly escaped when
// EscapeName is called with ContextLabel.
// Colons are reserved for metric names only and must never be preserved in escaped label names.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not strictly true. We do preserve colons in label names when we are doing NoEscaping. (whereas quote marks must be escaped even during "NoEscaping"...)

Suggested change
// Colons are reserved for metric names only and must never be preserved in escaped label names.
// Colons are legacy valid in metric names, but not label names.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

EscapeMetricFamily incorrectly preserves : in label names

3 participants