From 58c91153514c6e07ba3bed20473a92d87ddd3ca5 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 13 Dec 2021 16:34:55 +0100 Subject: util.format: Ensure metatable __tostring results are also sanitized --- spec/util_format_spec.lua | 16 ++++++++++++++++ tools/generate_format_spec.lua | 3 ++- util/format.lua | 3 ++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/spec/util_format_spec.lua b/spec/util_format_spec.lua index ca3025c8..184b8bd8 100644 --- a/spec/util_format_spec.lua +++ b/spec/util_format_spec.lua @@ -780,96 +780,112 @@ describe("util.format", function() 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 --git a/tools/generate_format_spec.lua b/tools/generate_format_spec.lua index f5902327..359986f7 100644 --- a/tools/generate_format_spec.lua +++ b/tools/generate_format_spec.lua @@ -18,13 +18,14 @@ local example_values = { ["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 --git a/util/format.lua b/util/format.lua index efd92e3d..e93c9096 100644 --- a/util/format.lua +++ b/util/format.lua @@ -70,7 +70,8 @@ local function format(formatstring, ...) -- 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 -- cgit v1.2.3