From 1eca4e8870f69699f7bc9bbd132a7bf64ca70918 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 11 Dec 2021 13:30:34 +0100 Subject: util.format: Ensure sanitation of strings passed to wrong format Ie. log("debug", "%d", "\1\2\3") should not result in garbage. Also optimizing for the common case of ASCII string passed to %s and early returns everywhere. Returning nil from a gsub callback keeps the original substring. --- util/format.lua | 58 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 21 deletions(-) (limited to 'util') diff --git a/util/format.lua b/util/format.lua index d452fd3d..8168ccaa 100644 --- a/util/format.lua +++ b/util/format.lua @@ -49,36 +49,52 @@ local function format(formatstring, ...) -- process each format specifier local i = 0; formatstring = formatstring:gsub("%%[^cdiouxXaAeEfgGqs%%]*[cdiouxXaAeEfgGqs%%]", function(spec) - if spec ~= "%%" then - i = i + 1; - local arg = args[i]; + if spec == "%%" then return end + i = i + 1; + local arg = args[i]; + + if arg == nil then + args[i] = "nil"; + return "(%s)"; + end + + local option = spec:sub(-1); + local t = type(arg); + + if option == "s" and t == "string" and not arg:find("[%z\1-\31\128-\255]") then + -- No UTF-8 or control characters, assumed to be the common case. + return + end - local option = spec:sub(-1); - if arg == nil then - args[i] = "nil"; - spec = "(%s)"; - elseif option == "q" then - args[i] = dump(arg); - spec = "%s"; - elseif option == "s" then + if option ~= "s" and option ~= "q" then + -- all other options expect numbers + if t ~= "number" then + -- arg isn't number as expected? arg = tostring(arg); - if arg:find("[\128-\255]") and not valid_utf8(arg) then - args[i] = dump(arg); - else - args[i] = arg:gsub("[%z\1-\8\11-\31\127]", control_symbols):gsub("\n\t?", "\n\t"); - end - elseif type(arg) ~= "number" then -- arg isn't number as expected? - args[i] = tostring(arg); - spec = "[%s]"; option = "s"; spec = "[%s]"; t = "string"; elseif expects_integer[option] and num_type(arg) ~= "integer" then args[i] = tostring(arg); - spec = "[%s]"; + return "[%s]"; + else + return -- acceptable number + end + end + + if t == "string" then + if not valid_utf8(arg) then + option = "q"; + else + args[i] = arg:gsub("[%z\1-\8\11-\31\127]", control_symbols):gsub("\n\t?", "\n\t"); + return spec; end end - return spec; + + if option == "q" then + args[i] = dump(arg); + return "%s"; + end end); -- process extra args -- cgit v1.2.3