util.format: Ensure metatable __tostring results are also sanitized
authorKim Alvefur <zash@zash.se>
Mon, 13 Dec 2021 16:34:55 +0100
changeset 12043 e0a8c5b1ab4f
parent 12042 4e54334febc1
child 12044 337b489532b7
util.format: Ensure metatable __tostring results are also sanitized
spec/util_format_spec.lua
tools/generate_format_spec.lua
util/format.lua
--- a/spec/util_format_spec.lua	Sun Dec 12 18:38:40 2021 +0100
+++ b/spec/util_format_spec.lua	Mon Dec 13 16:34:55 2021 +0100
@@ -780,96 +780,112 @@
 			describe("to %c", function ()
 				it("works", function ()
 					assert.matches("[table: 0[xX]%x+]", format("%c", { }))
+					assert.equal("[foo \226\144\129\226\144\130\226\144\131 bar]", format("%c", setmetatable({},{__tostring=function ()return "foo \1\2\3 bar"end})))
 				end);
 			end);
 
 			describe("to %d", function ()
 				it("works", function ()
 					assert.matches("[table: 0[xX]%x+]", format("%d", { }))
+					assert.equal("[foo \226\144\129\226\144\130\226\144\131 bar]", format("%d", setmetatable({},{__tostring=function ()return "foo \1\2\3 bar"end})))
 				end);
 			end);
 
 			describe("to %i", function ()
 				it("works", function ()
 					assert.matches("[table: 0[xX]%x+]", format("%i", { }))
+					assert.equal("[foo \226\144\129\226\144\130\226\144\131 bar]", format("%i", setmetatable({},{__tostring=function ()return "foo \1\2\3 bar"end})))
 				end);
 			end);
 
 			describe("to %o", function ()
 				it("works", function ()
 					assert.matches("[table: 0[xX]%x+]", format("%o", { }))
+					assert.equal("[foo \226\144\129\226\144\130\226\144\131 bar]", format("%o", setmetatable({},{__tostring=function ()return "foo \1\2\3 bar"end})))
 				end);
 			end);
 
 			describe("to %u", function ()
 				it("works", function ()
 					assert.matches("[table: 0[xX]%x+]", format("%u", { }))
+					assert.equal("[foo \226\144\129\226\144\130\226\144\131 bar]", format("%u", setmetatable({},{__tostring=function ()return "foo \1\2\3 bar"end})))
 				end);
 			end);
 
 			describe("to %x", function ()
 				it("works", function ()
 					assert.matches("[table: 0[xX]%x+]", format("%x", { }))
+					assert.equal("[foo \226\144\129\226\144\130\226\144\131 bar]", format("%x", setmetatable({},{__tostring=function ()return "foo \1\2\3 bar"end})))
 				end);
 			end);
 
 			describe("to %X", function ()
 				it("works", function ()
 					assert.matches("[table: 0[xX]%x+]", format("%X", { }))
+					assert.equal("[foo \226\144\129\226\144\130\226\144\131 bar]", format("%X", setmetatable({},{__tostring=function ()return "foo \1\2\3 bar"end})))
 				end);
 			end);
 
 			describe("to %a", function ()
 				it("works", function ()
 					assert.matches("[table: 0[xX]%x+]", format("%a", { }))
+					assert.equal("[foo \226\144\129\226\144\130\226\144\131 bar]", format("%a", setmetatable({},{__tostring=function ()return "foo \1\2\3 bar"end})))
 				end);
 			end);
 
 			describe("to %A", function ()
 				it("works", function ()
 					assert.matches("[table: 0[xX]%x+]", format("%A", { }))
+					assert.equal("[foo \226\144\129\226\144\130\226\144\131 bar]", format("%A", setmetatable({},{__tostring=function ()return "foo \1\2\3 bar"end})))
 				end);
 			end);
 
 			describe("to %e", function ()
 				it("works", function ()
 					assert.matches("[table: 0[xX]%x+]", format("%e", { }))
+					assert.equal("[foo \226\144\129\226\144\130\226\144\131 bar]", format("%e", setmetatable({},{__tostring=function ()return "foo \1\2\3 bar"end})))
 				end);
 			end);
 
 			describe("to %E", function ()
 				it("works", function ()
 					assert.matches("[table: 0[xX]%x+]", format("%E", { }))
+					assert.equal("[foo \226\144\129\226\144\130\226\144\131 bar]", format("%E", setmetatable({},{__tostring=function ()return "foo \1\2\3 bar"end})))
 				end);
 			end);
 
 			describe("to %f", function ()
 				it("works", function ()
 					assert.matches("[table: 0[xX]%x+]", format("%f", { }))
+					assert.equal("[foo \226\144\129\226\144\130\226\144\131 bar]", format("%f", setmetatable({},{__tostring=function ()return "foo \1\2\3 bar"end})))
 				end);
 			end);
 
 			describe("to %g", function ()
 				it("works", function ()
 					assert.matches("[table: 0[xX]%x+]", format("%g", { }))
+					assert.equal("[foo \226\144\129\226\144\130\226\144\131 bar]", format("%g", setmetatable({},{__tostring=function ()return "foo \1\2\3 bar"end})))
 				end);
 			end);
 
 			describe("to %G", function ()
 				it("works", function ()
 					assert.matches("[table: 0[xX]%x+]", format("%G", { }))
+					assert.equal("[foo \226\144\129\226\144\130\226\144\131 bar]", format("%G", setmetatable({},{__tostring=function ()return "foo \1\2\3 bar"end})))
 				end);
 			end);
 
 			describe("to %q", function ()
 				it("works", function ()
 					assert.matches("{ }", format("%q", { }))
+					assert.equal("{ }", format("%q", setmetatable({},{__tostring=function ()return "foo \1\2\3 bar"end})))
 				end);
 			end);
 
 			describe("to %s", function ()
 				it("works", function ()
 					assert.matches("table: 0[xX]%x+", format("%s", { }))
+					assert.equal("foo \226\144\129\226\144\130\226\144\131 bar", format("%s", setmetatable({},{__tostring=function ()return "foo \1\2\3 bar"end})))
 				end);
 			end);
 
--- a/tools/generate_format_spec.lua	Sun Dec 12 18:38:40 2021 +0100
+++ b/tools/generate_format_spec.lua	Mon Dec 13 16:34:55 2021 +0100
@@ -18,13 +18,14 @@
 	["function"] = { function() end };
 	-- ["userdata"] = {};
 	["thread"] = { coroutine.create(function() end) };
-	["table"] = { {} };
+	["table"] = { {}, setmetatable({},{__tostring=function ()return "foo \1\2\3 bar"end}) };
 };
 local example_strings = setmetatable({
 	["nil"] = { "nil" };
 	["function"] = { "function() end" };
 	["number"] = { "97"; "-12345"; "1.5"; "73786976294838206464"; "math.huge"; "2147483647" };
 	["thread"] = { "coroutine.create(function() end)" };
+	["table"] = { "{ }", "setmetatable({},{__tostring=function ()return \"foo \\1\\2\\3 bar\"end})" }
 }, { __index = function() return {} end });
 for _, lua_type in ipairs(types) do
 	print(string.format("\t\tdescribe(\"%s\", function ()", lua_type));
--- a/util/format.lua	Sun Dec 12 18:38:40 2021 +0100
+++ b/util/format.lua	Mon Dec 13 16:34:55 2021 +0100
@@ -70,7 +70,8 @@
 			-- No UTF-8 or control characters, assumed to be the common case.
 			return
 		elseif option == "s" and t ~= "string" then
-			args[i] = tostring(arg);
+			arg = tostring(arg);
+			t = "string";
 		end
 
 		if option ~= "s" and option ~= "q" and option ~= "p" then