From a76ebc123b44c9443e84885a0f55b55515c7647b Mon Sep 17 00:00:00 2001 From: sarankumar1325 <120080287+sarankumar1325@users.noreply.github.com> Date: Sat, 31 Oct 2026 16:32:07 +0000 Subject: [PATCH] fix: base64-encode "." and ".." in Pushgateway URL path segments A job name or grouping key value of "." or ".." was appended to the URL as is, and URI.normalize() then removed it as a dot segment. For job "j" with grouping key {instance=".."} the request went to /metrics/job/j/, and with {instance="."} to /metrics/job/j/instance/, so the push or delete never reaches the intended group. Base64-encode these values, as client_golang does. Signed-off-by: sarankumar1325 <120080287+sarankumar1325@users.noreply.github.com> --- .../exporter/pushgateway/PushGateway.java | 10 ++++++++-- .../exporter/pushgateway/PushGatewayTest.java | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/prometheus-metrics-exporter-pushgateway/src/main/java/io/prometheus/metrics/exporter/pushgateway/PushGateway.java b/prometheus-metrics-exporter-pushgateway/src/main/java/io/prometheus/metrics/exporter/pushgateway/PushGateway.java index b7dd844ec3..5c5cef3092 100644 --- a/prometheus-metrics-exporter-pushgateway/src/main/java/io/prometheus/metrics/exporter/pushgateway/PushGateway.java +++ b/prometheus-metrics-exporter-pushgateway/src/main/java/io/prometheus/metrics/exporter/pushgateway/PushGateway.java @@ -508,7 +508,7 @@ private URL makeUrl(@Nullable ExporterPushgatewayProperties properties) StringBuilder url = new StringBuilder(getScheme(properties) + "://" + getAddress(properties) + "/metrics/"); String job = getJob(properties); - if (job.contains("/")) { + if (needsBase64(job)) { url.append("job@base64/").append(base64url(job)); } else { url.append("job/").append(URLEncoder.encode(job, "UTF-8")); @@ -518,7 +518,7 @@ private URL makeUrl(@Nullable ExporterPushgatewayProperties properties) url.append("/") .append(escapeName(entry.getKey(), EscapingScheme.VALUE_ENCODING_ESCAPING)) .append("@base64/="); - } else if (entry.getValue().contains("/")) { + } else if (needsBase64(entry.getValue())) { url.append("/") .append(escapeName(entry.getKey(), EscapingScheme.VALUE_ENCODING_ESCAPING)) .append("@base64/") @@ -533,6 +533,12 @@ private URL makeUrl(@Nullable ExporterPushgatewayProperties properties) return URI.create(url.toString()).normalize().toURL(); } + // "/" would split the value into several path segments, and "." and ".." are dot segments + // that normalize() would remove from the path, sending the request to a different group. + private boolean needsBase64(String v) { + return v.contains("/") || v.equals(".") || v.equals(".."); + } + private String base64url(String v) { return Base64.getEncoder() .encodeToString(v.getBytes(StandardCharsets.UTF_8)) diff --git a/prometheus-metrics-exporter-pushgateway/src/test/java/io/prometheus/metrics/exporter/pushgateway/PushGatewayTest.java b/prometheus-metrics-exporter-pushgateway/src/test/java/io/prometheus/metrics/exporter/pushgateway/PushGatewayTest.java index 8c33543f95..a2296db7c0 100644 --- a/prometheus-metrics-exporter-pushgateway/src/test/java/io/prometheus/metrics/exporter/pushgateway/PushGatewayTest.java +++ b/prometheus-metrics-exporter-pushgateway/src/test/java/io/prometheus/metrics/exporter/pushgateway/PushGatewayTest.java @@ -232,6 +232,25 @@ void testPushWithGroupingKeyWithSlashes() throws IOException { pg.push(); } + @Test + void testPushWithDotSegmentJobAndGroupingKey() throws IOException { + mockServerClient + .when( + request() + .withMethod("PUT") + .withPath("/metrics/job@base64/Li4=/l@base64/Lg==/l2@base64/Li4=")) + .respond(response().withStatusCode(202)); + PushGateway pg = + PushGateway.builder() + .address("localhost:" + mockServerClient.getPort()) + .registry(registry) + .job("..") + .groupingKey("l", ".") + .groupingKey("l2", "..") + .build(); + pg.push(); + } + @Test void testPushCollectorWithGroupingKey() throws IOException { mockServerClient