Changes: Add an isolated public transport integration with finite byte, depth, element and attribute budgets; validate complete text XML with pinned saxes before real JXT parsing/import. Add adversarial and boundary fixtures, terminal cleanup tests, dependency notices and a proposed parser ADR. Validation: Fresh-cache npm ci and npm test passed all 55 tests with Node 24.21.0/npm 11.19.0. Reproduced three stock parser failures, checked ten exact dependency licenses/integrities and 340 unchanged Stanza files; make check passed 72 tooling tests. Scope and whitespace inspected. Browser runtime, live WSS and interoperability were not tested. Task: S00-018 Agent: auto-protocol-89fe6563092c Role: protocol Action: claim Status: review Task title: Validate bounded standalone XML ingress for the XMPP candidate
69 lines
4.1 KiB
JavaScript
69 lines
4.1 KiB
JavaScript
export const opening = '<message xmlns="jabber:client"><body>';
|
|
export const closing = '</body></message>';
|
|
export const message = text => opening + text + closing;
|
|
export const valid = message('finite fixture');
|
|
export const deep = n => '<message xmlns="jabber:client">' + '<x>'.repeat(n - 1) +
|
|
'</x>'.repeat(n - 1) + '</message>';
|
|
export const elements = n => '<message xmlns="jabber:client">' + '<x/>'.repeat(n - 1) + '</message>';
|
|
export const attributes = n => '<message xmlns="jabber:client"' +
|
|
Array.from({length: n - 1}, (_, i) => ` a${i}="x"`).join('') + '/>';
|
|
export function boundary() {
|
|
// 16 attributes / 1024 name+value UTF-16 units including xmlns.
|
|
let attrs = ' xmlns="jabber:client"';
|
|
let chars = 'xmlns'.length + 'jabber:client'.length;
|
|
for (let i = 0; i < 15; i++) {
|
|
const name = `a${i}`;
|
|
const value = 'v'.repeat(i === 14 ? 1024 - chars - name.length : 60);
|
|
attrs += ` ${name}="${value}"`;
|
|
chars += name.length + value.length;
|
|
}
|
|
// Root + body + chain of 7 + 23 siblings = 32 elements; depth 8.
|
|
const before = `<message${attrs}><body>`;
|
|
const after = '</body>' + '<x>'.repeat(7) + '</x>'.repeat(7) + '<x/>'.repeat(23) + '</message>';
|
|
return {xml: before + 'z'.repeat(4096 - before.length - after.length) + after,
|
|
body: 'z'.repeat(4096 - before.length - after.length)};
|
|
}
|
|
export const invalid = [
|
|
['large stock body', message('b'.repeat(1048576)), 'bytes'],
|
|
['deep stock XML', deep(257), 'depth'],
|
|
['unfinished opening', opening, 'invalid-xml'],
|
|
['unfinished attribute', '<message xmlns="jabber:client" a="unfinished', 'invalid-xml'],
|
|
['unfinished tag', '<message xmlns="jabber:client"', 'invalid-xml'],
|
|
['unfinished entity', message('&'), 'invalid-xml'],
|
|
['empty', '', 'framing'],
|
|
['leading whitespace', ' ' + valid, 'framing'],
|
|
['text only', 'not xml', 'framing'],
|
|
['two roots', valid + valid, 'invalid-xml'],
|
|
['valid root then unfinished root', valid + '<message', 'invalid-xml'],
|
|
['valid root then junk', valid + 'junk', 'invalid-xml'],
|
|
['mismatched tags', '<message xmlns="jabber:client"><body></message>', 'invalid-xml'],
|
|
['depth over', deep(9), 'depth'],
|
|
['elements over', elements(33), 'elements'],
|
|
['attributes over', attributes(17), 'attributes'],
|
|
['attribute value over', `<message xmlns="jabber:client" a="${'x'.repeat(257)}"/>`, 'attribute-value'],
|
|
['attribute chars over', '<message xmlns="jabber:client" ' +
|
|
Array.from({length: 4}, (_, i) => `a${i}="${'x'.repeat(256)}"`).join(' ') + '/>', 'attribute-chars'],
|
|
['oversized name', `<message xmlns="jabber:client"><${'x'.repeat(129)}/></message>`, 'name'],
|
|
['oversized attribute name', `<message xmlns="jabber:client" ${'x'.repeat(129)}="x"/>`, 'name'],
|
|
['oversized unfinished token', '<' + 'x'.repeat(4096), 'bytes'],
|
|
['UTF-8 bytes over with bounded units', message('🦉'.repeat(1100)), 'bytes'],
|
|
['binary buffer', new Uint8Array([60, 120, 47, 62]), 'text-required'],
|
|
['binary ArrayBuffer', new ArrayBuffer(4), 'text-required'],
|
|
['binary Blob', new Blob([valid]), 'text-required'],
|
|
['boxed string', new String(valid), 'text-required'],
|
|
['comment', '<!--x-->' + valid, 'restricted-xml'],
|
|
['DTD internal entity', '<!DOCTYPE message [<!ENTITY x "payload">]>' + message('&x;'), 'restricted-xml'],
|
|
['external DTD', '<!DOCTYPE message SYSTEM "https://example.invalid/dtd">' + valid, 'restricted-xml'],
|
|
['processing instruction', '<?test x?>' + valid, 'restricted-xml'],
|
|
['XML declaration', '<?xml version="1.0"?>' + valid, 'restricted-xml'],
|
|
['duplicate attribute', '<message xmlns="jabber:client" id="a" id="b"/>', 'invalid-xml'],
|
|
['duplicate expanded attribute', '<message xmlns="jabber:client" xmlns:a="urn:x" xmlns:b="urn:x" a:id="a" b:id="b"/>', 'invalid-xml'],
|
|
['unbound namespace', '<p:message/>', 'invalid-xml'],
|
|
['illegal namespace binding', '<message xmlns="jabber:client" xmlns:xml="urn:wrong"/>', 'invalid-xml'],
|
|
['unknown entity', message('&unknown;'), 'invalid-xml'],
|
|
['illegal control', message('\u0000'), 'invalid-xml'],
|
|
['unpaired surrogate', message('\ud800'), 'invalid-xml'],
|
|
['invalid char reference', message('�'), 'invalid-xml'],
|
|
['unknown root', '<unknown xmlns="urn:fixture"/>', 'unknown-root']
|
|
];
|