Skip to content

Reject an empty label name under UTF-8 validation - #1239

Open
chrikrah wants to merge 1 commit into
prometheus:masterfrom
chrikrah:fix/empty-labelname
Open

chrikrah wants to merge 1 commit into
prometheus:masterfrom
chrikrah:fix/empty-labelname

Conversation

@chrikrah

Copy link
Copy Markdown
Contributor

_validate_labelname in prometheus_client/validation.py:76 only checks that a name encodes as UTF-8 when legacy validation is off, which is the default, so an empty label name passes. The OpenMetrics 2.0 grammar allows no such key: label-key = label-name / DQUOTE escaped-string-non-empty DQUOTE. The library then writes the unquoted {="x"}, which its own parsers refuse.

# base ff9a2d3, python 3.12; repro.py builds Gauge('g', 'h', [''], registry=r), prints
# generate_latest(r) and feeds that text to parser.text_string_to_metric_families
$ python repro.py 2>&1 | grep -E '^(g|ValueError)'
g{="x"} 1.0
ValueError: unquoted UTF-8 metric name
ValueError: Invalid labels: ="x"
$ python repro.py 2>&1 | grep -E '^(g|ValueError)'   # this branch
ValueError: label name cannot be empty

On this branch the Gauge(...) call raises, worded like the error _validate_metric_name gives for an empty metric name. Exemplar keys go through the same function, so inc(exemplar={'': 'abc'}) is refused too. Legacy validation already rejected both through METRIC_LABEL_NAME_RE, and LabelName.IsValid in prometheus/common returns false for an empty name in UTF-8 mode.

parser.py:97 calls the same function, so both parsers now reject the quoted g{""="x"} 1.0 too, and a case in each parser's tests pins that. At base they returned labels={'': 'x'}. GaugeMetricFamily(labels=['']) in a custom collector still writes g{="x"} 1.0, because metric families skip every name check today, so I left them out of scope.

Verification

$ python -m pytest -q
431 passed, 14 skipped, 34 subtests passed   # this branch
428 passed, 14 skipped, 28 subtests passed   # base ff9a2d3
$ python -m pytest -q   # validation.py reverted, tests kept
9 failed, 428 passed, 14 skipped, 28 subtests passed   # six metric types, the exemplar, both parsers
$ flake8 prometheus_client/ tests/   # flake8 7.4.1
$ isort --check prometheus_client/ tests/   # isort 5.10.1
$ mypy prometheus_client tests/test_core.py tests/test_parser.py tests/openmetrics/test_parser.py   # mypy 2.4.0
Found 5 errors in 2 files   # identical at base: aiohttp stubs and metrics.py:184-210
# not run: tox on other interpreters, PyPy

There is no issue for this. #1236 edits two other lines of the same file and leaves _validate_labelname alone.

@csmarchbanks, this makes a call that used to succeed raise at construction. Its exposition already fails to parse, so I'd ship the raise without a deprecation warning first. Does that work for you?

With legacy validation off, which is the default, an empty string passes
_validate_labelname. A metric declared with labelnames=[''] then exposes
g{="x"} 1.0, which both bundled parsers reject as invalid labels. Legacy
validation already refuses it, and _validate_metric_name refuses an empty
metric name the same way.

Both parsers call the same function, so a quoted empty label name such as
a{""="x"} 1 is now refused on input as well.

Signed-off-by: Christopher Krah <48338417+chrikrah@users.noreply.github.com>

This branch has not been deployed

No deployments
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.

1 participant