diff options
author | Kim Alvefur <zash@zash.se> | 2021-12-11 13:30:34 +0100 |
---|---|---|
committer | Kim Alvefur <zash@zash.se> | 2021-12-11 13:30:34 +0100 |
commit | 1eca4e8870f69699f7bc9bbd132a7bf64ca70918 (patch) | |
tree | f46fbc10f5c88e0a0468b41f9dc5718b5a7e9aec /util/format.lua | |
parent | d4c145179420e6de0d2189059ea988072a7e7e45 (diff) | |
download | prosody-1eca4e8870f69699f7bc9bbd132a7bf64ca70918.tar.gz prosody-1eca4e8870f69699f7bc9bbd132a7bf64ca70918.zip |
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.
Diffstat (limited to 'util/format.lua')
-rw-r--r-- | util/format.lua | 58 |
1 files changed, 37 insertions, 21 deletions
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 |