util.stanza: Add stricter validation for data passed to stanza builder API
authorMatthew Wild <mwild1@gmail.com>
Fri, 16 Mar 2018 14:51:24 +0000
changeset 8602 62bfc85a53c8
parent 8601 282d544d48f4
child 8603 96f20cf92b84
util.stanza: Add stricter validation for data passed to stanza builder API
spec/util_stanza_spec.lua
util/stanza.lua
--- a/spec/util_stanza_spec.lua	Fri Mar 16 14:50:28 2018 +0000
+++ b/spec/util_stanza_spec.lua	Fri Mar 16 14:51:24 2018 +0000
@@ -164,4 +164,38 @@
 			assert.are.equal(r.tags[1].tags[1].name, "service-unavailable");
 		end);
 	end);
+
+	describe("#invalid", function ()
+		it("name should be rejected", function ()
+			assert.has_error(function ()
+				st.stanza(1234);
+			end);
+			assert.has_error(function ()
+				st.stanza({});
+			end);
+			assert.has_error(function ()
+				st.stanza();
+			end);
+			assert.has_error(function ()
+				st.stanza("");
+			end);
+			assert.has_error(function ()
+				st.stanza(string.char(0xC0));
+			end);
+			assert.has_error(function ()
+				st.stanza(string.char(0xF4, 0x90, 0x80, 0x80));
+			end);
+			assert.has_error(function ()
+				st.stanza("<>");
+			end);
+			assert.has_error(function ()
+				st.stanza("&");
+			end);
+		end);
+		it("UTF-8 should be rejected", function ()
+			assert.has_error(function ()
+				st.stanza("tag"):text("hello "..string.char(0xF4, 0x90, 0x80, 0x80).." world");
+			end);
+		end);
+	end);
 end);
--- a/util/stanza.lua	Fri Mar 16 14:50:28 2018 +0000
+++ b/util/stanza.lua	Fri Mar 16 14:51:24 2018 +0000
@@ -7,6 +7,7 @@
 --
 
 
+local assert        =        assert;
 local t_insert      =  table.insert;
 local t_remove      =  table.remove;
 local t_concat      =  table.concat;
@@ -23,6 +24,8 @@
 local s_find        =   string.find;
 local os            =            os;
 
+local valid_utf8 = require "util.encodings".utf8.valid;
+
 local do_pretty_printing = not os.getenv("WINDIR");
 local getstyle, getstring;
 if do_pretty_printing then
@@ -42,7 +45,32 @@
 local stanza_mt = { __name = "stanza" };
 stanza_mt.__index = stanza_mt;
 
+local function check_name(name)
+	assert(type(name) == "string", "tag name is not a string, "..type(name));
+	assert(#name > 0, "tag name is empty");
+	assert(not s_find(name, "[<>& '\"]"), "tag name contains invalid characters");
+	assert(valid_utf8(name), "tag name is invalid utf8");
+end
+local function check_attr(attr)
+	if attr ~= nil then
+		assert(type(attr) == "table", "attribute is not a table");
+		for k, v in pairs(attr) do
+			assert(type(k) == "string", "non-string key in attributes");
+			assert(valid_utf8(k), "attribute name is not valid utf8");
+			assert(type(v) == "string", "non-string value in attributes");
+			assert(valid_utf8(v), "attribute value is not valid utf8");
+		end
+	end
+end
+local function check_text(text)
+	assert(type(text) == "string", "text is not a string");
+	assert(valid_utf8(text), "text is not valid utf8");
+end
+
 local function new_stanza(name, attr, namespaces)
+	assert(name)
+	check_name(name);
+	check_attr(attr);
 	local stanza = { name = name, attr = attr or {}, namespaces = namespaces, tags = {} };
 	return setmetatable(stanza, stanza_mt);
 end
@@ -69,6 +97,7 @@
 end
 
 function stanza_mt:text(text)
+	check_text(text);
 	local last_add = self.last_add;
 	(last_add and last_add[#last_add] or self):add_direct_child(text);
 	return self;