Files
safetynet/tests/broker-multipart.test.mjs

51 lines
4.5 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import {createRequire} from 'node:module';
const require=createRequire(import.meta.url),{parseMultipart}=require('../apps/network-broker/multipart.cjs');
const boundary='----WebKitFormBoundarySynthetic42',type=`multipart/form-data; boundary=${boundary}`;
function encode(parts,ending=true){
const output=[];
for(const part of parts){output.push(Buffer.from(`--${boundary}\r\n${part.headers}\r\n\r\n`));output.push(Buffer.from(part.body));output.push(Buffer.from('\r\n'));}
if(ending)output.push(Buffer.from(`--${boundary}--\r\n`));return Buffer.concat(output);
}
const text={headers:'Content-Disposition: form-data; name="note"',body:'public note'};
const pdf={headers:'Content-Disposition: form-data; name="files"; filename="report.pdf"\r\nContent-Type: application/pdf',body:Buffer.from('%PDF-1.4\n\0\xffbinary','latin1')};
const zip={headers:'Content-Disposition: form-data; name="files"; filename="archive.zip"\r\nContent-Type: application/zip',body:Buffer.from([0x50,0x4b,3,4,0,255,1])};
test('Chromium form fields and PDF ZIP parts are preserved completely as independent bytes',()=>{
const body=encode([text,pdf,zip]),parsed=parseMultipart(body,type);
assert.equal(parsed.coverage.complete,true);assert.equal(parsed.parts.length,3);
assert.equal(parsed.parts[0].body.toString(),'public note');
assert.deepEqual(parsed.parts[1].body,pdf.body);assert.deepEqual(parsed.parts[2].body,zip.body);
assert.equal(parsed.parts[1].filename,'report.pdf');assert.equal(parsed.parts[1].contentType,'application/pdf');
body.fill(0);assert.deepEqual(parsed.parts[1].body,pdf.body);
parsed.dispose();assert.ok(parsed.parts.every(part=>part.body.every(value=>value===0)));
});
test('boundary-like bytes without complete delimiter suffix remain inside file',()=>{
const body=Buffer.from(`prefix\r\n--${boundary}XYZ\r\n--${boundary}-\r\nsuffix`);
const parsed=parseMultipart(encode([{...pdf,body}]),type);assert.deepEqual(parsed.parts[0].body,body);parsed.dispose();
});
test('quoted boundary and escaped filename handle actual MIME syntax',()=>{
const parsed=parseMultipart(encode([{...text,headers:'Content-Disposition: form-data; name="upload"; filename="quoted\\"name.txt"'}]),`multipart/form-data; boundary="${boundary}"`);
assert.equal(parsed.parts[0].filename,'quoted"name.txt');parsed.dispose();
});
test('empty multipart form has complete zero-part coverage',()=>{
assert.equal(parseMultipart(encode([]),type).parts.length,0);
});
test('truncation malformed CRLF preamble and epilogue never yield partial coverage',()=>{
const valid=encode([text,pdf]);
for(const body of [valid.subarray(0,-5),encode([text],false),Buffer.concat([Buffer.from('preamble'),valid]),Buffer.concat([valid,Buffer.from('epilogue')]),Buffer.from(valid.toString().replace('\r\n','\n'))])assert.throws(()=>parseMultipart(body,type),{code:'invalid-multipart'});
});
test('ambiguous disposition and duplicate headers are rejected',()=>{
for(const headers of ['Content-Disposition: form-data; name="a"; name="b"','Content-Disposition: form-data; name="a"\r\ncontent-disposition: form-data; name="b"','Content-Disposition: form-data; name="a"; filename="one"; filename="two"','Content-Disposition: form-data; name="a"\r\n folded: value'])assert.throws(()=>parseMultipart(encode([{headers,body:'public'}]),type),{code:'invalid-multipart'});
});
test('unsupported extended filenames nested multipart and transfer encoding fail closed',()=>{
for(const headers of ["Content-Disposition: form-data; name=upload; filename*=UTF-8''file.pdf",'Content-Disposition: form-data; name="a"\r\nContent-Type: multipart/mixed; boundary=inner','Content-Disposition: form-data; name="a"\r\nContent-Transfer-Encoding: base64'])assert.throws(()=>parseMultipart(encode([{headers,body:'public'}]),type),{code:'unsupported-multipart'});
});
test('whole payload part count and per-part headers are strictly bounded',()=>{
const body=encode([text,pdf]);
for(const options of [{maxBytes:body.length-1},{maxParts:1},{maxHeaderBytes:5}])assert.throws(()=>parseMultipart(body,type,options),{code:'multipart-limit'});
});
test('invalid boundary parameters and duplicate boundary are rejected',()=>{
for(const value of ['multipart/form-data','multipart/form-data; boundary=""','multipart/form-data; boundary="bad\r\nvalue"',`multipart/form-data; boundary=${boundary}; boundary=other`,'multipart/form-data; boundary='+ 'x'.repeat(71)])assert.throws(()=>parseMultipart(encode([text]),value),{code:'invalid-multipart'});
});