From 44d52665487bf600e24bd017fc764b179f6b8beb Mon Sep 17 00:00:00 2001 From: Bryce Kwon Date: Sat, 7 Nov 2026 11:02:05 -1000 Subject: Hold the avatar stub checks to an absolute hash The stub mode checks only compared hashes with each other, and the stand-in digest took any algorithm name, so md5, a misspelt sha256 or hex in capitals passed everywhere luaossl is missing, which is every machine here. The comparisons also passed with no hash found on either side. --- tests/extensions/harness.lua | 5 ++++- tests/extensions/test-email-avatar.lua | 30 ++++++++++++++++++++++++------ 2 files changed, 28 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/tests/extensions/harness.lua b/tests/extensions/harness.lua index 78e491e..5084ab4 100644 --- a/tests/extensions/harness.lua +++ b/tests/extensions/harness.lua @@ -201,10 +201,13 @@ local function fake_digest_bytes(text) return table.concat(bytes) end --- The slice of the luaossl digest interface the avatar filters use. +-- The slice of the luaossl digest interface the avatar filters use. Both +-- services document SHA-256, so the stand-in refuses any other algorithm. function harness.stub_digest() preload("openssl.digest", { new = function(algorithm) + assert(algorithm == "sha256", + "the avatar filters hash with sha256, not " .. tostring(algorithm)) return { final = function(self, text) return fake_digest_bytes(algorithm .. "\0" .. text) diff --git a/tests/extensions/test-email-avatar.lua b/tests/extensions/test-email-avatar.lua index 40375c8..5929634 100644 --- a/tests/extensions/test-email-avatar.lua +++ b/tests/extensions/test-email-avatar.lua @@ -14,7 +14,7 @@ local script = arg[1] local mode = h.mode("stub", "missing", "real") local base_url = "https://gravatar.com/avatar/" -if script:find("libravatar", 1, true) then +if script:match("[^/]*$"):find("libravatar", 1, true) then base_url = "https://seccdn.libravatar.org/avatar/" end @@ -44,9 +44,14 @@ if mode == "missing" then h.finish() end +local prefix = base_url:gsub("([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") + +local function hash_in(attribute, output) + return output:match(attribute .. "='" .. prefix .. "([0-9a-f]+)%?") +end + local function hash_of(output) - local prefix = base_url:gsub("([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") - return output:match("src='" .. prefix .. "(%x+)%?") + return hash_in("src", output) end local out, ret = render("", "A U Thor") @@ -61,17 +66,22 @@ h.contains("the fetch carries no Referer", out, "referrerpolicy='no-referrer'") h.contains("the name follows the icon", out, "> A U Thor") h.equals("close answers zero", ret, 0) h.check("the hash is hex", hash_of(out) ~= nil) +h.equals("the dense image is of the same address", hash_in("srcset", out), hash_of(out)) +if mode == "stub" then + h.equals("the hash is of the bare address in two hex digits a byte", hash_of(out), + "8318f94a3e09d48151cb849a783e6895") +end -- The service hashes the trimmed lowercase address, so every spelling of one -- address has to reach it as the same hash. local plain = hash_of(render("author@example.com", "x")) local wrapped = hash_of(render("", "x")) local shouting = hash_of(render(" ", "x")) -h.check("angle brackets do not change the hash", plain == wrapped) +h.check("angle brackets do not change the hash", plain ~= nil and plain == wrapped) h.check("case and padding do not change the hash", plain == shouting) local other = hash_of(render("other@example.com", "x")) -h.check("a different address hashes differently", plain ~= other) +h.check("a different address hashes differently", other ~= nil and plain ~= other) out = render(nil, "A U Thor") h.equals("no address draws no icon", out, "A U Thor") @@ -85,6 +95,11 @@ h.equals("empty angle brackets draw no icon", out, "A U Thor") out = h.run({ "", "commit" }, { "A U ", "Thor" }) h.contains("the name is buffered across writes", out, "> A U Thor") +-- cgit hands the name over already escaped. +out = render("", "A & B") +h.contains("a name keeps the escaping cgit gave it", out, "> A & B") +h.excludes("and is not escaped again", out, "&amp;") + -- A commit can carry any bytes in its address, and a log page runs the -- filter once per row, so the trim has to stay linear in a run of spaces. local started = os.clock() @@ -95,9 +110,12 @@ started = os.clock() render(string.rep("<", 40000), "x") h.check("a long run of open brackets is unwrapped quickly", os.clock() - started < 0.5) -h.check("the outermost brackets are the ones taken off", +h.check("text around the brackets is dropped", hash_of(render("x y", "x")) == plain) +local reversed = hash_of(render("a>b