master
c 214 lines 11.3 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "../libnetdata.h"
4
5 // defined again in health/rrdvar.h — keep both in sync
6 #ifndef RRDVAR_MAX_LENGTH
7 #define RRDVAR_MAX_LENGTH 1024
8 #endif
9
10 // --------------------------------------------------------------------------------------------------------------------
11 // RRD string sanitization (for units, title, family, context, plugin, module)
12 //
13 // This is more permissive than chart names - it preserves most printable ASCII.
14 // The only transformations are:
15 // - control characters → space (deduplicated by text_sanitize)
16 // - backslash → forward slash (to avoid escape sequence issues in protocols)
17 // - double quote → single quote (to avoid issues with quoted string parsing)
18 //
19 // UTF-8 is preserved by text_sanitize() when called with utf=true.
20
21 unsigned char rrd_string_allowed_chars[256] = {
22 // Control characters (0-31) → space
23 [0] = '\0', // NUL stays NUL (string terminator)
24 [1] = ' ', [2] = ' ', [3] = ' ', [4] = ' ', [5] = ' ', [6] = ' ', [7] = ' ', [8] = ' ',
25 ['\t'] = ' ', ['\n'] = ' ', ['\v'] = ' ', ['\f'] = ' ', ['\r'] = ' ',
26 [14] = ' ', [15] = ' ', [16] = ' ', [17] = ' ', [18] = ' ', [19] = ' ', [20] = ' ', [21] = ' ',
27 [22] = ' ', [23] = ' ', [24] = ' ', [25] = ' ', [26] = ' ', [27] = ' ', [28] = ' ', [29] = ' ',
28 [30] = ' ', [31] = ' ',
29
30 // Printable ASCII (32-126) - mostly pass through
31 [' '] = ' ',
32 ['!'] = '!', ['"'] = '\'', ['#'] = '#', ['$'] = '$', ['%'] = '%', ['&'] = '&', ['\''] = '\'',
33 ['('] = '(', [')'] = ')', ['*'] = '*', ['+'] = '+', [','] = ',', ['-'] = '-', ['.'] = '.', ['/'] = '/',
34 ['0'] = '0', ['1'] = '1', ['2'] = '2', ['3'] = '3', ['4'] = '4', ['5'] = '5', ['6'] = '6', ['7'] = '7',
35 ['8'] = '8', ['9'] = '9',
36 [':'] = ':', [';'] = ';', ['<'] = '<', ['='] = '=', ['>'] = '>', ['?'] = '?', ['@'] = '@',
37 ['A'] = 'A', ['B'] = 'B', ['C'] = 'C', ['D'] = 'D', ['E'] = 'E', ['F'] = 'F', ['G'] = 'G', ['H'] = 'H',
38 ['I'] = 'I', ['J'] = 'J', ['K'] = 'K', ['L'] = 'L', ['M'] = 'M', ['N'] = 'N', ['O'] = 'O', ['P'] = 'P',
39 ['Q'] = 'Q', ['R'] = 'R', ['S'] = 'S', ['T'] = 'T', ['U'] = 'U', ['V'] = 'V', ['W'] = 'W', ['X'] = 'X',
40 ['Y'] = 'Y', ['Z'] = 'Z',
41 ['['] = '[', ['\\'] = '/', [']'] = ']', ['^'] = '^', ['_'] = '_', ['`'] = '`',
42 ['a'] = 'a', ['b'] = 'b', ['c'] = 'c', ['d'] = 'd', ['e'] = 'e', ['f'] = 'f', ['g'] = 'g', ['h'] = 'h',
43 ['i'] = 'i', ['j'] = 'j', ['k'] = 'k', ['l'] = 'l', ['m'] = 'm', ['n'] = 'n', ['o'] = 'o', ['p'] = 'p',
44 ['q'] = 'q', ['r'] = 'r', ['s'] = 's', ['t'] = 't', ['u'] = 'u', ['v'] = 'v', ['w'] = 'w', ['x'] = 'x',
45 ['y'] = 'y', ['z'] = 'z',
46 ['{'] = '{', ['|'] = '|', ['}'] = '}', ['~'] = '~',
47
48 // DEL and high bytes (127-255) - text_sanitize handles UTF-8, these are fallbacks for orphan bytes
49 [127] = ' ',
50 [128] = ' ', [129] = ' ', [130] = ' ', [131] = ' ', [132] = ' ', [133] = ' ', [134] = ' ', [135] = ' ',
51 [136] = ' ', [137] = ' ', [138] = ' ', [139] = ' ', [140] = ' ', [141] = ' ', [142] = ' ', [143] = ' ',
52 [144] = ' ', [145] = ' ', [146] = ' ', [147] = ' ', [148] = ' ', [149] = ' ', [150] = ' ', [151] = ' ',
53 [152] = ' ', [153] = ' ', [154] = ' ', [155] = ' ', [156] = ' ', [157] = ' ', [158] = ' ', [159] = ' ',
54 [160] = ' ', [161] = ' ', [162] = ' ', [163] = ' ', [164] = ' ', [165] = ' ', [166] = ' ', [167] = ' ',
55 [168] = ' ', [169] = ' ', [170] = ' ', [171] = ' ', [172] = ' ', [173] = ' ', [174] = ' ', [175] = ' ',
56 [176] = ' ', [177] = ' ', [178] = ' ', [179] = ' ', [180] = ' ', [181] = ' ', [182] = ' ', [183] = ' ',
57 [184] = ' ', [185] = ' ', [186] = ' ', [187] = ' ', [188] = ' ', [189] = ' ', [190] = ' ', [191] = ' ',
58 [192] = ' ', [193] = ' ', [194] = ' ', [195] = ' ', [196] = ' ', [197] = ' ', [198] = ' ', [199] = ' ',
59 [200] = ' ', [201] = ' ', [202] = ' ', [203] = ' ', [204] = ' ', [205] = ' ', [206] = ' ', [207] = ' ',
60 [208] = ' ', [209] = ' ', [210] = ' ', [211] = ' ', [212] = ' ', [213] = ' ', [214] = ' ', [215] = ' ',
61 [216] = ' ', [217] = ' ', [218] = ' ', [219] = ' ', [220] = ' ', [221] = ' ', [222] = ' ', [223] = ' ',
62 [224] = ' ', [225] = ' ', [226] = ' ', [227] = ' ', [228] = ' ', [229] = ' ', [230] = ' ', [231] = ' ',
63 [232] = ' ', [233] = ' ', [234] = ' ', [235] = ' ', [236] = ' ', [237] = ' ', [238] = ' ', [239] = ' ',
64 [240] = ' ', [241] = ' ', [242] = ' ', [243] = ' ', [244] = ' ', [245] = ' ', [246] = ' ', [247] = ' ',
65 [248] = ' ', [249] = ' ', [250] = ' ', [251] = ' ', [252] = ' ', [253] = ' ', [254] = ' ', [255] = ' '
66 };
67
68 // --------------------------------------------------------------------------------------------------------------------
69
70 /*
71 * control characters become space, which are deduplicated.
72 *
73 * Character Name Sym To Why
74 * ---------------- --- --- -------------------------------------------------------------------------------------
75 * space [ ] -> [_]
76 * exclamation mark [!] -> [_] (only when it is the first character) simple patterns negation
77 * double quotes ["] -> [_] needs escaping when parsing
78 * dollar [$] -> [_] health variables and security in alarm-notify.sh, cgroup-name.sh, etc.
79 * percent [%] -> [_] http GET encoded characters
80 * ampersand [&] -> [_] http GET fields separator
81 * single quote ['] -> [_] needs escaping when parsing
82 * asterisk [*] -> [_] simple pattern wildcard
83 * plus [+] -> [_] http GET space
84 * comma [,] -> [.] list separator (probably not used today)
85 * equal [=] -> [_] plugins.d protocol separator
86 * question mark [?] -> [_] http GET query string separator
87 * at [@] -> [_] hostname separator (on the UI)
88 * apostrophe [`] -> [_] bash expansion (security in alarm-notify.sh and other shell scripts)
89 * pipe [|] -> [_] list separator (simple patterns and http GET)
90 * backslash [\] -> [/] to avoid interfering with escaping logic
91 */
92
93 unsigned char chart_names_allowed_chars[256] = {
94 [0] = '\0', [1] = ' ', [2] = ' ', [3] = ' ', [4] = ' ', [5] = ' ', [6] = ' ', [7] = ' ', [8] = ' ',
95
96 // control characters to be treated as spaces
97 ['\t'] = ' ', ['\n'] = ' ', ['\v'] = ' ', ['\f'] = ' ', ['\r'] = ' ',
98
99 [14] = ' ', [15] = ' ', [16] = ' ', [17] = ' ', [18] = ' ', [19] = ' ', [20] = ' ', [21] = ' ',
100 [22] = ' ', [23] = ' ', [24] = ' ', [25] = ' ', [26] = ' ', [27] = ' ', [28] = ' ', [29] = ' ',
101 [30] = ' ', [31] = ' ',
102
103 // symbols
104 [' '] = ' ', ['!'] = '!', ['"'] = '_', ['#'] = '#', ['$'] = '_', ['%'] = '_', ['&'] = '_', ['\''] = '_',
105 ['('] = '(', [')'] = ')', ['*'] = '_', ['+'] = '_', [','] = '.', ['-'] = '-', ['.'] = '.', ['/'] = '/',
106
107 // numbers
108 ['0'] = '0', ['1'] = '1', ['2'] = '2', ['3'] = '3', ['4'] = '4', ['5'] = '5', ['6'] = '6', ['7'] = '7',
109 ['8'] = '8', ['9'] = '9',
110
111 // symbols
112 [':'] = ':', [';'] = ';', ['<'] = '<', ['='] = '_', ['>'] = '>', ['?'] = '_', ['@'] = '_',
113
114 // capitals
115 ['A'] = 'A', ['B'] = 'B', ['C'] = 'C', ['D'] = 'D', ['E'] = 'E', ['F'] = 'F', ['G'] = 'G', ['H'] = 'H',
116 ['I'] = 'I', ['J'] = 'J', ['K'] = 'K', ['L'] = 'L', ['M'] = 'M', ['N'] = 'N', ['O'] = 'O', ['P'] = 'P',
117 ['Q'] = 'Q', ['R'] = 'R', ['S'] = 'S', ['T'] = 'T', ['U'] = 'U', ['V'] = 'V', ['W'] = 'W', ['X'] = 'X',
118 ['Y'] = 'Y', ['Z'] = 'Z',
119
120 // symbols
121 ['['] = '[', ['\\'] = '/', [']'] = ']', ['^'] = '_', ['_'] = '_', ['`'] = '_',
122
123 // lower
124 ['a'] = 'a', ['b'] = 'b', ['c'] = 'c', ['d'] = 'd', ['e'] = 'e', ['f'] = 'f', ['g'] = 'g', ['h'] = 'h',
125 ['i'] = 'i', ['j'] = 'j', ['k'] = 'k', ['l'] = 'l', ['m'] = 'm', ['n'] = 'n', ['o'] = 'o', ['p'] = 'p',
126 ['q'] = 'q', ['r'] = 'r', ['s'] = 's', ['t'] = 't', ['u'] = 'u', ['v'] = 'v', ['w'] = 'w', ['x'] = 'x',
127 ['y'] = 'y', ['z'] = 'z',
128
129 // symbols
130 ['{'] = '{', ['|'] = '_', ['}'] = '}', ['~'] = '~',
131
132 // rest
133 [127] = ' ', [128] = ' ', [129] = ' ', [130] = ' ', [131] = ' ', [132] = ' ', [133] = ' ', [134] = ' ',
134 [135] = ' ', [136] = ' ', [137] = ' ', [138] = ' ', [139] = ' ', [140] = ' ', [141] = ' ', [142] = ' ',
135 [143] = ' ', [144] = ' ', [145] = ' ', [146] = ' ', [147] = ' ', [148] = ' ', [149] = ' ', [150] = ' ',
136 [151] = ' ', [152] = ' ', [153] = ' ', [154] = ' ', [155] = ' ', [156] = ' ', [157] = ' ', [158] = ' ',
137 [159] = ' ', [160] = ' ', [161] = ' ', [162] = ' ', [163] = ' ', [164] = ' ', [165] = ' ', [166] = ' ',
138 [167] = ' ', [168] = ' ', [169] = ' ', [170] = ' ', [171] = ' ', [172] = ' ', [173] = ' ', [174] = ' ',
139 [175] = ' ', [176] = ' ', [177] = ' ', [178] = ' ', [179] = ' ', [180] = ' ', [181] = ' ', [182] = ' ',
140 [183] = ' ', [184] = ' ', [185] = ' ', [186] = ' ', [187] = ' ', [188] = ' ', [189] = ' ', [190] = ' ',
141 [191] = ' ', [192] = ' ', [193] = ' ', [194] = ' ', [195] = ' ', [196] = ' ', [197] = ' ', [198] = ' ',
142 [199] = ' ', [200] = ' ', [201] = ' ', [202] = ' ', [203] = ' ', [204] = ' ', [205] = ' ', [206] = ' ',
143 [207] = ' ', [208] = ' ', [209] = ' ', [210] = ' ', [211] = ' ', [212] = ' ', [213] = ' ', [214] = ' ',
144 [215] = ' ', [216] = ' ', [217] = ' ', [218] = ' ', [219] = ' ', [220] = ' ', [221] = ' ', [222] = ' ',
145 [223] = ' ', [224] = ' ', [225] = ' ', [226] = ' ', [227] = ' ', [228] = ' ', [229] = ' ', [230] = ' ',
146 [231] = ' ', [232] = ' ', [233] = ' ', [234] = ' ', [235] = ' ', [236] = ' ', [237] = ' ', [238] = ' ',
147 [239] = ' ', [240] = ' ', [241] = ' ', [242] = ' ', [243] = ' ', [244] = ' ', [245] = ' ', [246] = ' ',
148 [247] = ' ', [248] = ' ', [249] = ' ', [250] = ' ', [251] = ' ', [252] = ' ', [253] = ' ', [254] = ' ',
149 [255] = ' '
150 };
151
152 static inline void sanitize_chart_name(char *dst, const char *src, size_t dst_size) {
153 // text_sanitize deduplicates spaces
154 text_sanitize((unsigned char *)dst, (const unsigned char *)src, dst_size,
155 chart_names_allowed_chars, true, "", NULL);
156
157 char *d = dst;
158
159 // do not accept ! as the first character
160 if(*d == '!') *d = '_';
161
162 // convert remaining spaces to underscores
163 while(*d) {
164 if(*d == ' ') *d = '_';
165 d++;
166 }
167 }
168
169 // make sure the supplied string
170 // is good for a netdata chart/dimension ID/NAME
171 void netdata_fix_chart_name(char *s) {
172 sanitize_chart_name(s, s, strlen(s) + 1);
173 }
174
175 void netdata_fix_chart_id(char *s) {
176 sanitize_chart_name(s, s, strlen(s) + 1);
177 // size_t len = strlen(s);
178 // char buf[len + 1];
179 //
180 // text_sanitize((unsigned char *)buf, (const unsigned char *)s, sizeof(buf),
181 // chart_names_allowed_chars, true, "", NULL);
182 //
183 // if(memcmp(s, buf, sizeof(buf)) == 0)
184 // // they are the same
185 // return;
186 //
187 // // they differ
188 // XXH128_hash_t hash = XXH3_128bits(s, len);
189 // ND_UUID *uuid = (ND_UUID *)&hash;
190 // internal_fatal(sizeof(hash) != sizeof(ND_UUID), "XXH128 and ND_UUID do not have the same size");
191 // buf[0] = 'x';
192 // buf[1] = 'x';
193 // buf[2] = 'h';
194 // buf[3] = '_';
195 // uuid_unparse_lower_compact(uuid->uuid, &buf[4]);
196 }
197
198 char *rrdset_strncpyz_name(char *dst, const char *src, size_t dst_size_minus_1) {
199 // src starts with "type."
200 sanitize_chart_name(dst, src, dst_size_minus_1 + 1);
201 return dst;
202 }
203
204 bool rrdvar_fix_name(char *variable) {
205 size_t len = strlen(variable);
206
207 char buf[RRDVAR_MAX_LENGTH + 1];
208 if(unlikely(len >= sizeof(buf)))
209 len = sizeof(buf) - 1;
210
211 memcpy(buf, variable, len + 1);
212 sanitize_chart_name(variable, variable, len + 1);
213 return memcmp(buf, variable, len + 1) != 0;
214 }