# HG changeset patch # User Kim Alvefur # Date 1639409695 -3600 # Node ID e0a8c5b1ab4f895c6a6f79295af0e7d515ac2b1b # Parent 4e54334febc155f395e588813ed923de479713fc util.format: Ensure metatable __tostring results are also sanitized diff -r 4e54334febc1 -r e0a8c5b1ab4f spec/util_format_spec.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); diff -r 4e54334febc1 -r e0a8c5b1ab4f tools/generate_format_spec.lua --- 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)); diff -r 4e54334febc1 -r e0a8c5b1ab4f util/format.lua --- 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