not->toContain("\r"); expect($clean)->not->toContain("\n"); expect($clean)->toBe('/rra/legit.rrdfetch /rra/other.rrd AVERAGE'); }); test('newline in a DS field cannot split the shell command line', function () { // rrd_maximum-style value with an embedded newline separator $payload = "DS:ds:GAUGE:600:0:|query_ifSpeed|\n;id"; $clean = rrd_strip_control_chars($payload); expect($clean)->not->toContain("\n"); }); test('all C0 controls and DEL are removed, printable bytes kept', function () { $controls = ''; for ($i = 0; $i <= 0x1f; $i++) { $controls .= chr($i); } $controls .= chr(0x7f); expect(rrd_strip_control_chars("a{$controls}b"))->toBe('ab'); // NUL specifically expect(rrd_strip_control_chars("path\x00.rrd"))->toBe('path.rrd'); // legitimate command text is untouched $cmd = "graph - --imgformat=PNG DEF:a=/rra/x.rrd:ds:AVERAGE LINE1:a#00FF00:\"Traffic\""; expect(rrd_strip_control_chars($cmd))->toBe($cmd); }); test('always returns a string, even on empty and multibyte input', function () { expect(rrd_strip_control_chars(''))->toBe(''); expect(rrd_strip_control_chars('graph'))->toBeString(); // UTF-8 multibyte content is preserved (bytes above 0x7f are not stripped) expect(rrd_strip_control_chars("t\xC3\xA9st\ndata"))->toBe("t\xC3\xA9stdata"); }); test('both the local and proxy execute paths sanitize the command line', function () { $src = file_get_contents(dirname(__DIR__, 2) . '/lib/rrd.php'); // __rrd_execute() (local pipe/shell) and __rrd_proxy_execute() (rrdp proxy) // must both pass the assembled command through the stripper before it is sent. expect(substr_count($src, '$command_line = rrd_strip_control_chars($command_line);'))->toBeGreaterThanOrEqual(2); });