Skip to content

Commit 9bbdb60

Browse files
authored
Merge pull request syslog-ng#5046 from HofiOne/Fix-stats-prometheus-label-escaping
Fix stats prometheus label escaping
2 parents 25ca3ce + ac33a5c commit 9bbdb60

File tree

3 files changed

+77
-4
lines changed

3 files changed

+77
-4
lines changed

lib/stats/stats-prometheus.c

+44-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
2-
* Copyright (c) 2023 László Várady
2+
* Copyright (c) 2024 Axoflow
3+
* Copyright (c) 2023-2024 László Várady
34
*
45
* This library is free software; you can redistribute it and/or
56
* modify it under the terms of the GNU Lesser General Public
@@ -25,16 +26,56 @@
2526
#include "stats/stats-cluster.h"
2627
#include "stats/stats-counter.h"
2728
#include "timeutils/unixtime.h"
28-
#include "utf8utils.h"
29+
#include "str-utils.h"
2930
#include "scratch-buffers.h"
3031

3132
#include <string.h>
3233

34+
35+
/* Exposition format:
36+
*
37+
* A label value can be any sequence of UTF-8 characters, but the
38+
* backslash (\), double-quote ("), and line feed (\n) characters have to be
39+
* escaped as \\, \", and \n, respectively.
40+
*/
3341
static gchar *
3442
stats_format_prometheus_sanitize_label_value(const gchar *value)
3543
{
3644
GString *sanitized_value = scratch_buffers_alloc();
37-
append_unsafe_utf8_as_escaped_binary(sanitized_value, value, -1, "\"");
45+
46+
const gchar *value_end = value + strlen(value);
47+
48+
while (value < value_end)
49+
{
50+
gunichar uchar = g_utf8_get_char_validated(value, value_end - value);
51+
52+
if (G_UNLIKELY(uchar == (gunichar) -1 || uchar == (gunichar) -2))
53+
{
54+
/* double backslash to conform to the format */
55+
g_string_append_printf(sanitized_value, "\\\\x%02x", *(guint8 *) value);
56+
value++;
57+
continue;
58+
}
59+
60+
switch (uchar)
61+
{
62+
case '\\':
63+
g_string_append(sanitized_value, "\\\\");
64+
break;
65+
case '"':
66+
g_string_append(sanitized_value, "\\\"");
67+
break;
68+
case '\n':
69+
g_string_append(sanitized_value, "\\n");
70+
break;
71+
default:
72+
g_string_append_unichar_optimized(sanitized_value, uchar);
73+
break;
74+
}
75+
76+
value = g_utf8_next_char(value);
77+
}
78+
3879
return sanitized_value->str;
3980
}
4081

lib/stats/tests/test_stats_prometheus.c

+29-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
2-
* Copyright (c) 2023 László Várady
2+
* Copyright (c) 2024 Axoflow
3+
* Copyright (c) 2023-2024 László Várady
34
*
45
* This library is free software; you can redistribute it and/or
56
* modify it under the terms of the GNU Lesser General Public
@@ -164,6 +165,33 @@ Test(stats_prometheus, test_prometheus_format_sanitize)
164165
stats_cluster_free(cluster);
165166
}
166167

168+
Test(stats_prometheus, test_prometheus_format_label_escaping)
169+
{
170+
/* Exposition format:
171+
*
172+
* A label value can be any sequence of UTF-8 characters, but the
173+
* backslash (\), double-quote ("), and line feed (\n) characters have to be
174+
* escaped as \\, \", and \n, respectively.
175+
*/
176+
177+
StatsClusterLabel labels[] =
178+
{
179+
stats_cluster_label("control_chars", "a\a\tb\nc"),
180+
stats_cluster_label("backslashes", "a\\a\\t\\nb"),
181+
stats_cluster_label("quotes", "\"\"hello\""),
182+
stats_cluster_label("invalid_utf8", "a\xfa"),
183+
};
184+
StatsCluster *cluster = test_single_cluster("test_name", labels, G_N_ELEMENTS(labels));
185+
186+
assert_prometheus_format(cluster, SC_TYPE_SINGLE_VALUE,
187+
"syslogng_test_name{control_chars=\"a\a\tb\\nc\","
188+
"backslashes=\"a\\\\a\\\\t\\\\nb\","
189+
"quotes=\"\\\"\\\"hello\\\"\","
190+
"invalid_utf8=\"a\\\\xfa\"} 0\n");
191+
192+
stats_cluster_free(cluster);
193+
}
194+
167195
gchar *stats_format_prometheus_format_value(const StatsClusterKey *key, const StatsCounterItem *counter);
168196

169197
Test(stats_prometheus, test_prometheus_format_value)

news/bugfix-5046.md

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
`syslog-ng-ctl`: fix escaping of `stats prometheus`
2+
3+
Metric labels (for example, the ones produced by `metrics-probe()`) may contain control characters, invalid UTF-8 or `\`
4+
characters. In those specific rare cases, the escaping of the `stats prometheus` output was incorrect.

0 commit comments

Comments
 (0)