testsuite: add log control-char escaping test (KI-51/52)

Transfers files whose names carry C0 (0x1b) and C1 (0x9b) control bytes via
--log-file and checks the log contains no raw control bytes (only \#NNN
escapes).  RED before the fix: logit() writes the raw filename to the log
(CWE-117), and C1 controls slip through filtered_fwrite.  Skips if the fs
rejects control-char names.

Reported-by: Leonid Bugaev
This commit is contained in:
Andrew Tridgell committed 2026-07-20 14:05:32 +10:00
1 parent 0d0e505902
commit 674c175e31
1 file changed
+40
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env python3
# Regression for KI-51/52: the log file must escape control characters in a
# (possibly attacker-controlled) filename, so an admin who cat's the log can't
# have terminal escapes injected (CWE-117). Covers both C0 (< 0x20) and the C1
# range (0x80-0x9f, incl CSI 0x9b) on the log path.
import os
from rsyncfns import SCRATCHDIR, run_rsync, test_fail, test_skipped
base = SCRATCHDIR / 'logctl'
src = base / 'src'
dst = base / 'dst'
src.mkdir(parents=True, exist_ok=True)
dst.mkdir(parents=True, exist_ok=True)
log = base / 'rsync.log'
srcb = os.fsencode(str(src))
made = 0
for raw in (b'c0_\x1b_esc', b'c1_\x9b_csi'):
try:
with open(srcb + b'/' + raw, 'wb') as fh:
fh.write(b'x')
made += 1
except OSError:
pass # a filesystem that rejects control-char names (e.g. Cygwin)
if made == 0:
test_skipped("filesystem rejects control-char filenames")
run_rsync('-rv', f'--log-file={log}', f'{src}/', f'{dst}/')
data = log.read_bytes()
if b'\x1b' in data:
test_fail("raw C0 ESC (0x1b) byte left un-escaped in the log file")
if b'\x9b' in data:
test_fail("raw C1 CSI (0x9b) byte left un-escaped in the log file")
if b'\\#' not in data:
test_fail("expected escaped \\#NNN sequences in the log file, found none")
print(f'log-control-chars: {made} control-char name(s) escaped in the log file')