Mercurial > prosody-hg
comparison util/xmppstream.lua @ 14131:6c7549964d4d 13.0
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 | 166ac7d65cb6 |
| children | e3ddbf7f2d3f |
comparison
equal
deleted
inserted
replaced
| 14130:a957cd3ea4eb | 14131:6c7549964d4d |
|---|---|
| 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 { |
