Software /
code /
prosody
Comparison
util/format.lua @ 12032:3db09eb4c43b
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.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Sat, 11 Dec 2021 13:30:34 +0100 |
parent | 12031:87bc26f23d9b |
child | 12033:161f8268c4b3 |
comparison
equal
deleted
inserted
replaced
12031:87bc26f23d9b | 12032:3db09eb4c43b |
---|---|
47 | 47 |
48 | 48 |
49 -- process each format specifier | 49 -- process each format specifier |
50 local i = 0; | 50 local i = 0; |
51 formatstring = formatstring:gsub("%%[^cdiouxXaAeEfgGqs%%]*[cdiouxXaAeEfgGqs%%]", function(spec) | 51 formatstring = formatstring:gsub("%%[^cdiouxXaAeEfgGqs%%]*[cdiouxXaAeEfgGqs%%]", function(spec) |
52 if spec ~= "%%" then | 52 if spec == "%%" then return end |
53 i = i + 1; | 53 i = i + 1; |
54 local arg = args[i]; | 54 local arg = args[i]; |
55 | 55 |
56 local option = spec:sub(-1); | 56 if arg == nil then |
57 if arg == nil then | 57 args[i] = "nil"; |
58 args[i] = "nil"; | 58 return "(%s)"; |
59 spec = "(%s)"; | 59 end |
60 elseif option == "q" then | 60 |
61 args[i] = dump(arg); | 61 local option = spec:sub(-1); |
62 spec = "%s"; | 62 local t = type(arg); |
63 elseif option == "s" then | 63 |
64 if option == "s" and t == "string" and not arg:find("[%z\1-\31\128-\255]") then | |
65 -- No UTF-8 or control characters, assumed to be the common case. | |
66 return | |
67 end | |
68 | |
69 if option ~= "s" and option ~= "q" then | |
70 -- all other options expect numbers | |
71 if t ~= "number" then | |
72 -- arg isn't number as expected? | |
64 arg = tostring(arg); | 73 arg = tostring(arg); |
65 if arg:find("[\128-\255]") and not valid_utf8(arg) then | |
66 args[i] = dump(arg); | |
67 else | |
68 args[i] = arg:gsub("[%z\1-\8\11-\31\127]", control_symbols):gsub("\n\t?", "\n\t"); | |
69 end | |
70 elseif type(arg) ~= "number" then -- arg isn't number as expected? | |
71 args[i] = tostring(arg); | |
72 spec = "[%s]"; | |
73 option = "s"; | 74 option = "s"; |
74 spec = "[%s]"; | 75 spec = "[%s]"; |
75 t = "string"; | 76 t = "string"; |
76 elseif expects_integer[option] and num_type(arg) ~= "integer" then | 77 elseif expects_integer[option] and num_type(arg) ~= "integer" then |
77 args[i] = tostring(arg); | 78 args[i] = tostring(arg); |
78 spec = "[%s]"; | 79 return "[%s]"; |
80 else | |
81 return -- acceptable number | |
79 end | 82 end |
80 end | 83 end |
81 return spec; | 84 |
85 if t == "string" then | |
86 if not valid_utf8(arg) then | |
87 option = "q"; | |
88 else | |
89 args[i] = arg:gsub("[%z\1-\8\11-\31\127]", control_symbols):gsub("\n\t?", "\n\t"); | |
90 return spec; | |
91 end | |
92 end | |
93 | |
94 if option == "q" then | |
95 args[i] = dump(arg); | |
96 return "%s"; | |
97 end | |
82 end); | 98 end); |
83 | 99 |
84 -- process extra args | 100 -- process extra args |
85 while i < args_length do | 101 while i < args_length do |
86 i = i + 1; | 102 i = i + 1; |