Pull in gmock so the empty-delimiter Split assertions can keep using
EXPECT_THAT(..., ElementsAre(...)) and IsEmpty(). On failure the matcher
prints the actual vector contents, which EXPECT_TRUE(vempty.empty())
hides behind a bare 'not true'.
Switch the default Referrer-Policy from same-origin to no-referrer so the
webadmin URL (which can carry user/network names in the path) does not
leak to outbound clicks either.
Drop Pragma: no-cache; it is deprecated and modern intermediaries honor
Cache-Control. Simplify Cache-Control to a single no-store directive,
which on its own already prevents storing per RFC 9111; the previous
no-cache, must-revalidate, max-age=0 tail was HTTP/1.0-era padding.
StringTest.cpp does not include gmock, so EXPECT_THAT/ElementsAre/IsEmpty
do not compile and the unittest target fails on every CI configuration.
Use EXPECT_EQ against a VCString and EXPECT_TRUE(empty()) instead, which
keeps the test scope identical without dragging gmock into this file.
Close#2012.
Add X-Frame-Options: SAMEORIGIN, X-Content-Type-Options: nosniff and
Referrer-Policy: same-origin to every response so webadmin and module
pages are framed/sniff-protected by default. Add no-store Cache-Control
and Pragma: no-cache on dynamic responses so shared workstations can't
replay authenticated pages from browser history. Skip the cache headers
for 304 and for static asset MIME types (image, font, text/css,
application/javascript) that the existing ETag/Last-Modified path on
PrintFile already handles.
Per review feedback: the emitter is a private WriteHardeningHeaders that
writes each line via the socket directly from PrintHeader, not a public
helper returning a temporary VCString. Callers can override a default
value with AddHeader, or suppress one outright with the new public
OmitHardeningHeader(name).
Tests: drive PrintHeader on a CHTTPSock subclass that captures Write()
calls, then assert with gmock matchers (Contains(StartsWith(...))).
GCC parses "AA\xffA" greedily as \xffA (next character is a hex digit),
which is out of range for char and breaks the Linux CI build. Split the
literal into "AA\xff" "A" so the escape resolves before the next string,
yielding the intended four bytes (A, A, 0xff, A).
Per review feedback on #2017: a hand-rolled byte loop can in principle
be folded back into a short-circuiting compare by an aggressive optimizer.
Use CRYPTO_memcmp under HAVE_LIBSSL since OpenSSL is already a build
dependency for the SHA256 path. For non-OpenSSL builds, mark the
accumulator and pointers volatile and note in a comment that this is
best-effort.
CString::Equals falls through to strcmp, which short-circuits on the first
differing byte. That leaks the length of the common prefix between the
stored hash (or plain password) and the attacker's guess via response
timing. Argon2id already uses argon2id_verify which is constant-time.
Add a small ConstantTimeEquals helper and use it for the legacy MD5,
SHA256 and plain HASH_NONE branches. No new dependency: the helper is
~10 lines and works on builds without OpenSSL.
AddHeader wrote its arguments straight into the response stream. No
in-tree caller reaches it with attacker-controlled bytes today, but the
public API is exposed to module authors; one bad caller would be a
header-injection bug. Filter at the entry rather than at every caller.
CString::Replace with an empty sReplace underflowed 'p += uReplaceWidth - 1'
to SIZE_MAX and then the per-iteration 'p++' brought p back to the same
byte, so the function looped forever appending sWith to the output and
eventually OOMed.
CString::Split with empty sDelim and bAllowEmpty=false spun in the
prefix-skip loops because strncasecmp(p, "", 0) is unconditionally 0 and
p += 0 never advances.
No in-tree caller currently passes the bad argument, but the public
library API should not be a bear trap for module authors. Same shape as
#1994.
cctz::parse into a microseconds time_point internally multiplies the
parsed seconds-since-epoch by 1,000,000 in signed int64. Years past ~292k
overflow, which is UB under UBSan or -ftrapv builds. In plain production
builds the overflow silently wraps and buffer playback shows a garbage
timestamp.
Reject anything with a year longer than 5 digits before calling into
cctz. 5 digits covers every realistic IRCv3 @time tag.
base64_table uses the sentinel 0xff for bytes outside the base64 alphabet.
The old code read that through (char), producing signed -1, which made the
three (c << N) expressions in Base64Decode undefined behaviour when the
input contained any invalid byte.
Keep c and c1 as unsigned char so the shifts are well-defined. Reachable
pre-auth via CHTTPSock::ReadLine for the Authorization: Basic value.
Without this check, when uIdx >= m_vsParams.size() and the vector is
non-empty, the subtraction in the clamp condition underflows to SIZE_MAX.
GetParamsSplit() already has the equivalent check at the top of the
function; this brings GetParamsColon() in line with it.
Fixes#1994
Use user configured network on the IRC client connection message example, so it turns from `/server <znc_server_ip> 1025 Admin:<pass>` to `/server <znc_server_ip> 1025 Admin/libera:<pass>`.
Should have done this from the start... 😅