]> git.kaiwu.me - haproxy.git/commitdiff
REGTESTS: log: add a test for "show events -s"
authorWilly Tarreau <w@1wt.eu>
Tue, 1 Sep 2026 13:53:06 +0000 (15:53 +0200)
committerWilly Tarreau <w@1wt.eu>
Thu, 3 Sep 2026 05:48:23 +0000 (07:48 +0200)
The commit that added the "-s" option to "show events" was marked as hard
to test, the difficulty being to get control characters into a ring in
the first place. It is in fact easy from the configuration alone, without
involving a client at all: "Gw==", "Cg==" and "Cf==" are the base64
encodings of ESC, LF and TAB, so a log-format that passes them through
b64dec emits raw control characters into the ring, exactly like a log
field does when it is emitted without "+E".

The test then checks the three behaviours that matter:
  - the default output remains byte-for-byte faithful: the raw LF is
    still there and turns a single record into two lines,
  - "-s" replaces the ESC and the LF with '?', and the TAB with a space,
    so a record can no longer forge another one nor drive the terminal,
  - "-0 -s" still delimits the records, which the previous commit fixed.

The last check fails without that fix, and the whole test fails on any
version predating the "-s" option since it reports an unknown option.

reg-tests/log/ring_sanitize.vtc [new file with mode: 0644]

diff --git a/reg-tests/log/ring_sanitize.vtc b/reg-tests/log/ring_sanitize.vtc
new file mode 100644 (file)
index 0000000..db01b3e
--- /dev/null
@@ -0,0 +1,73 @@
+varnishtest "Check that 'show events -s' sanitizes the ring contents"
+feature ignore_unknown_macro
+
+# The log-format below deliberately emits control characters into the ring
+# without involving any client: "Gw==", "Cg==" and "Cf==" are the base64
+# encodings of ESC (0x1b), LF (0x0a) and TAB (0x09), so b64dec turns them into
+# raw bytes. This is what an attacker-influenced log field may carry when it is
+# emitted without "+E".
+#
+# Without "-s", "show events" dumps them verbatim: the ESC sequence reaches the
+# operator's terminal, and the LF splits one record into two, forging a log
+# line. With "-s", the ESC and LF become '?', and TAB becomes a space, so each
+# record stays on a single line.
+
+haproxy h1 -conf {
+    global
+    .if feature(THREAD)
+        thread-groups 1
+    .endif
+
+    ring myring
+        size 16384
+        format raw
+
+    defaults
+        mode http
+        timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+        timeout client  "${HAPROXY_TEST_TIMEOUT-5s}"
+        timeout server  "${HAPROXY_TEST_TIMEOUT-5s}"
+
+    frontend fe
+        bind "fd@${fe}"
+        log ring@myring local0
+        log-format "BEG%[str(Gw==),b64dec][2JMID%[str(Cg==),b64dec]FORGED%[str(Cf==),b64dec]END"
+        http-request return status 200
+} -start
+
+# two requests, hence two records, so that the record delimiter can be checked
+client c1 -connect ${h1_fe_sock} {
+    txreq -url "/"
+    rxresp
+    expect resp.status == 200
+} -run
+
+client c2 -connect ${h1_fe_sock} {
+    txreq -url "/"
+    rxresp
+    expect resp.status == 200
+} -run
+
+delay 0.2
+
+# the default output is byte-for-byte faithful: the raw LF is still there, so a
+# single record shows up as two lines
+haproxy h1 -cli {
+    send "show events myring"
+    expect ~ "MID\nFORGED\tEND"
+}
+
+# with -s the LF is turned into a space, so the forged line is joined back onto
+# the real one, and the ESC is replaced with '?'
+haproxy h1 -cli {
+    send "show events myring -s"
+    expect ~ "BEG\\?\\[2JMID\\?FORGED END"
+}
+
+# "-0" delimits records with a NUL byte instead of a LF. Combined with "-s" the
+# delimiter must still be emitted, otherwise consecutive records get glued
+# together and the end of one cannot be told from the start of the next.
+haproxy h1 -cli {
+    send "show events myring -0 -s"
+    expect !~ "ENDBEG"
+}