comparison util/xmppstream.lua @ 14153:aa29d5144c20 0.12

util.xmppstream: Support limiting total number of descendent elements in a stanza
author Matthew Wild <mwild1@gmail.com>
date Thu, 16 Apr 2026 18:52:58 +0100
parents 3bbb1af92514
children e3ddbf7f2d3f
comparison
equal deleted inserted replaced
14152:fed0dd8dda09 14153:aa29d5144c20
41 local ns_separator = "\1"; 41 local ns_separator = "\1";
42 local ns_pattern = "^([^"..ns_separator.."]*)"..ns_separator.."?(.*)$"; 42 local ns_pattern = "^([^"..ns_separator.."]*)"..ns_separator.."?(.*)$";
43 43
44 local function dummy_cb() end 44 local function dummy_cb() end
45 45
46 local function new_sax_handlers(session, stream_callbacks, cb_handleprogress) 46 local function new_sax_handlers(session, stream_callbacks, cb_handleprogress, max_elements)
47 local xml_handlers = {}; 47 local xml_handlers = {};
48 48
49 local cb_streamopened = stream_callbacks.streamopened; 49 local cb_streamopened = stream_callbacks.streamopened;
50 local cb_streamclosed = stream_callbacks.streamclosed; 50 local cb_streamclosed = stream_callbacks.streamclosed;
51 local cb_error = stream_callbacks.error or 51 local cb_error = stream_callbacks.error or
67 local stream_lang = "en"; 67 local stream_lang = "en";
68 68
69 local stack = {}; 69 local stack = {};
70 local chardata, stanza = {}; 70 local chardata, stanza = {};
71 local stanza_size = 0; 71 local stanza_size = 0;
72 local child_quota = 0;
72 local non_streamns_depth = 0; 73 local non_streamns_depth = 0;
73 function xml_handlers:StartElement(tagname, attr) 74 function xml_handlers:StartElement(tagname, attr)
74 if stanza and #chardata > 0 then 75 if stanza and #chardata > 0 then
75 -- We have some character data in the buffer 76 -- We have some character data in the buffer
76 t_insert(stanza, t_concat(chardata)); 77 t_insert(stanza, t_concat(chardata));
82 end 83 end
83 84
84 if curr_ns ~= stream_default_ns or non_streamns_depth > 0 then 85 if curr_ns ~= stream_default_ns or non_streamns_depth > 0 then
85 attr.xmlns = curr_ns; 86 attr.xmlns = curr_ns;
86 non_streamns_depth = non_streamns_depth + 1; 87 non_streamns_depth = non_streamns_depth + 1;
88 end
89
90 if stanza and child_quota then
91 -- Check stanza complexity limits
92 child_quota = child_quota - 1;
93 if child_quota <= 0 then
94 cb_error(session, "parse-error", "resource-constraint", "Stanza exceeds permitted children");
95 if not self.stop or not self:stop() then
96 error("Failed to abort parsing");
97 end
98 return;
99 end
87 end 100 end
88 101
89 for i=1,#attr do 102 for i=1,#attr do
90 local k = attr[i]; 103 local k = attr[i];
91 attr[i] = nil; 104 attr[i] = nil;
120 if curr_ns == "jabber:client" and name ~= "iq" and name ~= "presence" and name ~= "message" then 133 if curr_ns == "jabber:client" and name ~= "iq" and name ~= "presence" and name ~= "message" then
121 cb_error(session, "invalid-top-level-element"); 134 cb_error(session, "invalid-top-level-element");
122 end 135 end
123 136
124 stanza = setmetatable({ name = name, attr = attr, tags = {} }, stanza_mt); 137 stanza = setmetatable({ name = name, attr = attr, tags = {} }, stanza_mt);
138 child_quota = max_elements;
125 else -- we are inside a stanza, so add a tag 139 else -- we are inside a stanza, so add a tag
126 if lxp_supports_bytecount then 140 if lxp_supports_bytecount then
127 stanza_size = stanza_size + self:getcurrentbytecount(); 141 stanza_size = stanza_size + self:getcurrentbytecount();
128 end 142 end
129 t_insert(stack, stanza); 143 t_insert(stack, stanza);
235 249
236 local function set_session(stream, new_session) -- luacheck: ignore 212/stream 250 local function set_session(stream, new_session) -- luacheck: ignore 212/stream
237 session = new_session; 251 session = new_session;
238 end 252 end
239 253
240 return xml_handlers, { reset = reset, set_session = set_session }; 254 local function set_max_elements(new_max_elements)
255 max_elements = new_max_elements;
256 end
257
258 return xml_handlers, {
259 reset = reset;
260 set_session = set_session;
261 set_max_elements = set_max_elements;
262 };
241 end 263 end
242 264
243 local function new(session, stream_callbacks, stanza_size_limit) 265 local function new(session, stream_callbacks, stanza_size_limit, ex)
244 -- Used to track parser progress (e.g. to enforce size limits) 266 -- Used to track parser progress (e.g. to enforce size limits)
245 local n_outstanding_bytes = 0; 267 local n_outstanding_bytes = 0;
246 local handle_progress; 268 local handle_progress;
247 if lxp_supports_bytecount then 269 if lxp_supports_bytecount then
248 function handle_progress(n_parsed_bytes) 270 function handle_progress(n_parsed_bytes)
251 stanza_size_limit = stanza_size_limit or default_stanza_size_limit; 273 stanza_size_limit = stanza_size_limit or default_stanza_size_limit;
252 elseif stanza_size_limit then 274 elseif stanza_size_limit then
253 error("Stanza size limits are not supported on this version of LuaExpat") 275 error("Stanza size limits are not supported on this version of LuaExpat")
254 end 276 end
255 277
278 local max_elements = ex and ex.max_elements;
279
256 local handlers, meta = new_sax_handlers(session, stream_callbacks, handle_progress); 280 local handlers, meta = new_sax_handlers(session, stream_callbacks, handle_progress);
257 local parser = new_parser(handlers, ns_separator, false); 281 local parser = new_parser(handlers, ns_separator, false, max_elements);
258 local parse = parser.parse; 282 local parse = parser.parse;
259 283
260 function session.open_stream(session, from, to) -- luacheck: ignore 432/session 284 function session.open_stream(session, from, to) -- luacheck: ignore 432/session
261 local send = session.sends2s or session.send; 285 local send = session.sends2s or session.send;
262 286
296 _parser:close(); 320 _parser:close();
297 end 321 end
298 return ok, err; 322 return ok, err;
299 end, 323 end,
300 set_session = meta.set_session; 324 set_session = meta.set_session;
301 set_stanza_size_limit = function (_, new_stanza_size_limit) 325 set_stanza_size_limit = function (_, new_stanza_size_limit, new_max_elements)
302 stanza_size_limit = new_stanza_size_limit; 326 stanza_size_limit = new_stanza_size_limit;
327 meta.set_max_elements(new_max_elements);
303 end; 328 end;
304 }; 329 };
305 end 330 end
306 331
307 return { 332 return {