Added a API endpoint for adding games (authentication required config.app.password)

This commit is contained in:
Paul Black 2023-11-12 23:07:50 +00:00
parent a25879abee
commit c650e4b9d8
101 changed files with 12977 additions and 190 deletions

View file

@ -1,3 +1,5 @@
app:
password: PASSWORD
database: database:
host: your-database-host host: your-database-host
user: your-username user: your-username

1
node_modules/.bin/js-yaml generated vendored
View file

@ -1 +0,0 @@
../js-yaml/bin/js-yaml.js

126
node_modules/.bin/js-yaml generated vendored Executable file
View file

@ -0,0 +1,126 @@
#!/usr/bin/env node
'use strict';
/*eslint-disable no-console*/
var fs = require('fs');
var argparse = require('argparse');
var yaml = require('..');
////////////////////////////////////////////////////////////////////////////////
var cli = new argparse.ArgumentParser({
prog: 'js-yaml',
add_help: true
});
cli.add_argument('-v', '--version', {
action: 'version',
version: require('../package.json').version
});
cli.add_argument('-c', '--compact', {
help: 'Display errors in compact mode',
action: 'store_true'
});
// deprecated (not needed after we removed output colors)
// option suppressed, but not completely removed for compatibility
cli.add_argument('-j', '--to-json', {
help: argparse.SUPPRESS,
dest: 'json',
action: 'store_true'
});
cli.add_argument('-t', '--trace', {
help: 'Show stack trace on error',
action: 'store_true'
});
cli.add_argument('file', {
help: 'File to read, utf-8 encoded without BOM',
nargs: '?',
default: '-'
});
////////////////////////////////////////////////////////////////////////////////
var options = cli.parse_args();
////////////////////////////////////////////////////////////////////////////////
function readFile(filename, encoding, callback) {
if (options.file === '-') {
// read from stdin
var chunks = [];
process.stdin.on('data', function (chunk) {
chunks.push(chunk);
});
process.stdin.on('end', function () {
return callback(null, Buffer.concat(chunks).toString(encoding));
});
} else {
fs.readFile(filename, encoding, callback);
}
}
readFile(options.file, 'utf8', function (error, input) {
var output, isYaml;
if (error) {
if (error.code === 'ENOENT') {
console.error('File not found: ' + options.file);
process.exit(2);
}
console.error(
options.trace && error.stack ||
error.message ||
String(error));
process.exit(1);
}
try {
output = JSON.parse(input);
isYaml = false;
} catch (err) {
if (err instanceof SyntaxError) {
try {
output = [];
yaml.loadAll(input, function (doc) { output.push(doc); }, {});
isYaml = true;
if (output.length === 0) output = null;
else if (output.length === 1) output = output[0];
} catch (e) {
if (options.trace && err.stack) console.error(e.stack);
else console.error(e.toString(options.compact));
process.exit(1);
}
} else {
console.error(
options.trace && err.stack ||
err.message ||
String(err));
process.exit(1);
}
}
if (isYaml) console.log(JSON.stringify(output, null, ' '));
else console.log(yaml.dump(output));
});

1
node_modules/.bin/mime generated vendored
View file

@ -1 +0,0 @@
../mime/cli.js

8
node_modules/.bin/mime generated vendored Executable file
View file

@ -0,0 +1,8 @@
#!/usr/bin/env node
var mime = require('./mime.js');
var file = process.argv[2];
var type = mime.lookup(file);
process.stdout.write(type + '\n');

301
node_modules/.package-lock.json generated vendored
View file

@ -6,8 +6,7 @@
"packages": { "packages": {
"node_modules/accepts": { "node_modules/accepts": {
"version": "1.3.8", "version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "license": "MIT",
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
"dependencies": { "dependencies": {
"mime-types": "~2.1.34", "mime-types": "~2.1.34",
"negotiator": "0.6.3" "negotiator": "0.6.3"
@ -18,29 +17,26 @@
}, },
"node_modules/argparse": { "node_modules/argparse": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "license": "Python-2.0"
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
}, },
"node_modules/array-flatten": { "node_modules/array-flatten": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "license": "MIT"
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
}, },
"node_modules/bignumber.js": { "node_modules/bignumber.js": {
"version": "9.0.0", "version": "9.0.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", "license": "MIT",
"integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==",
"engines": { "engines": {
"node": "*" "node": "*"
} }
}, },
"node_modules/body-parser": { "node_modules/body-parser": {
"version": "1.20.1", "version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"dependencies": { "dependencies": {
"bytes": "3.1.2", "bytes": "3.1.2",
"content-type": "~1.0.4", "content-type": "~1.0.5",
"debug": "2.6.9", "debug": "2.6.9",
"depd": "2.0.0", "depd": "2.0.0",
"destroy": "1.2.0", "destroy": "1.2.0",
@ -48,7 +44,7 @@
"iconv-lite": "0.4.24", "iconv-lite": "0.4.24",
"on-finished": "2.4.1", "on-finished": "2.4.1",
"qs": "6.11.0", "qs": "6.11.0",
"raw-body": "2.5.1", "raw-body": "2.5.2",
"type-is": "~1.6.18", "type-is": "~1.6.18",
"unpipe": "1.0.0" "unpipe": "1.0.0"
}, },
@ -57,6 +53,17 @@
"npm": "1.2.8000 || >= 1.4.16" "npm": "1.2.8000 || >= 1.4.16"
} }
}, },
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
"dependencies": {
"streamsearch": "^1.1.0"
},
"engines": {
"node": ">=10.16.0"
}
},
"node_modules/bytes": { "node_modules/bytes": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@ -67,8 +74,7 @@
}, },
"node_modules/call-bind": { "node_modules/call-bind": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", "license": "MIT",
"integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
"dependencies": { "dependencies": {
"function-bind": "^1.1.2", "function-bind": "^1.1.2",
"get-intrinsic": "^1.2.1", "get-intrinsic": "^1.2.1",
@ -80,8 +86,7 @@
}, },
"node_modules/content-disposition": { "node_modules/content-disposition": {
"version": "0.5.4", "version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "license": "MIT",
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
"dependencies": { "dependencies": {
"safe-buffer": "5.2.1" "safe-buffer": "5.2.1"
}, },
@ -91,42 +96,36 @@
}, },
"node_modules/content-type": { "node_modules/content-type": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "license": "MIT",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/cookie": { "node_modules/cookie": {
"version": "0.5.0", "version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", "license": "MIT",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/cookie-signature": { "node_modules/cookie-signature": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "license": "MIT"
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
}, },
"node_modules/core-util-is": { "node_modules/core-util-is": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "license": "MIT"
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
}, },
"node_modules/debug": { "node_modules/debug": {
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "license": "MIT",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": { "dependencies": {
"ms": "2.0.0" "ms": "2.0.0"
} }
}, },
"node_modules/define-data-property": { "node_modules/define-data-property": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", "license": "MIT",
"integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
"dependencies": { "dependencies": {
"get-intrinsic": "^1.2.1", "get-intrinsic": "^1.2.1",
"gopd": "^1.0.1", "gopd": "^1.0.1",
@ -138,16 +137,14 @@
}, },
"node_modules/depd": { "node_modules/depd": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "license": "MIT",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"engines": { "engines": {
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/destroy": { "node_modules/destroy": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "license": "MIT",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
"engines": { "engines": {
"node": ">= 0.8", "node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16" "npm": "1.2.8000 || >= 1.4.16"
@ -155,34 +152,29 @@
}, },
"node_modules/ee-first": { "node_modules/ee-first": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "license": "MIT"
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
}, },
"node_modules/encodeurl": { "node_modules/encodeurl": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "license": "MIT",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"engines": { "engines": {
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/escape-html": { "node_modules/escape-html": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "license": "MIT"
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
}, },
"node_modules/etag": { "node_modules/etag": {
"version": "1.8.1", "version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "license": "MIT",
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/express": { "node_modules/express": {
"version": "4.18.2", "version": "4.18.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", "license": "MIT",
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
"dependencies": { "dependencies": {
"accepts": "~1.3.8", "accepts": "~1.3.8",
"array-flatten": "1.1.1", "array-flatten": "1.1.1",
@ -220,10 +212,57 @@
"node": ">= 0.10.0" "node": ">= 0.10.0"
} }
}, },
"node_modules/express-fileupload": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.4.2.tgz",
"integrity": "sha512-vk+9cK595jP03T+YgoYPAebynVCZuUBtW1JkyJnitQnWzlONHdxdAIm9yo99V4viTEftq7MUfzuqmWyqWGzMIg==",
"dependencies": {
"busboy": "^1.6.0"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/express/node_modules/body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/express/node_modules/raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/finalhandler": { "node_modules/finalhandler": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", "license": "MIT",
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
"dependencies": { "dependencies": {
"debug": "2.6.9", "debug": "2.6.9",
"encodeurl": "~1.0.2", "encodeurl": "~1.0.2",
@ -239,37 +278,32 @@
}, },
"node_modules/forwarded": { "node_modules/forwarded": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "license": "MIT",
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/fresh": { "node_modules/fresh": {
"version": "0.5.2", "version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "license": "MIT",
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/fs": { "node_modules/fs": {
"version": "0.0.1-security", "version": "0.0.1-security",
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", "license": "ISC"
"integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w=="
}, },
"node_modules/function-bind": { "node_modules/function-bind": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "license": "MIT",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/get-intrinsic": { "node_modules/get-intrinsic": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", "license": "MIT",
"integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
"dependencies": { "dependencies": {
"function-bind": "^1.1.2", "function-bind": "^1.1.2",
"has-proto": "^1.0.1", "has-proto": "^1.0.1",
@ -282,8 +316,7 @@
}, },
"node_modules/gopd": { "node_modules/gopd": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "license": "MIT",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"dependencies": { "dependencies": {
"get-intrinsic": "^1.1.3" "get-intrinsic": "^1.1.3"
}, },
@ -293,8 +326,7 @@
}, },
"node_modules/has-property-descriptors": { "node_modules/has-property-descriptors": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", "license": "MIT",
"integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
"dependencies": { "dependencies": {
"get-intrinsic": "^1.2.2" "get-intrinsic": "^1.2.2"
}, },
@ -304,8 +336,7 @@
}, },
"node_modules/has-proto": { "node_modules/has-proto": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", "license": "MIT",
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
}, },
@ -315,8 +346,7 @@
}, },
"node_modules/has-symbols": { "node_modules/has-symbols": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "license": "MIT",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
}, },
@ -326,8 +356,7 @@
}, },
"node_modules/hasown": { "node_modules/hasown": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", "license": "MIT",
"integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
"dependencies": { "dependencies": {
"function-bind": "^1.1.2" "function-bind": "^1.1.2"
}, },
@ -337,8 +366,7 @@
}, },
"node_modules/http-errors": { "node_modules/http-errors": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "license": "MIT",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"dependencies": { "dependencies": {
"depd": "2.0.0", "depd": "2.0.0",
"inherits": "2.0.4", "inherits": "2.0.4",
@ -363,26 +391,22 @@
}, },
"node_modules/inherits": { "node_modules/inherits": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "license": "ISC"
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
}, },
"node_modules/ipaddr.js": { "node_modules/ipaddr.js": {
"version": "1.9.1", "version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "license": "MIT",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"engines": { "engines": {
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/isarray": { "node_modules/isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "license": "MIT"
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
}, },
"node_modules/js-yaml": { "node_modules/js-yaml": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "license": "MIT",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dependencies": { "dependencies": {
"argparse": "^2.0.1" "argparse": "^2.0.1"
}, },
@ -392,29 +416,25 @@
}, },
"node_modules/media-typer": { "node_modules/media-typer": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "license": "MIT",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/merge-descriptors": { "node_modules/merge-descriptors": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "license": "MIT"
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
}, },
"node_modules/methods": { "node_modules/methods": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "license": "MIT",
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/mime": { "node_modules/mime": {
"version": "1.6.0", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "license": "MIT",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"bin": { "bin": {
"mime": "cli.js" "mime": "cli.js"
}, },
@ -424,16 +444,14 @@
}, },
"node_modules/mime-db": { "node_modules/mime-db": {
"version": "1.52.0", "version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "license": "MIT",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/mime-types": { "node_modules/mime-types": {
"version": "2.1.35", "version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "license": "MIT",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": { "dependencies": {
"mime-db": "1.52.0" "mime-db": "1.52.0"
}, },
@ -443,13 +461,11 @@
}, },
"node_modules/ms": { "node_modules/ms": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "license": "MIT"
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
}, },
"node_modules/mysql": { "node_modules/mysql": {
"version": "2.18.1", "version": "2.18.1",
"resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", "license": "MIT",
"integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==",
"dependencies": { "dependencies": {
"bignumber.js": "9.0.0", "bignumber.js": "9.0.0",
"readable-stream": "2.3.7", "readable-stream": "2.3.7",
@ -462,29 +478,25 @@
}, },
"node_modules/mysql/node_modules/safe-buffer": { "node_modules/mysql/node_modules/safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "license": "MIT"
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}, },
"node_modules/negotiator": { "node_modules/negotiator": {
"version": "0.6.3", "version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "license": "MIT",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/object-inspect": { "node_modules/object-inspect": {
"version": "1.13.1", "version": "1.13.1",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", "license": "MIT",
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/on-finished": { "node_modules/on-finished": {
"version": "2.4.1", "version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "license": "MIT",
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"dependencies": { "dependencies": {
"ee-first": "1.1.1" "ee-first": "1.1.1"
}, },
@ -494,26 +506,22 @@
}, },
"node_modules/parseurl": { "node_modules/parseurl": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "license": "MIT",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"engines": { "engines": {
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/path-to-regexp": { "node_modules/path-to-regexp": {
"version": "0.1.7", "version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "license": "MIT"
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
}, },
"node_modules/process-nextick-args": { "node_modules/process-nextick-args": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "license": "MIT"
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
}, },
"node_modules/proxy-addr": { "node_modules/proxy-addr": {
"version": "2.0.7", "version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "license": "MIT",
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
"dependencies": { "dependencies": {
"forwarded": "0.2.0", "forwarded": "0.2.0",
"ipaddr.js": "1.9.1" "ipaddr.js": "1.9.1"
@ -524,8 +532,7 @@
}, },
"node_modules/qs": { "node_modules/qs": {
"version": "6.11.0", "version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", "license": "BSD-3-Clause",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"dependencies": { "dependencies": {
"side-channel": "^1.0.4" "side-channel": "^1.0.4"
}, },
@ -538,16 +545,15 @@
}, },
"node_modules/range-parser": { "node_modules/range-parser": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "license": "MIT",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/raw-body": { "node_modules/raw-body": {
"version": "2.5.1", "version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"dependencies": { "dependencies": {
"bytes": "3.1.2", "bytes": "3.1.2",
"http-errors": "2.0.0", "http-errors": "2.0.0",
@ -560,8 +566,7 @@
}, },
"node_modules/readable-stream": { "node_modules/readable-stream": {
"version": "2.3.7", "version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "license": "MIT",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"dependencies": { "dependencies": {
"core-util-is": "~1.0.0", "core-util-is": "~1.0.0",
"inherits": "~2.0.3", "inherits": "~2.0.3",
@ -574,13 +579,10 @@
}, },
"node_modules/readable-stream/node_modules/safe-buffer": { "node_modules/readable-stream/node_modules/safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "license": "MIT"
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}, },
"node_modules/safe-buffer": { "node_modules/safe-buffer": {
"version": "5.2.1", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@ -594,7 +596,8 @@
"type": "consulting", "type": "consulting",
"url": "https://feross.org/support" "url": "https://feross.org/support"
} }
] ],
"license": "MIT"
}, },
"node_modules/safer-buffer": { "node_modules/safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
@ -603,8 +606,7 @@
}, },
"node_modules/send": { "node_modules/send": {
"version": "0.18.0", "version": "0.18.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", "license": "MIT",
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
"dependencies": { "dependencies": {
"debug": "2.6.9", "debug": "2.6.9",
"depd": "2.0.0", "depd": "2.0.0",
@ -626,13 +628,11 @@
}, },
"node_modules/send/node_modules/ms": { "node_modules/send/node_modules/ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "license": "MIT"
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
}, },
"node_modules/serve-static": { "node_modules/serve-static": {
"version": "1.15.0", "version": "1.15.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", "license": "MIT",
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
"dependencies": { "dependencies": {
"encodeurl": "~1.0.2", "encodeurl": "~1.0.2",
"escape-html": "~1.0.3", "escape-html": "~1.0.3",
@ -645,8 +645,7 @@
}, },
"node_modules/set-function-length": { "node_modules/set-function-length": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", "license": "MIT",
"integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
"dependencies": { "dependencies": {
"define-data-property": "^1.1.1", "define-data-property": "^1.1.1",
"get-intrinsic": "^1.2.1", "get-intrinsic": "^1.2.1",
@ -659,13 +658,11 @@
}, },
"node_modules/setprototypeof": { "node_modules/setprototypeof": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "license": "ISC"
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
}, },
"node_modules/side-channel": { "node_modules/side-channel": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "license": "MIT",
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
"dependencies": { "dependencies": {
"call-bind": "^1.0.0", "call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2", "get-intrinsic": "^1.0.2",
@ -677,45 +674,47 @@
}, },
"node_modules/sqlstring": { "node_modules/sqlstring": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", "license": "MIT",
"integrity": "sha512-ooAzh/7dxIG5+uDik1z/Rd1vli0+38izZhGzSa34FwR7IbelPWCCKSNIl8jlL/F7ERvy8CB2jNeM1E9i9mXMAQ==",
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/statuses": { "node_modules/statuses": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "license": "MIT",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"engines": { "engines": {
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/string_decoder": { "node_modules/string_decoder": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "license": "MIT",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dependencies": { "dependencies": {
"safe-buffer": "~5.1.0" "safe-buffer": "~5.1.0"
} }
}, },
"node_modules/string_decoder/node_modules/safe-buffer": { "node_modules/string_decoder/node_modules/safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "license": "MIT"
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}, },
"node_modules/toidentifier": { "node_modules/toidentifier": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "license": "MIT",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"engines": { "engines": {
"node": ">=0.6" "node": ">=0.6"
} }
}, },
"node_modules/type-is": { "node_modules/type-is": {
"version": "1.6.18", "version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "license": "MIT",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dependencies": { "dependencies": {
"media-typer": "0.3.0", "media-typer": "0.3.0",
"mime-types": "~2.1.24" "mime-types": "~2.1.24"
@ -726,29 +725,25 @@
}, },
"node_modules/unpipe": { "node_modules/unpipe": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "license": "MIT",
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
"engines": { "engines": {
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/util-deprecate": { "node_modules/util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "license": "MIT"
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
}, },
"node_modules/utils-merge": { "node_modules/utils-merge": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "license": "MIT",
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"engines": { "engines": {
"node": ">= 0.4.0" "node": ">= 0.4.0"
} }
}, },
"node_modules/vary": { "node_modules/vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "license": "MIT",
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
"engines": { "engines": {
"node": ">= 0.8" "node": ">= 0.8"
} }

View file

@ -1,3 +1,11 @@
1.20.2 / 2023-02-21
===================
* Fix strict json error message on Node.js 19+
* deps: content-type@~1.0.5
- perf: skip value escaping when unnecessary
* deps: raw-body@2.5.2
1.20.1 / 2022-10-06 1.20.1 / 2022-10-06
=================== ===================

21
node_modules/body-parser/README.md generated vendored
View file

@ -1,8 +1,8 @@
# body-parser # body-parser
[![NPM Version][npm-image]][npm-url] [![NPM Version][npm-version-image]][npm-url]
[![NPM Downloads][downloads-image]][downloads-url] [![NPM Downloads][npm-downloads-image]][npm-url]
[![Build Status][github-actions-ci-image]][github-actions-ci-url] [![Build Status][ci-image]][ci-url]
[![Test Coverage][coveralls-image]][coveralls-url] [![Test Coverage][coveralls-image]][coveralls-url]
Node.js body parsing middleware. Node.js body parsing middleware.
@ -454,11 +454,12 @@ app.use(bodyParser.text({ type: 'text/html' }))
[MIT](LICENSE) [MIT](LICENSE)
[npm-image]: https://img.shields.io/npm/v/body-parser.svg [ci-image]: https://badgen.net/github/checks/expressjs/body-parser/master?label=ci
[npm-url]: https://npmjs.org/package/body-parser [ci-url]: https://github.com/expressjs/body-parser/actions/workflows/ci.yml
[coveralls-image]: https://img.shields.io/coveralls/expressjs/body-parser/master.svg [coveralls-image]: https://badgen.net/coveralls/c/github/expressjs/body-parser/master
[coveralls-url]: https://coveralls.io/r/expressjs/body-parser?branch=master [coveralls-url]: https://coveralls.io/r/expressjs/body-parser?branch=master
[downloads-image]: https://img.shields.io/npm/dm/body-parser.svg [node-version-image]: https://badgen.net/npm/node/body-parser
[downloads-url]: https://npmjs.org/package/body-parser [node-version-url]: https://nodejs.org/en/download
[github-actions-ci-image]: https://img.shields.io/github/workflow/status/expressjs/body-parser/ci/master?label=ci [npm-downloads-image]: https://badgen.net/npm/dm/body-parser
[github-actions-ci-url]: https://github.com/expressjs/body-parser/actions/workflows/ci.yml [npm-url]: https://npmjs.org/package/body-parser
[npm-version-image]: https://badgen.net/npm/v/body-parser

View file

@ -39,6 +39,9 @@ module.exports = json
var FIRST_CHAR_REGEXP = /^[\x20\x09\x0a\x0d]*([^\x20\x09\x0a\x0d])/ // eslint-disable-line no-control-regex var FIRST_CHAR_REGEXP = /^[\x20\x09\x0a\x0d]*([^\x20\x09\x0a\x0d])/ // eslint-disable-line no-control-regex
var JSON_SYNTAX_CHAR = '#'
var JSON_SYNTAX_REGEXP = /#+/g
/** /**
* Create a middleware to parse JSON bodies. * Create a middleware to parse JSON bodies.
* *
@ -152,15 +155,23 @@ function json (options) {
function createStrictSyntaxError (str, char) { function createStrictSyntaxError (str, char) {
var index = str.indexOf(char) var index = str.indexOf(char)
var partial = index !== -1 var partial = ''
? str.substring(0, index) + '#'
: '' if (index !== -1) {
partial = str.substring(0, index) + JSON_SYNTAX_CHAR
for (var i = index + 1; i < str.length; i++) {
partial += JSON_SYNTAX_CHAR
}
}
try { try {
JSON.parse(partial); /* istanbul ignore next */ throw new SyntaxError('strict violation') JSON.parse(partial); /* istanbul ignore next */ throw new SyntaxError('strict violation')
} catch (e) { } catch (e) {
return normalizeJsonSyntaxError(e, { return normalizeJsonSyntaxError(e, {
message: e.message.replace('#', char), message: e.message.replace(JSON_SYNTAX_REGEXP, function (placeholder) {
return str.substring(index, index + placeholder.length)
}),
stack: e.stack stack: e.stack
}) })
} }

View file

@ -1,7 +1,7 @@
{ {
"name": "body-parser", "name": "body-parser",
"description": "Node.js body parsing middleware", "description": "Node.js body parsing middleware",
"version": "1.20.1", "version": "1.20.2",
"contributors": [ "contributors": [
"Douglas Christopher Wilson <doug@somethingdoug.com>", "Douglas Christopher Wilson <doug@somethingdoug.com>",
"Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)" "Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)"
@ -10,7 +10,7 @@
"repository": "expressjs/body-parser", "repository": "expressjs/body-parser",
"dependencies": { "dependencies": {
"bytes": "3.1.2", "bytes": "3.1.2",
"content-type": "~1.0.4", "content-type": "~1.0.5",
"debug": "2.6.9", "debug": "2.6.9",
"depd": "2.0.0", "depd": "2.0.0",
"destroy": "1.2.0", "destroy": "1.2.0",
@ -18,23 +18,23 @@
"iconv-lite": "0.4.24", "iconv-lite": "0.4.24",
"on-finished": "2.4.1", "on-finished": "2.4.1",
"qs": "6.11.0", "qs": "6.11.0",
"raw-body": "2.5.1", "raw-body": "2.5.2",
"type-is": "~1.6.18", "type-is": "~1.6.18",
"unpipe": "1.0.0" "unpipe": "1.0.0"
}, },
"devDependencies": { "devDependencies": {
"eslint": "8.24.0", "eslint": "8.34.0",
"eslint-config-standard": "14.1.1", "eslint-config-standard": "14.1.1",
"eslint-plugin-import": "2.26.0", "eslint-plugin-import": "2.27.5",
"eslint-plugin-markdown": "3.0.0", "eslint-plugin-markdown": "3.0.0",
"eslint-plugin-node": "11.1.0", "eslint-plugin-node": "11.1.0",
"eslint-plugin-promise": "6.0.1", "eslint-plugin-promise": "6.1.1",
"eslint-plugin-standard": "4.1.0", "eslint-plugin-standard": "4.1.0",
"methods": "1.1.2", "methods": "1.1.2",
"mocha": "10.0.0", "mocha": "10.2.0",
"nyc": "15.1.0", "nyc": "15.1.0",
"safe-buffer": "5.2.1", "safe-buffer": "5.2.1",
"supertest": "6.3.0" "supertest": "6.3.3"
}, },
"files": [ "files": [
"lib/", "lib/",

5
node_modules/busboy/.eslintrc.js generated vendored Normal file
View file

@ -0,0 +1,5 @@
'use strict';
module.exports = {
extends: '@mscdex/eslint-config',
};

24
node_modules/busboy/.github/workflows/ci.yml generated vendored Normal file
View file

@ -0,0 +1,24 @@
name: CI
on:
pull_request:
push:
branches: [ master ]
jobs:
tests-linux:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [10.16.0, 10.x, 12.x, 14.x, 16.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Install module
run: npm install
- name: Run tests
run: npm test

23
node_modules/busboy/.github/workflows/lint.yml generated vendored Normal file
View file

@ -0,0 +1,23 @@
name: lint
on:
pull_request:
push:
branches: [ master ]
env:
NODE_VERSION: 16.x
jobs:
lint-js:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@v1
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install ESLint + ESLint configs/plugins
run: npm install --only=dev
- name: Lint files
run: npm run lint

19
node_modules/busboy/LICENSE generated vendored Normal file
View file

@ -0,0 +1,19 @@
Copyright Brian White. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

191
node_modules/busboy/README.md generated vendored Normal file
View file

@ -0,0 +1,191 @@
# Description
A node.js module for parsing incoming HTML form data.
Changes (breaking or otherwise) in v1.0.0 can be found [here](https://github.com/mscdex/busboy/issues/266).
# Requirements
* [node.js](http://nodejs.org/) -- v10.16.0 or newer
# Install
npm install busboy
# Examples
* Parsing (multipart) with default options:
```js
const http = require('http');
const busboy = require('busboy');
http.createServer((req, res) => {
if (req.method === 'POST') {
console.log('POST request');
const bb = busboy({ headers: req.headers });
bb.on('file', (name, file, info) => {
const { filename, encoding, mimeType } = info;
console.log(
`File [${name}]: filename: %j, encoding: %j, mimeType: %j`,
filename,
encoding,
mimeType
);
file.on('data', (data) => {
console.log(`File [${name}] got ${data.length} bytes`);
}).on('close', () => {
console.log(`File [${name}] done`);
});
});
bb.on('field', (name, val, info) => {
console.log(`Field [${name}]: value: %j`, val);
});
bb.on('close', () => {
console.log('Done parsing form!');
res.writeHead(303, { Connection: 'close', Location: '/' });
res.end();
});
req.pipe(bb);
} else if (req.method === 'GET') {
res.writeHead(200, { Connection: 'close' });
res.end(`
<html>
<head></head>
<body>
<form method="POST" enctype="multipart/form-data">
<input type="file" name="filefield"><br />
<input type="text" name="textfield"><br />
<input type="submit">
</form>
</body>
</html>
`);
}
}).listen(8000, () => {
console.log('Listening for requests');
});
// Example output:
//
// Listening for requests
// < ... form submitted ... >
// POST request
// File [filefield]: filename: "logo.jpg", encoding: "binary", mime: "image/jpeg"
// File [filefield] got 11912 bytes
// Field [textfield]: value: "testing! :-)"
// File [filefield] done
// Done parsing form!
```
* Save all incoming files to disk:
```js
const { randomFillSync } = require('crypto');
const fs = require('fs');
const http = require('http');
const os = require('os');
const path = require('path');
const busboy = require('busboy');
const random = (() => {
const buf = Buffer.alloc(16);
return () => randomFillSync(buf).toString('hex');
})();
http.createServer((req, res) => {
if (req.method === 'POST') {
const bb = busboy({ headers: req.headers });
bb.on('file', (name, file, info) => {
const saveTo = path.join(os.tmpdir(), `busboy-upload-${random()}`);
file.pipe(fs.createWriteStream(saveTo));
});
bb.on('close', () => {
res.writeHead(200, { 'Connection': 'close' });
res.end(`That's all folks!`);
});
req.pipe(bb);
return;
}
res.writeHead(404);
res.end();
}).listen(8000, () => {
console.log('Listening for requests');
});
```
# API
## Exports
`busboy` exports a single function:
**( _function_ )**(< _object_ >config) - Creates and returns a new _Writable_ form parser stream.
* Valid `config` properties:
* **headers** - _object_ - These are the HTTP headers of the incoming request, which are used by individual parsers.
* **highWaterMark** - _integer_ - highWaterMark to use for the parser stream. **Default:** node's _stream.Writable_ default.
* **fileHwm** - _integer_ - highWaterMark to use for individual file streams. **Default:** node's _stream.Readable_ default.
* **defCharset** - _string_ - Default character set to use when one isn't defined. **Default:** `'utf8'`.
* **defParamCharset** - _string_ - For multipart forms, the default character set to use for values of part header parameters (e.g. filename) that are not extended parameters (that contain an explicit charset). **Default:** `'latin1'`.
* **preservePath** - _boolean_ - If paths in filenames from file parts in a `'multipart/form-data'` request shall be preserved. **Default:** `false`.
* **limits** - _object_ - Various limits on incoming data. Valid properties are:
* **fieldNameSize** - _integer_ - Max field name size (in bytes). **Default:** `100`.
* **fieldSize** - _integer_ - Max field value size (in bytes). **Default:** `1048576` (1MB).
* **fields** - _integer_ - Max number of non-file fields. **Default:** `Infinity`.
* **fileSize** - _integer_ - For multipart forms, the max file size (in bytes). **Default:** `Infinity`.
* **files** - _integer_ - For multipart forms, the max number of file fields. **Default:** `Infinity`.
* **parts** - _integer_ - For multipart forms, the max number of parts (fields + files). **Default:** `Infinity`.
* **headerPairs** - _integer_ - For multipart forms, the max number of header key-value pairs to parse. **Default:** `2000` (same as node's http module).
This function can throw exceptions if there is something wrong with the values in `config`. For example, if the Content-Type in `headers` is missing entirely, is not a supported type, or is missing the boundary for `'multipart/form-data'` requests.
## (Special) Parser stream events
* **file**(< _string_ >name, < _Readable_ >stream, < _object_ >info) - Emitted for each new file found. `name` contains the form field name. `stream` is a _Readable_ stream containing the file's data. No transformations/conversions (e.g. base64 to raw binary) are done on the file's data. `info` contains the following properties:
* `filename` - _string_ - If supplied, this contains the file's filename. **WARNING:** You should almost _never_ use this value as-is (especially if you are using `preservePath: true` in your `config`) as it could contain malicious input. You are better off generating your own (safe) filenames, or at the very least using a hash of the filename.
* `encoding` - _string_ - The file's `'Content-Transfer-Encoding'` value.
* `mimeType` - _string_ - The file's `'Content-Type'` value.
**Note:** If you listen for this event, you should always consume the `stream` whether you care about its contents or not (you can simply do `stream.resume();` if you want to discard/skip the contents), otherwise the `'finish'`/`'close'` event will never fire on the busboy parser stream.
However, if you aren't accepting files, you can either simply not listen for the `'file'` event at all or set `limits.files` to `0`, and any/all files will be automatically skipped (these skipped files will still count towards any configured `limits.files` and `limits.parts` limits though).
**Note:** If a configured `limits.fileSize` limit was reached for a file, `stream` will both have a boolean property `truncated` set to `true` (best checked at the end of the stream) and emit a `'limit'` event to notify you when this happens.
* **field**(< _string_ >name, < _string_ >value, < _object_ >info) - Emitted for each new non-file field found. `name` contains the form field name. `value` contains the string value of the field. `info` contains the following properties:
* `nameTruncated` - _boolean_ - Whether `name` was truncated or not (due to a configured `limits.fieldNameSize` limit)
* `valueTruncated` - _boolean_ - Whether `value` was truncated or not (due to a configured `limits.fieldSize` limit)
* `encoding` - _string_ - The field's `'Content-Transfer-Encoding'` value.
* `mimeType` - _string_ - The field's `'Content-Type'` value.
* **partsLimit**() - Emitted when the configured `limits.parts` limit has been reached. No more `'file'` or `'field'` events will be emitted.
* **filesLimit**() - Emitted when the configured `limits.files` limit has been reached. No more `'file'` events will be emitted.
* **fieldsLimit**() - Emitted when the configured `limits.fields` limit has been reached. No more `'field'` events will be emitted.

View file

@ -0,0 +1,149 @@
'use strict';
function createMultipartBuffers(boundary, sizes) {
const bufs = [];
for (let i = 0; i < sizes.length; ++i) {
const mb = sizes[i] * 1024 * 1024;
bufs.push(Buffer.from([
`--${boundary}`,
`content-disposition: form-data; name="field${i + 1}"`,
'',
'0'.repeat(mb),
'',
].join('\r\n')));
}
bufs.push(Buffer.from([
`--${boundary}--`,
'',
].join('\r\n')));
return bufs;
}
const boundary = '-----------------------------168072824752491622650073';
const buffers = createMultipartBuffers(boundary, [
10,
10,
10,
20,
50,
]);
const calls = {
partBegin: 0,
headerField: 0,
headerValue: 0,
headerEnd: 0,
headersEnd: 0,
partData: 0,
partEnd: 0,
end: 0,
};
const moduleName = process.argv[2];
switch (moduleName) {
case 'busboy': {
const busboy = require('busboy');
const parser = busboy({
limits: {
fieldSizeLimit: Infinity,
},
headers: {
'content-type': `multipart/form-data; boundary=${boundary}`,
},
});
parser.on('field', (name, val, info) => {
++calls.partBegin;
++calls.partData;
++calls.partEnd;
}).on('close', () => {
++calls.end;
console.timeEnd(moduleName);
});
console.time(moduleName);
for (const buf of buffers)
parser.write(buf);
break;
}
case 'formidable': {
const { MultipartParser } = require('formidable');
const parser = new MultipartParser();
parser.initWithBoundary(boundary);
parser.on('data', ({ name }) => {
++calls[name];
if (name === 'end')
console.timeEnd(moduleName);
});
console.time(moduleName);
for (const buf of buffers)
parser.write(buf);
break;
}
case 'multiparty': {
const { Readable } = require('stream');
const { Form } = require('multiparty');
const form = new Form({
maxFieldsSize: Infinity,
maxFields: Infinity,
maxFilesSize: Infinity,
autoFields: false,
autoFiles: false,
});
const req = new Readable({ read: () => {} });
req.headers = {
'content-type': `multipart/form-data; boundary=${boundary}`,
};
function hijack(name, fn) {
const oldFn = form[name];
form[name] = function() {
fn();
return oldFn.apply(this, arguments);
};
}
hijack('onParseHeaderField', () => {
++calls.headerField;
});
hijack('onParseHeaderValue', () => {
++calls.headerValue;
});
hijack('onParsePartBegin', () => {
++calls.partBegin;
});
hijack('onParsePartData', () => {
++calls.partData;
});
hijack('onParsePartEnd', () => {
++calls.partEnd;
});
form.on('close', () => {
++calls.end;
console.timeEnd(moduleName);
}).on('part', (p) => p.resume());
console.time(moduleName);
form.parse(req);
for (const buf of buffers)
req.push(buf);
req.push(null);
break;
}
default:
if (moduleName === undefined)
console.error('Missing parser module name');
else
console.error(`Invalid parser module name: ${moduleName}`);
process.exit(1);
}

View file

@ -0,0 +1,143 @@
'use strict';
function createMultipartBuffers(boundary, sizes) {
const bufs = [];
for (let i = 0; i < sizes.length; ++i) {
const mb = sizes[i] * 1024 * 1024;
bufs.push(Buffer.from([
`--${boundary}`,
`content-disposition: form-data; name="field${i + 1}"`,
'',
'0'.repeat(mb),
'',
].join('\r\n')));
}
bufs.push(Buffer.from([
`--${boundary}--`,
'',
].join('\r\n')));
return bufs;
}
const boundary = '-----------------------------168072824752491622650073';
const buffers = createMultipartBuffers(boundary, (new Array(100)).fill(1));
const calls = {
partBegin: 0,
headerField: 0,
headerValue: 0,
headerEnd: 0,
headersEnd: 0,
partData: 0,
partEnd: 0,
end: 0,
};
const moduleName = process.argv[2];
switch (moduleName) {
case 'busboy': {
const busboy = require('busboy');
const parser = busboy({
limits: {
fieldSizeLimit: Infinity,
},
headers: {
'content-type': `multipart/form-data; boundary=${boundary}`,
},
});
parser.on('field', (name, val, info) => {
++calls.partBegin;
++calls.partData;
++calls.partEnd;
}).on('close', () => {
++calls.end;
console.timeEnd(moduleName);
});
console.time(moduleName);
for (const buf of buffers)
parser.write(buf);
break;
}
case 'formidable': {
const { MultipartParser } = require('formidable');
const parser = new MultipartParser();
parser.initWithBoundary(boundary);
parser.on('data', ({ name }) => {
++calls[name];
if (name === 'end')
console.timeEnd(moduleName);
});
console.time(moduleName);
for (const buf of buffers)
parser.write(buf);
break;
}
case 'multiparty': {
const { Readable } = require('stream');
const { Form } = require('multiparty');
const form = new Form({
maxFieldsSize: Infinity,
maxFields: Infinity,
maxFilesSize: Infinity,
autoFields: false,
autoFiles: false,
});
const req = new Readable({ read: () => {} });
req.headers = {
'content-type': `multipart/form-data; boundary=${boundary}`,
};
function hijack(name, fn) {
const oldFn = form[name];
form[name] = function() {
fn();
return oldFn.apply(this, arguments);
};
}
hijack('onParseHeaderField', () => {
++calls.headerField;
});
hijack('onParseHeaderValue', () => {
++calls.headerValue;
});
hijack('onParsePartBegin', () => {
++calls.partBegin;
});
hijack('onParsePartData', () => {
++calls.partData;
});
hijack('onParsePartEnd', () => {
++calls.partEnd;
});
form.on('close', () => {
++calls.end;
console.timeEnd(moduleName);
}).on('part', (p) => p.resume());
console.time(moduleName);
form.parse(req);
for (const buf of buffers)
req.push(buf);
req.push(null);
break;
}
default:
if (moduleName === undefined)
console.error('Missing parser module name');
else
console.error(`Invalid parser module name: ${moduleName}`);
process.exit(1);
}

View file

@ -0,0 +1,154 @@
'use strict';
function createMultipartBuffers(boundary, sizes) {
const bufs = [];
for (let i = 0; i < sizes.length; ++i) {
const mb = sizes[i] * 1024 * 1024;
bufs.push(Buffer.from([
`--${boundary}`,
`content-disposition: form-data; name="file${i + 1}"; `
+ `filename="random${i + 1}.bin"`,
'content-type: application/octet-stream',
'',
'0'.repeat(mb),
'',
].join('\r\n')));
}
bufs.push(Buffer.from([
`--${boundary}--`,
'',
].join('\r\n')));
return bufs;
}
const boundary = '-----------------------------168072824752491622650073';
const buffers = createMultipartBuffers(boundary, [
10,
10,
10,
20,
50,
]);
const calls = {
partBegin: 0,
headerField: 0,
headerValue: 0,
headerEnd: 0,
headersEnd: 0,
partData: 0,
partEnd: 0,
end: 0,
};
const moduleName = process.argv[2];
switch (moduleName) {
case 'busboy': {
const busboy = require('busboy');
const parser = busboy({
limits: {
fieldSizeLimit: Infinity,
},
headers: {
'content-type': `multipart/form-data; boundary=${boundary}`,
},
});
parser.on('file', (name, stream, info) => {
++calls.partBegin;
stream.on('data', (chunk) => {
++calls.partData;
}).on('end', () => {
++calls.partEnd;
});
}).on('close', () => {
++calls.end;
console.timeEnd(moduleName);
});
console.time(moduleName);
for (const buf of buffers)
parser.write(buf);
break;
}
case 'formidable': {
const { MultipartParser } = require('formidable');
const parser = new MultipartParser();
parser.initWithBoundary(boundary);
parser.on('data', ({ name }) => {
++calls[name];
if (name === 'end')
console.timeEnd(moduleName);
});
console.time(moduleName);
for (const buf of buffers)
parser.write(buf);
break;
}
case 'multiparty': {
const { Readable } = require('stream');
const { Form } = require('multiparty');
const form = new Form({
maxFieldsSize: Infinity,
maxFields: Infinity,
maxFilesSize: Infinity,
autoFields: false,
autoFiles: false,
});
const req = new Readable({ read: () => {} });
req.headers = {
'content-type': `multipart/form-data; boundary=${boundary}`,
};
function hijack(name, fn) {
const oldFn = form[name];
form[name] = function() {
fn();
return oldFn.apply(this, arguments);
};
}
hijack('onParseHeaderField', () => {
++calls.headerField;
});
hijack('onParseHeaderValue', () => {
++calls.headerValue;
});
hijack('onParsePartBegin', () => {
++calls.partBegin;
});
hijack('onParsePartData', () => {
++calls.partData;
});
hijack('onParsePartEnd', () => {
++calls.partEnd;
});
form.on('close', () => {
++calls.end;
console.timeEnd(moduleName);
}).on('part', (p) => p.resume());
console.time(moduleName);
form.parse(req);
for (const buf of buffers)
req.push(buf);
req.push(null);
break;
}
default:
if (moduleName === undefined)
console.error('Missing parser module name');
else
console.error(`Invalid parser module name: ${moduleName}`);
process.exit(1);
}

View file

@ -0,0 +1,148 @@
'use strict';
function createMultipartBuffers(boundary, sizes) {
const bufs = [];
for (let i = 0; i < sizes.length; ++i) {
const mb = sizes[i] * 1024 * 1024;
bufs.push(Buffer.from([
`--${boundary}`,
`content-disposition: form-data; name="file${i + 1}"; `
+ `filename="random${i + 1}.bin"`,
'content-type: application/octet-stream',
'',
'0'.repeat(mb),
'',
].join('\r\n')));
}
bufs.push(Buffer.from([
`--${boundary}--`,
'',
].join('\r\n')));
return bufs;
}
const boundary = '-----------------------------168072824752491622650073';
const buffers = createMultipartBuffers(boundary, (new Array(100)).fill(1));
const calls = {
partBegin: 0,
headerField: 0,
headerValue: 0,
headerEnd: 0,
headersEnd: 0,
partData: 0,
partEnd: 0,
end: 0,
};
const moduleName = process.argv[2];
switch (moduleName) {
case 'busboy': {
const busboy = require('busboy');
const parser = busboy({
limits: {
fieldSizeLimit: Infinity,
},
headers: {
'content-type': `multipart/form-data; boundary=${boundary}`,
},
});
parser.on('file', (name, stream, info) => {
++calls.partBegin;
stream.on('data', (chunk) => {
++calls.partData;
}).on('end', () => {
++calls.partEnd;
});
}).on('close', () => {
++calls.end;
console.timeEnd(moduleName);
});
console.time(moduleName);
for (const buf of buffers)
parser.write(buf);
break;
}
case 'formidable': {
const { MultipartParser } = require('formidable');
const parser = new MultipartParser();
parser.initWithBoundary(boundary);
parser.on('data', ({ name }) => {
++calls[name];
if (name === 'end')
console.timeEnd(moduleName);
});
console.time(moduleName);
for (const buf of buffers)
parser.write(buf);
break;
}
case 'multiparty': {
const { Readable } = require('stream');
const { Form } = require('multiparty');
const form = new Form({
maxFieldsSize: Infinity,
maxFields: Infinity,
maxFilesSize: Infinity,
autoFields: false,
autoFiles: false,
});
const req = new Readable({ read: () => {} });
req.headers = {
'content-type': `multipart/form-data; boundary=${boundary}`,
};
function hijack(name, fn) {
const oldFn = form[name];
form[name] = function() {
fn();
return oldFn.apply(this, arguments);
};
}
hijack('onParseHeaderField', () => {
++calls.headerField;
});
hijack('onParseHeaderValue', () => {
++calls.headerValue;
});
hijack('onParsePartBegin', () => {
++calls.partBegin;
});
hijack('onParsePartData', () => {
++calls.partData;
});
hijack('onParsePartEnd', () => {
++calls.partEnd;
});
form.on('close', () => {
++calls.end;
console.timeEnd(moduleName);
}).on('part', (p) => p.resume());
console.time(moduleName);
form.parse(req);
for (const buf of buffers)
req.push(buf);
req.push(null);
break;
}
default:
if (moduleName === undefined)
console.error('Missing parser module name');
else
console.error(`Invalid parser module name: ${moduleName}`);
process.exit(1);
}

View file

@ -0,0 +1,101 @@
'use strict';
const buffers = [
Buffer.from(
(new Array(100)).fill('').map((_, i) => `key${i}=value${i}`).join('&')
),
];
const calls = {
field: 0,
end: 0,
};
let n = 3e3;
const moduleName = process.argv[2];
switch (moduleName) {
case 'busboy': {
const busboy = require('busboy');
console.time(moduleName);
(function next() {
const parser = busboy({
limits: {
fieldSizeLimit: Infinity,
},
headers: {
'content-type': 'application/x-www-form-urlencoded; charset=utf-8',
},
});
parser.on('field', (name, val, info) => {
++calls.field;
}).on('close', () => {
++calls.end;
if (--n === 0)
console.timeEnd(moduleName);
else
process.nextTick(next);
});
for (const buf of buffers)
parser.write(buf);
parser.end();
})();
break;
}
case 'formidable': {
const QuerystringParser =
require('formidable/src/parsers/Querystring.js');
console.time(moduleName);
(function next() {
const parser = new QuerystringParser();
parser.on('data', (obj) => {
++calls.field;
}).on('end', () => {
++calls.end;
if (--n === 0)
console.timeEnd(moduleName);
else
process.nextTick(next);
});
for (const buf of buffers)
parser.write(buf);
parser.end();
})();
break;
}
case 'formidable-streaming': {
const QuerystringParser =
require('formidable/src/parsers/StreamingQuerystring.js');
console.time(moduleName);
(function next() {
const parser = new QuerystringParser();
parser.on('data', (obj) => {
++calls.field;
}).on('end', () => {
++calls.end;
if (--n === 0)
console.timeEnd(moduleName);
else
process.nextTick(next);
});
for (const buf of buffers)
parser.write(buf);
parser.end();
})();
break;
}
default:
if (moduleName === undefined)
console.error('Missing parser module name');
else
console.error(`Invalid parser module name: ${moduleName}`);
process.exit(1);
}

View file

@ -0,0 +1,84 @@
'use strict';
const buffers = [
Buffer.from(
(new Array(900)).fill('').map((_, i) => `key${i}=value${i}`).join('&')
),
];
const calls = {
field: 0,
end: 0,
};
const moduleName = process.argv[2];
switch (moduleName) {
case 'busboy': {
const busboy = require('busboy');
console.time(moduleName);
const parser = busboy({
limits: {
fieldSizeLimit: Infinity,
},
headers: {
'content-type': 'application/x-www-form-urlencoded; charset=utf-8',
},
});
parser.on('field', (name, val, info) => {
++calls.field;
}).on('close', () => {
++calls.end;
console.timeEnd(moduleName);
});
for (const buf of buffers)
parser.write(buf);
parser.end();
break;
}
case 'formidable': {
const QuerystringParser =
require('formidable/src/parsers/Querystring.js');
console.time(moduleName);
const parser = new QuerystringParser();
parser.on('data', (obj) => {
++calls.field;
}).on('end', () => {
++calls.end;
console.timeEnd(moduleName);
});
for (const buf of buffers)
parser.write(buf);
parser.end();
break;
}
case 'formidable-streaming': {
const QuerystringParser =
require('formidable/src/parsers/StreamingQuerystring.js');
console.time(moduleName);
const parser = new QuerystringParser();
parser.on('data', (obj) => {
++calls.field;
}).on('end', () => {
++calls.end;
console.timeEnd(moduleName);
});
for (const buf of buffers)
parser.write(buf);
parser.end();
break;
}
default:
if (moduleName === undefined)
console.error('Missing parser module name');
else
console.error(`Invalid parser module name: ${moduleName}`);
process.exit(1);
}

57
node_modules/busboy/lib/index.js generated vendored Normal file
View file

@ -0,0 +1,57 @@
'use strict';
const { parseContentType } = require('./utils.js');
function getInstance(cfg) {
const headers = cfg.headers;
const conType = parseContentType(headers['content-type']);
if (!conType)
throw new Error('Malformed content type');
for (const type of TYPES) {
const matched = type.detect(conType);
if (!matched)
continue;
const instanceCfg = {
limits: cfg.limits,
headers,
conType,
highWaterMark: undefined,
fileHwm: undefined,
defCharset: undefined,
defParamCharset: undefined,
preservePath: false,
};
if (cfg.highWaterMark)
instanceCfg.highWaterMark = cfg.highWaterMark;
if (cfg.fileHwm)
instanceCfg.fileHwm = cfg.fileHwm;
instanceCfg.defCharset = cfg.defCharset;
instanceCfg.defParamCharset = cfg.defParamCharset;
instanceCfg.preservePath = cfg.preservePath;
return new type(instanceCfg);
}
throw new Error(`Unsupported content type: ${headers['content-type']}`);
}
// Note: types are explicitly listed here for easier bundling
// See: https://github.com/mscdex/busboy/issues/121
const TYPES = [
require('./types/multipart'),
require('./types/urlencoded'),
].filter(function(typemod) { return typeof typemod.detect === 'function'; });
module.exports = (cfg) => {
if (typeof cfg !== 'object' || cfg === null)
cfg = {};
if (typeof cfg.headers !== 'object'
|| cfg.headers === null
|| typeof cfg.headers['content-type'] !== 'string') {
throw new Error('Missing Content-Type');
}
return getInstance(cfg);
};

653
node_modules/busboy/lib/types/multipart.js generated vendored Normal file
View file

@ -0,0 +1,653 @@
'use strict';
const { Readable, Writable } = require('stream');
const StreamSearch = require('streamsearch');
const {
basename,
convertToUTF8,
getDecoder,
parseContentType,
parseDisposition,
} = require('../utils.js');
const BUF_CRLF = Buffer.from('\r\n');
const BUF_CR = Buffer.from('\r');
const BUF_DASH = Buffer.from('-');
function noop() {}
const MAX_HEADER_PAIRS = 2000; // From node
const MAX_HEADER_SIZE = 16 * 1024; // From node (its default value)
const HPARSER_NAME = 0;
const HPARSER_PRE_OWS = 1;
const HPARSER_VALUE = 2;
class HeaderParser {
constructor(cb) {
this.header = Object.create(null);
this.pairCount = 0;
this.byteCount = 0;
this.state = HPARSER_NAME;
this.name = '';
this.value = '';
this.crlf = 0;
this.cb = cb;
}
reset() {
this.header = Object.create(null);
this.pairCount = 0;
this.byteCount = 0;
this.state = HPARSER_NAME;
this.name = '';
this.value = '';
this.crlf = 0;
}
push(chunk, pos, end) {
let start = pos;
while (pos < end) {
switch (this.state) {
case HPARSER_NAME: {
let done = false;
for (; pos < end; ++pos) {
if (this.byteCount === MAX_HEADER_SIZE)
return -1;
++this.byteCount;
const code = chunk[pos];
if (TOKEN[code] !== 1) {
if (code !== 58/* ':' */)
return -1;
this.name += chunk.latin1Slice(start, pos);
if (this.name.length === 0)
return -1;
++pos;
done = true;
this.state = HPARSER_PRE_OWS;
break;
}
}
if (!done) {
this.name += chunk.latin1Slice(start, pos);
break;
}
// FALLTHROUGH
}
case HPARSER_PRE_OWS: {
// Skip optional whitespace
let done = false;
for (; pos < end; ++pos) {
if (this.byteCount === MAX_HEADER_SIZE)
return -1;
++this.byteCount;
const code = chunk[pos];
if (code !== 32/* ' ' */ && code !== 9/* '\t' */) {
start = pos;
done = true;
this.state = HPARSER_VALUE;
break;
}
}
if (!done)
break;
// FALLTHROUGH
}
case HPARSER_VALUE:
switch (this.crlf) {
case 0: // Nothing yet
for (; pos < end; ++pos) {
if (this.byteCount === MAX_HEADER_SIZE)
return -1;
++this.byteCount;
const code = chunk[pos];
if (FIELD_VCHAR[code] !== 1) {
if (code !== 13/* '\r' */)
return -1;
++this.crlf;
break;
}
}
this.value += chunk.latin1Slice(start, pos++);
break;
case 1: // Received CR
if (this.byteCount === MAX_HEADER_SIZE)
return -1;
++this.byteCount;
if (chunk[pos++] !== 10/* '\n' */)
return -1;
++this.crlf;
break;
case 2: { // Received CR LF
if (this.byteCount === MAX_HEADER_SIZE)
return -1;
++this.byteCount;
const code = chunk[pos];
if (code === 32/* ' ' */ || code === 9/* '\t' */) {
// Folded value
start = pos;
this.crlf = 0;
} else {
if (++this.pairCount < MAX_HEADER_PAIRS) {
this.name = this.name.toLowerCase();
if (this.header[this.name] === undefined)
this.header[this.name] = [this.value];
else
this.header[this.name].push(this.value);
}
if (code === 13/* '\r' */) {
++this.crlf;
++pos;
} else {
// Assume start of next header field name
start = pos;
this.crlf = 0;
this.state = HPARSER_NAME;
this.name = '';
this.value = '';
}
}
break;
}
case 3: { // Received CR LF CR
if (this.byteCount === MAX_HEADER_SIZE)
return -1;
++this.byteCount;
if (chunk[pos++] !== 10/* '\n' */)
return -1;
// End of header
const header = this.header;
this.reset();
this.cb(header);
return pos;
}
}
break;
}
}
return pos;
}
}
class FileStream extends Readable {
constructor(opts, owner) {
super(opts);
this.truncated = false;
this._readcb = null;
this.once('end', () => {
// We need to make sure that we call any outstanding _writecb() that is
// associated with this file so that processing of the rest of the form
// can continue. This may not happen if the file stream ends right after
// backpressure kicks in, so we force it here.
this._read();
if (--owner._fileEndsLeft === 0 && owner._finalcb) {
const cb = owner._finalcb;
owner._finalcb = null;
// Make sure other 'end' event handlers get a chance to be executed
// before busboy's 'finish' event is emitted
process.nextTick(cb);
}
});
}
_read(n) {
const cb = this._readcb;
if (cb) {
this._readcb = null;
cb();
}
}
}
const ignoreData = {
push: (chunk, pos) => {},
destroy: () => {},
};
function callAndUnsetCb(self, err) {
const cb = self._writecb;
self._writecb = null;
if (err)
self.destroy(err);
else if (cb)
cb();
}
function nullDecoder(val, hint) {
return val;
}
class Multipart extends Writable {
constructor(cfg) {
const streamOpts = {
autoDestroy: true,
emitClose: true,
highWaterMark: (typeof cfg.highWaterMark === 'number'
? cfg.highWaterMark
: undefined),
};
super(streamOpts);
if (!cfg.conType.params || typeof cfg.conType.params.boundary !== 'string')
throw new Error('Multipart: Boundary not found');
const boundary = cfg.conType.params.boundary;
const paramDecoder = (typeof cfg.defParamCharset === 'string'
&& cfg.defParamCharset
? getDecoder(cfg.defParamCharset)
: nullDecoder);
const defCharset = (cfg.defCharset || 'utf8');
const preservePath = cfg.preservePath;
const fileOpts = {
autoDestroy: true,
emitClose: true,
highWaterMark: (typeof cfg.fileHwm === 'number'
? cfg.fileHwm
: undefined),
};
const limits = cfg.limits;
const fieldSizeLimit = (limits && typeof limits.fieldSize === 'number'
? limits.fieldSize
: 1 * 1024 * 1024);
const fileSizeLimit = (limits && typeof limits.fileSize === 'number'
? limits.fileSize
: Infinity);
const filesLimit = (limits && typeof limits.files === 'number'
? limits.files
: Infinity);
const fieldsLimit = (limits && typeof limits.fields === 'number'
? limits.fields
: Infinity);
const partsLimit = (limits && typeof limits.parts === 'number'
? limits.parts
: Infinity);
let parts = -1; // Account for initial boundary
let fields = 0;
let files = 0;
let skipPart = false;
this._fileEndsLeft = 0;
this._fileStream = undefined;
this._complete = false;
let fileSize = 0;
let field;
let fieldSize = 0;
let partCharset;
let partEncoding;
let partType;
let partName;
let partTruncated = false;
let hitFilesLimit = false;
let hitFieldsLimit = false;
this._hparser = null;
const hparser = new HeaderParser((header) => {
this._hparser = null;
skipPart = false;
partType = 'text/plain';
partCharset = defCharset;
partEncoding = '7bit';
partName = undefined;
partTruncated = false;
let filename;
if (!header['content-disposition']) {
skipPart = true;
return;
}
const disp = parseDisposition(header['content-disposition'][0],
paramDecoder);
if (!disp || disp.type !== 'form-data') {
skipPart = true;
return;
}
if (disp.params) {
if (disp.params.name)
partName = disp.params.name;
if (disp.params['filename*'])
filename = disp.params['filename*'];
else if (disp.params.filename)
filename = disp.params.filename;
if (filename !== undefined && !preservePath)
filename = basename(filename);
}
if (header['content-type']) {
const conType = parseContentType(header['content-type'][0]);
if (conType) {
partType = `${conType.type}/${conType.subtype}`;
if (conType.params && typeof conType.params.charset === 'string')
partCharset = conType.params.charset.toLowerCase();
}
}
if (header['content-transfer-encoding'])
partEncoding = header['content-transfer-encoding'][0].toLowerCase();
if (partType === 'application/octet-stream' || filename !== undefined) {
// File
if (files === filesLimit) {
if (!hitFilesLimit) {
hitFilesLimit = true;
this.emit('filesLimit');
}
skipPart = true;
return;
}
++files;
if (this.listenerCount('file') === 0) {
skipPart = true;
return;
}
fileSize = 0;
this._fileStream = new FileStream(fileOpts, this);
++this._fileEndsLeft;
this.emit(
'file',
partName,
this._fileStream,
{ filename,
encoding: partEncoding,
mimeType: partType }
);
} else {
// Non-file
if (fields === fieldsLimit) {
if (!hitFieldsLimit) {
hitFieldsLimit = true;
this.emit('fieldsLimit');
}
skipPart = true;
return;
}
++fields;
if (this.listenerCount('field') === 0) {
skipPart = true;
return;
}
field = [];
fieldSize = 0;
}
});
let matchPostBoundary = 0;
const ssCb = (isMatch, data, start, end, isDataSafe) => {
retrydata:
while (data) {
if (this._hparser !== null) {
const ret = this._hparser.push(data, start, end);
if (ret === -1) {
this._hparser = null;
hparser.reset();
this.emit('error', new Error('Malformed part header'));
break;
}
start = ret;
}
if (start === end)
break;
if (matchPostBoundary !== 0) {
if (matchPostBoundary === 1) {
switch (data[start]) {
case 45: // '-'
// Try matching '--' after boundary
matchPostBoundary = 2;
++start;
break;
case 13: // '\r'
// Try matching CR LF before header
matchPostBoundary = 3;
++start;
break;
default:
matchPostBoundary = 0;
}
if (start === end)
return;
}
if (matchPostBoundary === 2) {
matchPostBoundary = 0;
if (data[start] === 45/* '-' */) {
// End of multipart data
this._complete = true;
this._bparser = ignoreData;
return;
}
// We saw something other than '-', so put the dash we consumed
// "back"
const writecb = this._writecb;
this._writecb = noop;
ssCb(false, BUF_DASH, 0, 1, false);
this._writecb = writecb;
} else if (matchPostBoundary === 3) {
matchPostBoundary = 0;
if (data[start] === 10/* '\n' */) {
++start;
if (parts >= partsLimit)
break;
// Prepare the header parser
this._hparser = hparser;
if (start === end)
break;
// Process the remaining data as a header
continue retrydata;
} else {
// We saw something other than LF, so put the CR we consumed
// "back"
const writecb = this._writecb;
this._writecb = noop;
ssCb(false, BUF_CR, 0, 1, false);
this._writecb = writecb;
}
}
}
if (!skipPart) {
if (this._fileStream) {
let chunk;
const actualLen = Math.min(end - start, fileSizeLimit - fileSize);
if (!isDataSafe) {
chunk = Buffer.allocUnsafe(actualLen);
data.copy(chunk, 0, start, start + actualLen);
} else {
chunk = data.slice(start, start + actualLen);
}
fileSize += chunk.length;
if (fileSize === fileSizeLimit) {
if (chunk.length > 0)
this._fileStream.push(chunk);
this._fileStream.emit('limit');
this._fileStream.truncated = true;
skipPart = true;
} else if (!this._fileStream.push(chunk)) {
if (this._writecb)
this._fileStream._readcb = this._writecb;
this._writecb = null;
}
} else if (field !== undefined) {
let chunk;
const actualLen = Math.min(
end - start,
fieldSizeLimit - fieldSize
);
if (!isDataSafe) {
chunk = Buffer.allocUnsafe(actualLen);
data.copy(chunk, 0, start, start + actualLen);
} else {
chunk = data.slice(start, start + actualLen);
}
fieldSize += actualLen;
field.push(chunk);
if (fieldSize === fieldSizeLimit) {
skipPart = true;
partTruncated = true;
}
}
}
break;
}
if (isMatch) {
matchPostBoundary = 1;
if (this._fileStream) {
// End the active file stream if the previous part was a file
this._fileStream.push(null);
this._fileStream = null;
} else if (field !== undefined) {
let data;
switch (field.length) {
case 0:
data = '';
break;
case 1:
data = convertToUTF8(field[0], partCharset, 0);
break;
default:
data = convertToUTF8(
Buffer.concat(field, fieldSize),
partCharset,
0
);
}
field = undefined;
fieldSize = 0;
this.emit(
'field',
partName,
data,
{ nameTruncated: false,
valueTruncated: partTruncated,
encoding: partEncoding,
mimeType: partType }
);
}
if (++parts === partsLimit)
this.emit('partsLimit');
}
};
this._bparser = new StreamSearch(`\r\n--${boundary}`, ssCb);
this._writecb = null;
this._finalcb = null;
// Just in case there is no preamble
this.write(BUF_CRLF);
}
static detect(conType) {
return (conType.type === 'multipart' && conType.subtype === 'form-data');
}
_write(chunk, enc, cb) {
this._writecb = cb;
this._bparser.push(chunk, 0);
if (this._writecb)
callAndUnsetCb(this);
}
_destroy(err, cb) {
this._hparser = null;
this._bparser = ignoreData;
if (!err)
err = checkEndState(this);
const fileStream = this._fileStream;
if (fileStream) {
this._fileStream = null;
fileStream.destroy(err);
}
cb(err);
}
_final(cb) {
this._bparser.destroy();
if (!this._complete)
return cb(new Error('Unexpected end of form'));
if (this._fileEndsLeft)
this._finalcb = finalcb.bind(null, this, cb);
else
finalcb(this, cb);
}
}
function finalcb(self, cb, err) {
if (err)
return cb(err);
err = checkEndState(self);
cb(err);
}
function checkEndState(self) {
if (self._hparser)
return new Error('Malformed part header');
const fileStream = self._fileStream;
if (fileStream) {
self._fileStream = null;
fileStream.destroy(new Error('Unexpected end of file'));
}
if (!self._complete)
return new Error('Unexpected end of form');
}
const TOKEN = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
const FIELD_VCHAR = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
];
module.exports = Multipart;

350
node_modules/busboy/lib/types/urlencoded.js generated vendored Normal file
View file

@ -0,0 +1,350 @@
'use strict';
const { Writable } = require('stream');
const { getDecoder } = require('../utils.js');
class URLEncoded extends Writable {
constructor(cfg) {
const streamOpts = {
autoDestroy: true,
emitClose: true,
highWaterMark: (typeof cfg.highWaterMark === 'number'
? cfg.highWaterMark
: undefined),
};
super(streamOpts);
let charset = (cfg.defCharset || 'utf8');
if (cfg.conType.params && typeof cfg.conType.params.charset === 'string')
charset = cfg.conType.params.charset;
this.charset = charset;
const limits = cfg.limits;
this.fieldSizeLimit = (limits && typeof limits.fieldSize === 'number'
? limits.fieldSize
: 1 * 1024 * 1024);
this.fieldsLimit = (limits && typeof limits.fields === 'number'
? limits.fields
: Infinity);
this.fieldNameSizeLimit = (
limits && typeof limits.fieldNameSize === 'number'
? limits.fieldNameSize
: 100
);
this._inKey = true;
this._keyTrunc = false;
this._valTrunc = false;
this._bytesKey = 0;
this._bytesVal = 0;
this._fields = 0;
this._key = '';
this._val = '';
this._byte = -2;
this._lastPos = 0;
this._encode = 0;
this._decoder = getDecoder(charset);
}
static detect(conType) {
return (conType.type === 'application'
&& conType.subtype === 'x-www-form-urlencoded');
}
_write(chunk, enc, cb) {
if (this._fields >= this.fieldsLimit)
return cb();
let i = 0;
const len = chunk.length;
this._lastPos = 0;
// Check if we last ended mid-percent-encoded byte
if (this._byte !== -2) {
i = readPctEnc(this, chunk, i, len);
if (i === -1)
return cb(new Error('Malformed urlencoded form'));
if (i >= len)
return cb();
if (this._inKey)
++this._bytesKey;
else
++this._bytesVal;
}
main:
while (i < len) {
if (this._inKey) {
// Parsing key
i = skipKeyBytes(this, chunk, i, len);
while (i < len) {
switch (chunk[i]) {
case 61: // '='
if (this._lastPos < i)
this._key += chunk.latin1Slice(this._lastPos, i);
this._lastPos = ++i;
this._key = this._decoder(this._key, this._encode);
this._encode = 0;
this._inKey = false;
continue main;
case 38: // '&'
if (this._lastPos < i)
this._key += chunk.latin1Slice(this._lastPos, i);
this._lastPos = ++i;
this._key = this._decoder(this._key, this._encode);
this._encode = 0;
if (this._bytesKey > 0) {
this.emit(
'field',
this._key,
'',
{ nameTruncated: this._keyTrunc,
valueTruncated: false,
encoding: this.charset,
mimeType: 'text/plain' }
);
}
this._key = '';
this._val = '';
this._keyTrunc = false;
this._valTrunc = false;
this._bytesKey = 0;
this._bytesVal = 0;
if (++this._fields >= this.fieldsLimit) {
this.emit('fieldsLimit');
return cb();
}
continue;
case 43: // '+'
if (this._lastPos < i)
this._key += chunk.latin1Slice(this._lastPos, i);
this._key += ' ';
this._lastPos = i + 1;
break;
case 37: // '%'
if (this._encode === 0)
this._encode = 1;
if (this._lastPos < i)
this._key += chunk.latin1Slice(this._lastPos, i);
this._lastPos = i + 1;
this._byte = -1;
i = readPctEnc(this, chunk, i + 1, len);
if (i === -1)
return cb(new Error('Malformed urlencoded form'));
if (i >= len)
return cb();
++this._bytesKey;
i = skipKeyBytes(this, chunk, i, len);
continue;
}
++i;
++this._bytesKey;
i = skipKeyBytes(this, chunk, i, len);
}
if (this._lastPos < i)
this._key += chunk.latin1Slice(this._lastPos, i);
} else {
// Parsing value
i = skipValBytes(this, chunk, i, len);
while (i < len) {
switch (chunk[i]) {
case 38: // '&'
if (this._lastPos < i)
this._val += chunk.latin1Slice(this._lastPos, i);
this._lastPos = ++i;
this._inKey = true;
this._val = this._decoder(this._val, this._encode);
this._encode = 0;
if (this._bytesKey > 0 || this._bytesVal > 0) {
this.emit(
'field',
this._key,
this._val,
{ nameTruncated: this._keyTrunc,
valueTruncated: this._valTrunc,
encoding: this.charset,
mimeType: 'text/plain' }
);
}
this._key = '';
this._val = '';
this._keyTrunc = false;
this._valTrunc = false;
this._bytesKey = 0;
this._bytesVal = 0;
if (++this._fields >= this.fieldsLimit) {
this.emit('fieldsLimit');
return cb();
}
continue main;
case 43: // '+'
if (this._lastPos < i)
this._val += chunk.latin1Slice(this._lastPos, i);
this._val += ' ';
this._lastPos = i + 1;
break;
case 37: // '%'
if (this._encode === 0)
this._encode = 1;
if (this._lastPos < i)
this._val += chunk.latin1Slice(this._lastPos, i);
this._lastPos = i + 1;
this._byte = -1;
i = readPctEnc(this, chunk, i + 1, len);
if (i === -1)
return cb(new Error('Malformed urlencoded form'));
if (i >= len)
return cb();
++this._bytesVal;
i = skipValBytes(this, chunk, i, len);
continue;
}
++i;
++this._bytesVal;
i = skipValBytes(this, chunk, i, len);
}
if (this._lastPos < i)
this._val += chunk.latin1Slice(this._lastPos, i);
}
}
cb();
}
_final(cb) {
if (this._byte !== -2)
return cb(new Error('Malformed urlencoded form'));
if (!this._inKey || this._bytesKey > 0 || this._bytesVal > 0) {
if (this._inKey)
this._key = this._decoder(this._key, this._encode);
else
this._val = this._decoder(this._val, this._encode);
this.emit(
'field',
this._key,
this._val,
{ nameTruncated: this._keyTrunc,
valueTruncated: this._valTrunc,
encoding: this.charset,
mimeType: 'text/plain' }
);
}
cb();
}
}
function readPctEnc(self, chunk, pos, len) {
if (pos >= len)
return len;
if (self._byte === -1) {
// We saw a '%' but no hex characters yet
const hexUpper = HEX_VALUES[chunk[pos++]];
if (hexUpper === -1)
return -1;
if (hexUpper >= 8)
self._encode = 2; // Indicate high bits detected
if (pos < len) {
// Both hex characters are in this chunk
const hexLower = HEX_VALUES[chunk[pos++]];
if (hexLower === -1)
return -1;
if (self._inKey)
self._key += String.fromCharCode((hexUpper << 4) + hexLower);
else
self._val += String.fromCharCode((hexUpper << 4) + hexLower);
self._byte = -2;
self._lastPos = pos;
} else {
// Only one hex character was available in this chunk
self._byte = hexUpper;
}
} else {
// We saw only one hex character so far
const hexLower = HEX_VALUES[chunk[pos++]];
if (hexLower === -1)
return -1;
if (self._inKey)
self._key += String.fromCharCode((self._byte << 4) + hexLower);
else
self._val += String.fromCharCode((self._byte << 4) + hexLower);
self._byte = -2;
self._lastPos = pos;
}
return pos;
}
function skipKeyBytes(self, chunk, pos, len) {
// Skip bytes if we've truncated
if (self._bytesKey > self.fieldNameSizeLimit) {
if (!self._keyTrunc) {
if (self._lastPos < pos)
self._key += chunk.latin1Slice(self._lastPos, pos - 1);
}
self._keyTrunc = true;
for (; pos < len; ++pos) {
const code = chunk[pos];
if (code === 61/* '=' */ || code === 38/* '&' */)
break;
++self._bytesKey;
}
self._lastPos = pos;
}
return pos;
}
function skipValBytes(self, chunk, pos, len) {
// Skip bytes if we've truncated
if (self._bytesVal > self.fieldSizeLimit) {
if (!self._valTrunc) {
if (self._lastPos < pos)
self._val += chunk.latin1Slice(self._lastPos, pos - 1);
}
self._valTrunc = true;
for (; pos < len; ++pos) {
if (chunk[pos] === 38/* '&' */)
break;
++self._bytesVal;
}
self._lastPos = pos;
}
return pos;
}
/* eslint-disable no-multi-spaces */
const HEX_VALUES = [
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
];
/* eslint-enable no-multi-spaces */
module.exports = URLEncoded;

596
node_modules/busboy/lib/utils.js generated vendored Normal file
View file

@ -0,0 +1,596 @@
'use strict';
function parseContentType(str) {
if (str.length === 0)
return;
const params = Object.create(null);
let i = 0;
// Parse type
for (; i < str.length; ++i) {
const code = str.charCodeAt(i);
if (TOKEN[code] !== 1) {
if (code !== 47/* '/' */ || i === 0)
return;
break;
}
}
// Check for type without subtype
if (i === str.length)
return;
const type = str.slice(0, i).toLowerCase();
// Parse subtype
const subtypeStart = ++i;
for (; i < str.length; ++i) {
const code = str.charCodeAt(i);
if (TOKEN[code] !== 1) {
// Make sure we have a subtype
if (i === subtypeStart)
return;
if (parseContentTypeParams(str, i, params) === undefined)
return;
break;
}
}
// Make sure we have a subtype
if (i === subtypeStart)
return;
const subtype = str.slice(subtypeStart, i).toLowerCase();
return { type, subtype, params };
}
function parseContentTypeParams(str, i, params) {
while (i < str.length) {
// Consume whitespace
for (; i < str.length; ++i) {
const code = str.charCodeAt(i);
if (code !== 32/* ' ' */ && code !== 9/* '\t' */)
break;
}
// Ended on whitespace
if (i === str.length)
break;
// Check for malformed parameter
if (str.charCodeAt(i++) !== 59/* ';' */)
return;
// Consume whitespace
for (; i < str.length; ++i) {
const code = str.charCodeAt(i);
if (code !== 32/* ' ' */ && code !== 9/* '\t' */)
break;
}
// Ended on whitespace (malformed)
if (i === str.length)
return;
let name;
const nameStart = i;
// Parse parameter name
for (; i < str.length; ++i) {
const code = str.charCodeAt(i);
if (TOKEN[code] !== 1) {
if (code !== 61/* '=' */)
return;
break;
}
}
// No value (malformed)
if (i === str.length)
return;
name = str.slice(nameStart, i);
++i; // Skip over '='
// No value (malformed)
if (i === str.length)
return;
let value = '';
let valueStart;
if (str.charCodeAt(i) === 34/* '"' */) {
valueStart = ++i;
let escaping = false;
// Parse quoted value
for (; i < str.length; ++i) {
const code = str.charCodeAt(i);
if (code === 92/* '\\' */) {
if (escaping) {
valueStart = i;
escaping = false;
} else {
value += str.slice(valueStart, i);
escaping = true;
}
continue;
}
if (code === 34/* '"' */) {
if (escaping) {
valueStart = i;
escaping = false;
continue;
}
value += str.slice(valueStart, i);
break;
}
if (escaping) {
valueStart = i - 1;
escaping = false;
}
// Invalid unescaped quoted character (malformed)
if (QDTEXT[code] !== 1)
return;
}
// No end quote (malformed)
if (i === str.length)
return;
++i; // Skip over double quote
} else {
valueStart = i;
// Parse unquoted value
for (; i < str.length; ++i) {
const code = str.charCodeAt(i);
if (TOKEN[code] !== 1) {
// No value (malformed)
if (i === valueStart)
return;
break;
}
}
value = str.slice(valueStart, i);
}
name = name.toLowerCase();
if (params[name] === undefined)
params[name] = value;
}
return params;
}
function parseDisposition(str, defDecoder) {
if (str.length === 0)
return;
const params = Object.create(null);
let i = 0;
for (; i < str.length; ++i) {
const code = str.charCodeAt(i);
if (TOKEN[code] !== 1) {
if (parseDispositionParams(str, i, params, defDecoder) === undefined)
return;
break;
}
}
const type = str.slice(0, i).toLowerCase();
return { type, params };
}
function parseDispositionParams(str, i, params, defDecoder) {
while (i < str.length) {
// Consume whitespace
for (; i < str.length; ++i) {
const code = str.charCodeAt(i);
if (code !== 32/* ' ' */ && code !== 9/* '\t' */)
break;
}
// Ended on whitespace
if (i === str.length)
break;
// Check for malformed parameter
if (str.charCodeAt(i++) !== 59/* ';' */)
return;
// Consume whitespace
for (; i < str.length; ++i) {
const code = str.charCodeAt(i);
if (code !== 32/* ' ' */ && code !== 9/* '\t' */)
break;
}
// Ended on whitespace (malformed)
if (i === str.length)
return;
let name;
const nameStart = i;
// Parse parameter name
for (; i < str.length; ++i) {
const code = str.charCodeAt(i);
if (TOKEN[code] !== 1) {
if (code === 61/* '=' */)
break;
return;
}
}
// No value (malformed)
if (i === str.length)
return;
let value = '';
let valueStart;
let charset;
//~ let lang;
name = str.slice(nameStart, i);
if (name.charCodeAt(name.length - 1) === 42/* '*' */) {
// Extended value
const charsetStart = ++i;
// Parse charset name
for (; i < str.length; ++i) {
const code = str.charCodeAt(i);
if (CHARSET[code] !== 1) {
if (code !== 39/* '\'' */)
return;
break;
}
}
// Incomplete charset (malformed)
if (i === str.length)
return;
charset = str.slice(charsetStart, i);
++i; // Skip over the '\''
//~ const langStart = ++i;
// Parse language name
for (; i < str.length; ++i) {
const code = str.charCodeAt(i);
if (code === 39/* '\'' */)
break;
}
// Incomplete language (malformed)
if (i === str.length)
return;
//~ lang = str.slice(langStart, i);
++i; // Skip over the '\''
// No value (malformed)
if (i === str.length)
return;
valueStart = i;
let encode = 0;
// Parse value
for (; i < str.length; ++i) {
const code = str.charCodeAt(i);
if (EXTENDED_VALUE[code] !== 1) {
if (code === 37/* '%' */) {
let hexUpper;
let hexLower;
if (i + 2 < str.length
&& (hexUpper = HEX_VALUES[str.charCodeAt(i + 1)]) !== -1
&& (hexLower = HEX_VALUES[str.charCodeAt(i + 2)]) !== -1) {
const byteVal = (hexUpper << 4) + hexLower;
value += str.slice(valueStart, i);
value += String.fromCharCode(byteVal);
i += 2;
valueStart = i + 1;
if (byteVal >= 128)
encode = 2;
else if (encode === 0)
encode = 1;
continue;
}
// '%' disallowed in non-percent encoded contexts (malformed)
return;
}
break;
}
}
value += str.slice(valueStart, i);
value = convertToUTF8(value, charset, encode);
if (value === undefined)
return;
} else {
// Non-extended value
++i; // Skip over '='
// No value (malformed)
if (i === str.length)
return;
if (str.charCodeAt(i) === 34/* '"' */) {
valueStart = ++i;
let escaping = false;
// Parse quoted value
for (; i < str.length; ++i) {
const code = str.charCodeAt(i);
if (code === 92/* '\\' */) {
if (escaping) {
valueStart = i;
escaping = false;
} else {
value += str.slice(valueStart, i);
escaping = true;
}
continue;
}
if (code === 34/* '"' */) {
if (escaping) {
valueStart = i;
escaping = false;
continue;
}
value += str.slice(valueStart, i);
break;
}
if (escaping) {
valueStart = i - 1;
escaping = false;
}
// Invalid unescaped quoted character (malformed)
if (QDTEXT[code] !== 1)
return;
}
// No end quote (malformed)
if (i === str.length)
return;
++i; // Skip over double quote
} else {
valueStart = i;
// Parse unquoted value
for (; i < str.length; ++i) {
const code = str.charCodeAt(i);
if (TOKEN[code] !== 1) {
// No value (malformed)
if (i === valueStart)
return;
break;
}
}
value = str.slice(valueStart, i);
}
value = defDecoder(value, 2);
if (value === undefined)
return;
}
name = name.toLowerCase();
if (params[name] === undefined)
params[name] = value;
}
return params;
}
function getDecoder(charset) {
let lc;
while (true) {
switch (charset) {
case 'utf-8':
case 'utf8':
return decoders.utf8;
case 'latin1':
case 'ascii': // TODO: Make these a separate, strict decoder?
case 'us-ascii':
case 'iso-8859-1':
case 'iso8859-1':
case 'iso88591':
case 'iso_8859-1':
case 'windows-1252':
case 'iso_8859-1:1987':
case 'cp1252':
case 'x-cp1252':
return decoders.latin1;
case 'utf16le':
case 'utf-16le':
case 'ucs2':
case 'ucs-2':
return decoders.utf16le;
case 'base64':
return decoders.base64;
default:
if (lc === undefined) {
lc = true;
charset = charset.toLowerCase();
continue;
}
return decoders.other.bind(charset);
}
}
}
const decoders = {
utf8: (data, hint) => {
if (data.length === 0)
return '';
if (typeof data === 'string') {
// If `data` never had any percent-encoded bytes or never had any that
// were outside of the ASCII range, then we can safely just return the
// input since UTF-8 is ASCII compatible
if (hint < 2)
return data;
data = Buffer.from(data, 'latin1');
}
return data.utf8Slice(0, data.length);
},
latin1: (data, hint) => {
if (data.length === 0)
return '';
if (typeof data === 'string')
return data;
return data.latin1Slice(0, data.length);
},
utf16le: (data, hint) => {
if (data.length === 0)
return '';
if (typeof data === 'string')
data = Buffer.from(data, 'latin1');
return data.ucs2Slice(0, data.length);
},
base64: (data, hint) => {
if (data.length === 0)
return '';
if (typeof data === 'string')
data = Buffer.from(data, 'latin1');
return data.base64Slice(0, data.length);
},
other: (data, hint) => {
if (data.length === 0)
return '';
if (typeof data === 'string')
data = Buffer.from(data, 'latin1');
try {
const decoder = new TextDecoder(this);
return decoder.decode(data);
} catch {}
},
};
function convertToUTF8(data, charset, hint) {
const decode = getDecoder(charset);
if (decode)
return decode(data, hint);
}
function basename(path) {
if (typeof path !== 'string')
return '';
for (let i = path.length - 1; i >= 0; --i) {
switch (path.charCodeAt(i)) {
case 0x2F: // '/'
case 0x5C: // '\'
path = path.slice(i + 1);
return (path === '..' || path === '.' ? '' : path);
}
}
return (path === '..' || path === '.' ? '' : path);
}
const TOKEN = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
const QDTEXT = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
];
const CHARSET = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
const EXTENDED_VALUE = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
/* eslint-disable no-multi-spaces */
const HEX_VALUES = [
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
];
/* eslint-enable no-multi-spaces */
module.exports = {
basename,
convertToUTF8,
getDecoder,
parseContentType,
parseDisposition,
};

22
node_modules/busboy/package.json generated vendored Normal file
View file

@ -0,0 +1,22 @@
{ "name": "busboy",
"version": "1.6.0",
"author": "Brian White <mscdex@mscdex.net>",
"description": "A streaming parser for HTML form data for node.js",
"main": "./lib/index.js",
"dependencies": {
"streamsearch": "^1.1.0"
},
"devDependencies": {
"@mscdex/eslint-config": "^1.1.0",
"eslint": "^7.32.0"
},
"scripts": {
"test": "node test/test.js",
"lint": "eslint --cache --report-unused-disable-directives --ext=.js .eslintrc.js lib test bench",
"lint:fix": "npm run lint -- --fix"
},
"engines": { "node": ">=10.16.0" },
"keywords": [ "uploads", "forms", "multipart", "form-data" ],
"licenses": [ { "type": "MIT", "url": "http://github.com/mscdex/busboy/raw/master/LICENSE" } ],
"repository": { "type": "git", "url": "http://github.com/mscdex/busboy.git" }
}

109
node_modules/busboy/test/common.js generated vendored Normal file
View file

@ -0,0 +1,109 @@
'use strict';
const assert = require('assert');
const { inspect } = require('util');
const mustCallChecks = [];
function noop() {}
function runCallChecks(exitCode) {
if (exitCode !== 0) return;
const failed = mustCallChecks.filter((context) => {
if ('minimum' in context) {
context.messageSegment = `at least ${context.minimum}`;
return context.actual < context.minimum;
}
context.messageSegment = `exactly ${context.exact}`;
return context.actual !== context.exact;
});
failed.forEach((context) => {
console.error('Mismatched %s function calls. Expected %s, actual %d.',
context.name,
context.messageSegment,
context.actual);
console.error(context.stack.split('\n').slice(2).join('\n'));
});
if (failed.length)
process.exit(1);
}
function mustCall(fn, exact) {
return _mustCallInner(fn, exact, 'exact');
}
function mustCallAtLeast(fn, minimum) {
return _mustCallInner(fn, minimum, 'minimum');
}
function _mustCallInner(fn, criteria = 1, field) {
if (process._exiting)
throw new Error('Cannot use common.mustCall*() in process exit handler');
if (typeof fn === 'number') {
criteria = fn;
fn = noop;
} else if (fn === undefined) {
fn = noop;
}
if (typeof criteria !== 'number')
throw new TypeError(`Invalid ${field} value: ${criteria}`);
const context = {
[field]: criteria,
actual: 0,
stack: inspect(new Error()),
name: fn.name || '<anonymous>'
};
// Add the exit listener only once to avoid listener leak warnings
if (mustCallChecks.length === 0)
process.on('exit', runCallChecks);
mustCallChecks.push(context);
function wrapped(...args) {
++context.actual;
return fn.call(this, ...args);
}
// TODO: remove origFn?
wrapped.origFn = fn;
return wrapped;
}
function getCallSite(top) {
const originalStackFormatter = Error.prepareStackTrace;
Error.prepareStackTrace = (err, stack) =>
`${stack[0].getFileName()}:${stack[0].getLineNumber()}`;
const err = new Error();
Error.captureStackTrace(err, top);
// With the V8 Error API, the stack is not formatted until it is accessed
// eslint-disable-next-line no-unused-expressions
err.stack;
Error.prepareStackTrace = originalStackFormatter;
return err.stack;
}
function mustNotCall(msg) {
const callSite = getCallSite(mustNotCall);
return function mustNotCall(...args) {
args = args.map(inspect).join(', ');
const argsInfo = (args.length > 0
? `\ncalled with arguments: ${args}`
: '');
assert.fail(
`${msg || 'function should not have been called'} at ${callSite}`
+ argsInfo);
};
}
module.exports = {
mustCall,
mustCallAtLeast,
mustNotCall,
};

View file

@ -0,0 +1,94 @@
'use strict';
const assert = require('assert');
const { inspect } = require('util');
const { mustCall } = require(`${__dirname}/common.js`);
const busboy = require('..');
const input = Buffer.from([
'-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k',
'Content-Disposition: form-data; '
+ 'name="upload_file_0"; filename="テスト.dat"',
'Content-Type: application/octet-stream',
'',
'A'.repeat(1023),
'-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k--'
].join('\r\n'));
const boundary = '---------------------------paZqsnEHRufoShdX6fh0lUhXBP4k';
const expected = [
{ type: 'file',
name: 'upload_file_0',
data: Buffer.from('A'.repeat(1023)),
info: {
filename: 'テスト.dat',
encoding: '7bit',
mimeType: 'application/octet-stream',
},
limited: false,
},
];
const bb = busboy({
defParamCharset: 'utf8',
headers: {
'content-type': `multipart/form-data; boundary=${boundary}`,
}
});
const results = [];
bb.on('field', (name, val, info) => {
results.push({ type: 'field', name, val, info });
});
bb.on('file', (name, stream, info) => {
const data = [];
let nb = 0;
const file = {
type: 'file',
name,
data: null,
info,
limited: false,
};
results.push(file);
stream.on('data', (d) => {
data.push(d);
nb += d.length;
}).on('limit', () => {
file.limited = true;
}).on('close', () => {
file.data = Buffer.concat(data, nb);
assert.strictEqual(stream.truncated, file.limited);
}).once('error', (err) => {
file.err = err.message;
});
});
bb.on('error', (err) => {
results.push({ error: err.message });
});
bb.on('partsLimit', () => {
results.push('partsLimit');
});
bb.on('filesLimit', () => {
results.push('filesLimit');
});
bb.on('fieldsLimit', () => {
results.push('fieldsLimit');
});
bb.on('close', mustCall(() => {
assert.deepStrictEqual(
results,
expected,
'Results mismatch.\n'
+ `Parsed: ${inspect(results)}\n`
+ `Expected: ${inspect(expected)}`
);
}));
bb.end(input);

View file

@ -0,0 +1,102 @@
'use strict';
const assert = require('assert');
const { randomFillSync } = require('crypto');
const { inspect } = require('util');
const busboy = require('..');
const { mustCall } = require('./common.js');
const BOUNDARY = 'u2KxIV5yF1y+xUspOQCCZopaVgeV6Jxihv35XQJmuTx8X3sh';
function formDataSection(key, value) {
return Buffer.from(
`\r\n--${BOUNDARY}`
+ `\r\nContent-Disposition: form-data; name="${key}"`
+ `\r\n\r\n${value}`
);
}
function formDataFile(key, filename, contentType) {
const buf = Buffer.allocUnsafe(100000);
return Buffer.concat([
Buffer.from(`\r\n--${BOUNDARY}\r\n`),
Buffer.from(`Content-Disposition: form-data; name="${key}"`
+ `; filename="${filename}"\r\n`),
Buffer.from(`Content-Type: ${contentType}\r\n\r\n`),
randomFillSync(buf)
]);
}
const reqChunks = [
Buffer.concat([
formDataFile('file', 'file.bin', 'application/octet-stream'),
formDataSection('foo', 'foo value'),
]),
formDataSection('bar', 'bar value'),
Buffer.from(`\r\n--${BOUNDARY}--\r\n`)
];
const bb = busboy({
headers: {
'content-type': `multipart/form-data; boundary=${BOUNDARY}`
}
});
const expected = [
{ type: 'file',
name: 'file',
info: {
filename: 'file.bin',
encoding: '7bit',
mimeType: 'application/octet-stream',
},
},
{ type: 'field',
name: 'foo',
val: 'foo value',
info: {
nameTruncated: false,
valueTruncated: false,
encoding: '7bit',
mimeType: 'text/plain',
},
},
{ type: 'field',
name: 'bar',
val: 'bar value',
info: {
nameTruncated: false,
valueTruncated: false,
encoding: '7bit',
mimeType: 'text/plain',
},
},
];
const results = [];
bb.on('field', (name, val, info) => {
results.push({ type: 'field', name, val, info });
});
bb.on('file', (name, stream, info) => {
results.push({ type: 'file', name, info });
// Simulate a pipe where the destination is pausing (perhaps due to waiting
// for file system write to finish)
setTimeout(() => {
stream.resume();
}, 10);
});
bb.on('close', mustCall(() => {
assert.deepStrictEqual(
results,
expected,
'Results mismatch.\n'
+ `Parsed: ${inspect(results)}\n`
+ `Expected: ${inspect(expected)}`
);
}));
for (const chunk of reqChunks)
bb.write(chunk);
bb.end();

1053
node_modules/busboy/test/test-types-multipart.js generated vendored Normal file

File diff suppressed because it is too large Load diff

488
node_modules/busboy/test/test-types-urlencoded.js generated vendored Normal file
View file

@ -0,0 +1,488 @@
'use strict';
const assert = require('assert');
const { transcode } = require('buffer');
const { inspect } = require('util');
const busboy = require('..');
const active = new Map();
const tests = [
{ source: ['foo'],
expected: [
['foo',
'',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
],
what: 'Unassigned value'
},
{ source: ['foo=bar'],
expected: [
['foo',
'bar',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
],
what: 'Assigned value'
},
{ source: ['foo&bar=baz'],
expected: [
['foo',
'',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
['bar',
'baz',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
],
what: 'Unassigned and assigned value'
},
{ source: ['foo=bar&baz'],
expected: [
['foo',
'bar',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
['baz',
'',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
],
what: 'Assigned and unassigned value'
},
{ source: ['foo=bar&baz=bla'],
expected: [
['foo',
'bar',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
['baz',
'bla',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
],
what: 'Two assigned values'
},
{ source: ['foo&bar'],
expected: [
['foo',
'',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
['bar',
'',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
],
what: 'Two unassigned values'
},
{ source: ['foo&bar&'],
expected: [
['foo',
'',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
['bar',
'',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
],
what: 'Two unassigned values and ampersand'
},
{ source: ['foo+1=bar+baz%2Bquux'],
expected: [
['foo 1',
'bar baz+quux',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
],
what: 'Assigned key and value with (plus) space'
},
{ source: ['foo=bar%20baz%21'],
expected: [
['foo',
'bar baz!',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
],
what: 'Assigned value with encoded bytes'
},
{ source: ['foo%20bar=baz%20bla%21'],
expected: [
['foo bar',
'baz bla!',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
],
what: 'Assigned value with encoded bytes #2'
},
{ source: ['foo=bar%20baz%21&num=1000'],
expected: [
['foo',
'bar baz!',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
['num',
'1000',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
],
what: 'Two assigned values, one with encoded bytes'
},
{ source: [
Array.from(transcode(Buffer.from('foo'), 'utf8', 'utf16le')).map(
(n) => `%${n.toString(16).padStart(2, '0')}`
).join(''),
'=',
Array.from(transcode(Buffer.from('😀!'), 'utf8', 'utf16le')).map(
(n) => `%${n.toString(16).padStart(2, '0')}`
).join(''),
],
expected: [
['foo',
'😀!',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'UTF-16LE',
mimeType: 'text/plain' },
],
],
charset: 'UTF-16LE',
what: 'Encoded value with multi-byte charset'
},
{ source: [
'foo=<',
Array.from(transcode(Buffer.from('©:^þ'), 'utf8', 'latin1')).map(
(n) => `%${n.toString(16).padStart(2, '0')}`
).join(''),
],
expected: [
['foo',
'<©:^þ',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'ISO-8859-1',
mimeType: 'text/plain' },
],
],
charset: 'ISO-8859-1',
what: 'Encoded value with single-byte, ASCII-compatible, non-UTF8 charset'
},
{ source: ['foo=bar&baz=bla'],
expected: [],
what: 'Limits: zero fields',
limits: { fields: 0 }
},
{ source: ['foo=bar&baz=bla'],
expected: [
['foo',
'bar',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
],
what: 'Limits: one field',
limits: { fields: 1 }
},
{ source: ['foo=bar&baz=bla'],
expected: [
['foo',
'bar',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
['baz',
'bla',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
],
what: 'Limits: field part lengths match limits',
limits: { fieldNameSize: 3, fieldSize: 3 }
},
{ source: ['foo=bar&baz=bla'],
expected: [
['fo',
'bar',
{ nameTruncated: true,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
['ba',
'bla',
{ nameTruncated: true,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
],
what: 'Limits: truncated field name',
limits: { fieldNameSize: 2 }
},
{ source: ['foo=bar&baz=bla'],
expected: [
['foo',
'ba',
{ nameTruncated: false,
valueTruncated: true,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
['baz',
'bl',
{ nameTruncated: false,
valueTruncated: true,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
],
what: 'Limits: truncated field value',
limits: { fieldSize: 2 }
},
{ source: ['foo=bar&baz=bla'],
expected: [
['fo',
'ba',
{ nameTruncated: true,
valueTruncated: true,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
['ba',
'bl',
{ nameTruncated: true,
valueTruncated: true,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
],
what: 'Limits: truncated field name and value',
limits: { fieldNameSize: 2, fieldSize: 2 }
},
{ source: ['foo=bar&baz=bla'],
expected: [
['fo',
'',
{ nameTruncated: true,
valueTruncated: true,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
['ba',
'',
{ nameTruncated: true,
valueTruncated: true,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
],
what: 'Limits: truncated field name and zero value limit',
limits: { fieldNameSize: 2, fieldSize: 0 }
},
{ source: ['foo=bar&baz=bla'],
expected: [
['',
'',
{ nameTruncated: true,
valueTruncated: true,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
['',
'',
{ nameTruncated: true,
valueTruncated: true,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
],
what: 'Limits: truncated zero field name and zero value limit',
limits: { fieldNameSize: 0, fieldSize: 0 }
},
{ source: ['&'],
expected: [],
what: 'Ampersand'
},
{ source: ['&&&&&'],
expected: [],
what: 'Many ampersands'
},
{ source: ['='],
expected: [
['',
'',
{ nameTruncated: false,
valueTruncated: false,
encoding: 'utf-8',
mimeType: 'text/plain' },
],
],
what: 'Assigned value, empty name and value'
},
{ source: [''],
expected: [],
what: 'Nothing'
},
];
for (const test of tests) {
active.set(test, 1);
const { what } = test;
const charset = test.charset || 'utf-8';
const bb = busboy({
limits: test.limits,
headers: {
'content-type': `application/x-www-form-urlencoded; charset=${charset}`,
},
});
const results = [];
bb.on('field', (key, val, info) => {
results.push([key, val, info]);
});
bb.on('file', () => {
throw new Error(`[${what}] Unexpected file`);
});
bb.on('close', () => {
active.delete(test);
assert.deepStrictEqual(
results,
test.expected,
`[${what}] Results mismatch.\n`
+ `Parsed: ${inspect(results)}\n`
+ `Expected: ${inspect(test.expected)}`
);
});
for (const src of test.source) {
const buf = (typeof src === 'string' ? Buffer.from(src, 'utf8') : src);
bb.write(buf);
}
bb.end();
}
// Byte-by-byte versions
for (let test of tests) {
test = { ...test };
test.what += ' (byte-by-byte)';
active.set(test, 1);
const { what } = test;
const charset = test.charset || 'utf-8';
const bb = busboy({
limits: test.limits,
headers: {
'content-type': `application/x-www-form-urlencoded; charset="${charset}"`,
},
});
const results = [];
bb.on('field', (key, val, info) => {
results.push([key, val, info]);
});
bb.on('file', () => {
throw new Error(`[${what}] Unexpected file`);
});
bb.on('close', () => {
active.delete(test);
assert.deepStrictEqual(
results,
test.expected,
`[${what}] Results mismatch.\n`
+ `Parsed: ${inspect(results)}\n`
+ `Expected: ${inspect(test.expected)}`
);
});
for (const src of test.source) {
const buf = (typeof src === 'string' ? Buffer.from(src, 'utf8') : src);
for (let i = 0; i < buf.length; ++i)
bb.write(buf.slice(i, i + 1));
}
bb.end();
}
{
let exception = false;
process.once('uncaughtException', (ex) => {
exception = true;
throw ex;
});
process.on('exit', () => {
if (exception || active.size === 0)
return;
process.exitCode = 1;
console.error('==========================');
console.error(`${active.size} test(s) did not finish:`);
console.error('==========================');
console.error(Array.from(active.keys()).map((v) => v.what).join('\n'));
});
}

20
node_modules/busboy/test/test.js generated vendored Normal file
View file

@ -0,0 +1,20 @@
'use strict';
const { spawnSync } = require('child_process');
const { readdirSync } = require('fs');
const { join } = require('path');
const files = readdirSync(__dirname).sort();
for (const filename of files) {
if (filename.startsWith('test-')) {
const path = join(__dirname, filename);
console.log(`> Running ${filename} ...`);
const result = spawnSync(`${process.argv0} ${path}`, {
shell: true,
stdio: 'inherit',
windowsHide: true
});
if (result.status !== 0)
process.exitCode = 1;
}
}

54
node_modules/express-fileupload/.circleci/config.yml generated vendored Normal file
View file

@ -0,0 +1,54 @@
version: 2.1
orbs:
node: circleci/node@5.1.0
jobs:
lintandcoverage:
docker:
- image: cimg/node:16.20.2
steps:
- checkout
- run: npm install
- run: npm run lint
- run: npm run test
- run: npm run coveralls
workflows:
test:
jobs:
- lintandcoverage:
context:
- COVERALLS
- node/test:
version: '16.20.2'
pkg-manager: npm
filters:
tags:
ignore:
- /.*/
- node/test:
version: '17.9.1'
pkg-manager: npm
filters:
tags:
ignore:
- /.*/
- node/test:
version: '18.17.1'
pkg-manager: npm
filters:
tags:
ignore:
- /.*/
- node/test:
version: '19.9.0'
pkg-manager: npm
filters:
tags:
ignore:
- /.*/
- node/test:
version: '20.6.0'
pkg-manager: npm
filters:
tags:
ignore:
- /.*/

1
node_modules/express-fileupload/.eslintignore generated vendored Normal file
View file

@ -0,0 +1 @@
coverage

23
node_modules/express-fileupload/.eslintrc generated vendored Normal file
View file

@ -0,0 +1,23 @@
{
"extends": [
"eslint:recommended"
],
"env": {
"node": true,
"mocha": true,
"es6": true
},
"parserOptions": {
"ecmaVersion": 6
},
"rules": {
"comma-dangle": [2, "never"],
"max-len": [2, {
"code": 100,
"tabWidth": 2
}],
"semi": 2,
"keyword-spacing": 2,
"indent": [2, 2, { "SwitchCase": 1 }]
}
}

3
node_modules/express-fileupload/.mocharc.json generated vendored Normal file
View file

@ -0,0 +1,3 @@
{
"spec": "test/**/*.spec.js"
}

1
node_modules/express-fileupload/.prettierrc generated vendored Normal file
View file

@ -0,0 +1 @@
{singleQuote: true}

22
node_modules/express-fileupload/LICENSE generated vendored Normal file
View file

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Richard Girges
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

130
node_modules/express-fileupload/README.md generated vendored Normal file
View file

@ -0,0 +1,130 @@
# express-fileupload
Simple express middleware for uploading files.
[![npm](https://img.shields.io/npm/v/express-fileupload.svg)](https://www.npmjs.org/package/express-fileupload)
[![downloads per month](http://img.shields.io/npm/dm/express-fileupload.svg)](https://www.npmjs.org/package/express-fileupload)
[![CircleCI](https://circleci.com/gh/richardgirges/express-fileupload/tree/master.svg?style=svg)](https://circleci.com/gh/richardgirges/express-fileupload/tree/master)
[![Coverage Status](https://img.shields.io/coveralls/richardgirges/express-fileupload.svg)](https://coveralls.io/r/richardgirges/express-fileupload)
# Help us Improve express-fileupload
This package is still very much supported and maintained. But the more help the better. If you're interested any of the following:
* Ticket and PR triage
* Feature scoping and implementation
* Maintenance (upgrading packages, fixing security vulnerabilities, etc)
...please contact richardgirges '-at-' gmail.com
# Install
```bash
# With NPM
npm i express-fileupload
# With Yarn
yarn add express-fileupload
```
# Usage
When you upload a file, the file will be accessible from `req.files`.
Example:
* You're uploading a file called **car.jpg**
* Your input's name field is **foo**: `<input name="foo" type="file" />`
* In your express server request, you can access your uploaded file from `req.files.foo`:
```javascript
app.post('/upload', function(req, res) {
console.log(req.files.foo); // the uploaded file object
});
```
The **req.files.foo** object will contain the following:
* `req.files.foo.name`: "car.jpg"
* `req.files.foo.mv`: A function to move the file elsewhere on your server. Can take a callback or return a promise.
* `req.files.foo.mimetype`: The mimetype of your file
* `req.files.foo.data`: A buffer representation of your file, returns empty buffer in case useTempFiles option was set to true.
* `req.files.foo.tempFilePath`: A path to the temporary file in case useTempFiles option was set to true.
* `req.files.foo.truncated`: A boolean that represents if the file is over the size limit
* `req.files.foo.size`: Uploaded size in bytes
* `req.files.foo.md5`: MD5 checksum of the uploaded file
**Notes about breaking changes with MD5 handling:**
* Before 1.0.0, `md5` is an MD5 checksum of the uploaded file.
* From 1.0.0 until 1.1.1, `md5` is a function to compute an MD5 hash ([Read about it here.](https://github.com/richardgirges/express-fileupload/releases/tag/v1.0.0-alpha.1)).
* From 1.1.1 onward, `md5` is reverted back to MD5 checksum value and also added full MD5 support in case you are using temporary files.
### Examples
* [Example Project](https://github.com/richardgirges/express-fileupload/tree/master/example)
* [Basic File Upload](https://github.com/richardgirges/express-fileupload/tree/master/example#basic-file-upload)
* [Multi-File Upload](https://github.com/richardgirges/express-fileupload/tree/master/example#multi-file-upload)
### Using Busboy Options
Pass in Busboy options directly to the express-fileupload middleware. [Check out the Busboy documentation here](https://github.com/mscdex/busboy#api).
```javascript
app.use(fileUpload({
limits: { fileSize: 50 * 1024 * 1024 },
}));
```
### Using useTempFile Options
Use temp files instead of memory for managing the upload process.
```javascript
// Note that this option available for versions 1.0.0 and newer.
app.use(fileUpload({
useTempFiles : true,
tempFileDir : '/tmp/'
}));
```
### Using debug option
You can set `debug` option to `true` to see some logging about upload process.
In this case middleware uses `console.log` and adds `Express-file-upload` prefix for outputs.
It will show you whether the request is invalid and also common events triggered during upload.
That can be really useful for troubleshooting and ***we recommend attaching debug output to each issue on Github***.
***Output example:***
```
Express-file-upload: Temporary file path is /node/express-fileupload/test/temp/tmp-16-1570084843942
Express-file-upload: New upload started testFile->car.png, bytes:0
Express-file-upload: Uploading testFile->car.png, bytes:21232...
Express-file-upload: Uploading testFile->car.png, bytes:86768...
Express-file-upload: Upload timeout testFile->car.png, bytes:86768
Express-file-upload: Cleaning up temporary file /node/express-fileupload/test/temp/tmp-16-1570084843942...
```
***Description:***
* `Temporary file path is...` says that `useTempfiles` was set to true and also shows you temp file name and path.
* `New upload started testFile->car.png` says that new upload started with field `testFile` and file name `car.png`.
* `Uploading testFile->car.png, bytes:21232...` shows current progress for each new data chunk.
* `Upload timeout` means that no data came during `uploadTimeout`.
* `Cleaning up temporary file` Here finaly we see cleaning up of the temporary file because of upload timeout reached.
### Available Options
Pass in non-Busboy options directly to the middleware. These are express-fileupload specific options.
Option | Acceptable&nbsp;Values | Details
--- | --- | ---
createParentPath | <ul><li><code>false</code>&nbsp;**(default)**</li><li><code>true</code></ul> | Automatically creates the directory path specified in `.mv(filePathName)`
uriDecodeFileNames | <ul><li><code>false</code>&nbsp;**(default)**</li><li><code>true</code></ul> | Applies uri decoding to file names if set true.
safeFileNames | <ul><li><code>false</code>&nbsp;**(default)**</li><li><code>true</code></li><li>regex</li></ul> | Strips characters from the upload's filename. You can use custom regex to determine what to strip. If set to `true`, non-alphanumeric characters _except_ dashes and underscores will be stripped. This option is off by default.<br /><br />**Example #1 (strip slashes from file names):** `app.use(fileUpload({ safeFileNames: /\\/g }))`<br />**Example #2:** `app.use(fileUpload({ safeFileNames: true }))`
preserveExtension | <ul><li><code>false</code>&nbsp;**(default)**</li><li><code>true</code></li><li><code>*Number*</code></li></ul> | Preserves filename extension when using <code>safeFileNames</code> option. If set to <code>true</code>, will default to an extension length of 3. If set to <code>*Number*</code>, this will be the max allowable extension length. If an extension is smaller than the extension length, it remains untouched. If the extension is longer, it is shifted.<br /><br />**Example #1 (true):**<br /><code>app.use(fileUpload({ safeFileNames: true, preserveExtension: true }));</code><br />*myFileName.ext* --> *myFileName.ext*<br /><br />**Example #2 (max extension length 2, extension shifted):**<br /><code>app.use(fileUpload({ safeFileNames: true, preserveExtension: 2 }));</code><br />*myFileName.ext* --> *myFileNamee.xt*
abortOnLimit | <ul><li><code>false</code>&nbsp;**(default)**</li><li><code>true</code></ul> | Returns a HTTP 413 when the file is bigger than the size limit if true. Otherwise, it will add a <code>truncated = true</code> to the resulting file structure.
responseOnLimit | <ul><li><code>'File size limit has been reached'</code>&nbsp;**(default)**</li><li><code>*String*</code></ul> | Response which will be send to client if file size limit exceeded when abortOnLimit set to true.
limitHandler | <ul><li><code>false</code>&nbsp;**(default)**</li><li><code>function(req, res, next)</code></li></ul> | User defined limit handler which will be invoked if the file is bigger than configured limits.
useTempFiles | <ul><li><code>false</code>&nbsp;**(default)**</li><li><code>true</code></ul> | By default this module uploads files into RAM. Setting this option to True turns on using temporary files instead of utilising RAM. This avoids memory overflow issues when uploading large files or in case of uploading lots of files at same time.
tempFileDir | <ul><li><code>String</code>&nbsp;**(path)**</li></ul> | Path to store temporary files.<br />Used along with the <code>useTempFiles</code> option. By default this module uses 'tmp' folder in the current working directory.<br />You can use trailing slash, but it is not necessary.
parseNested | <ul><li><code>false</code>&nbsp;**(default)**</li><li><code>true</code></li></ul> | By default, req.body and req.files are flattened like this: <code>{'name': 'John', 'hobbies[0]': 'Cinema', 'hobbies[1]': 'Bike'}</code><br /><br/>When this option is enabled they are parsed in order to be nested like this: <code>{'name': 'John', 'hobbies': ['Cinema', 'Bike']}</code>
debug | <ul><li><code>false</code>&nbsp;**(default)**</li><li><code>true</code></ul> | Turn on/off upload process logging. Can be useful for troubleshooting.
uploadTimeout | <ul><li><code>60000</code>&nbsp;**(default)**</li><li><code>Integer</code></ul> | This defines how long to wait for data before aborting. Set to 0 if you want to turn off timeout checks.
# Help Wanted
Looking for additional maintainers. Please contact `richardgirges [ at ] gmail.com` if you're interested. Pull Requests are welcome!
# Thanks & Credit
[Brian White](https://github.com/mscdex) for his stellar work on the [Busboy Package](https://github.com/mscdex/busboy) and the [connect-busboy Package](https://github.com/mscdex/connect-busboy)

15
node_modules/express-fileupload/SECURITY.md generated vendored Normal file
View file

@ -0,0 +1,15 @@
# Security Policy
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
| ------- | ------------------ |
| 1.3.x | :white_check_mark: |
| 1.2.1 | :white_check_mark: |
| < 1.2.1 | :x: |
## Reporting a Vulnerability
Please report (suspected) security vulnerabilities to richardgirges@gmail.com. You will receive a response from us within 72 hours. If the issue is confirmed, we will release a patch as soon as possible depending on complexity but historically within a few days.

70
node_modules/express-fileupload/example/README.md generated vendored Normal file
View file

@ -0,0 +1,70 @@
# express-fileupload Examples
## Basic File Upload
**Your node.js code:**
```javascript
const express = require('express');
const fileUpload = require('express-fileupload');
const app = express();
// default options
app.use(fileUpload());
app.post('/upload', function(req, res) {
let sampleFile;
let uploadPath;
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send('No files were uploaded.');
}
// The name of the input field (i.e. "sampleFile") is used to retrieve the uploaded file
sampleFile = req.files.sampleFile;
uploadPath = __dirname + '/somewhere/on/your/server/' + sampleFile.name;
// Use the mv() method to place the file somewhere on your server
sampleFile.mv(uploadPath, function(err) {
if (err)
return res.status(500).send(err);
res.send('File uploaded!');
});
});
```
**Your HTML file upload form:**
```html
<html>
<body>
<form ref='uploadForm'
id='uploadForm'
action='http://localhost:8000/upload'
method='post'
encType="multipart/form-data">
<input type="file" name="sampleFile" />
<input type='submit' value='Upload!' />
</form>
</body>
</html>
```
## Multi-File Upload
express-fileupload supports multiple file uploads at the same time.
Let's say you have three files in your form, each of the inputs with the name `my_profile_pic`, `my_pet`, and `my_cover_photo`:
```html
<input type="file" name="my_profile_pic" />
<input type="file" name="my_pet" />
<input type="file" name="my_cover_photo" />
```
These uploaded files would be accessible like so:
```javascript
app.post('/upload', function(req, res) {
// Uploaded files:
console.log(req.files.my_profile_pic.name);
console.log(req.files.my_pet.name);
console.log(req.files.my_cover_photo.name);
});
```

12
node_modules/express-fileupload/example/index.html generated vendored Normal file
View file

@ -0,0 +1,12 @@
<html>
<body>
<form ref='uploadForm'
id='uploadForm'
action='/upload'
method='post'
encType="multipart/form-data">
<input type="file" name="sampleFile" />
<input type='submit' value='Upload!' />
</form>
</body>
</html>

41
node_modules/express-fileupload/example/server.js generated vendored Normal file
View file

@ -0,0 +1,41 @@
const express = require('express');
const fileUpload = require('../lib/index');
const app = express();
const PORT = 8000;
app.use('/form', express.static(__dirname + '/index.html'));
// default options
app.use(fileUpload());
app.get('/ping', function(req, res) {
res.send('pong');
});
app.post('/upload', function(req, res) {
let sampleFile;
let uploadPath;
if (!req.files || Object.keys(req.files).length === 0) {
res.status(400).send('No files were uploaded.');
return;
}
console.log('req.files >>>', req.files); // eslint-disable-line
sampleFile = req.files.sampleFile;
uploadPath = __dirname + '/uploads/' + sampleFile.name;
sampleFile.mv(uploadPath, function(err) {
if (err) {
return res.status(500).send(err);
}
res.send('File uploaded to ' + uploadPath);
});
});
app.listen(PORT, function() {
console.log('Express server listening on port ', PORT); // eslint-disable-line
});

View file

@ -0,0 +1 @@
files are placed here when uploaded using the upload.test.js express server

65
node_modules/express-fileupload/lib/fileFactory.js generated vendored Normal file
View file

@ -0,0 +1,65 @@
'use strict';
const {
isFunc,
debugLog,
moveFile,
promiseCallback,
checkAndMakeDir,
saveBufferToFile
} = require('./utilities');
/**
* Returns Local function that moves the file to a different location on the filesystem
* which takes two function arguments to make it compatible w/ Promise or Callback APIs
* @param {String} filePath - destination file path.
* @param {Object} options - file factory options.
* @param {Object} fileUploadOptions - middleware options.
* @returns {Function}
*/
const moveFromTemp = (filePath, options, fileUploadOptions) => (resolve, reject) => {
debugLog(fileUploadOptions, `Moving temporary file ${options.tempFilePath} to ${filePath}`);
moveFile(options.tempFilePath, filePath, promiseCallback(resolve, reject));
};
/**
* Returns Local function that moves the file from buffer to a different location on the filesystem
* which takes two function arguments to make it compatible w/ Promise or Callback APIs
* @param {String} filePath - destination file path.
* @param {Object} options - file factory options.
* @param {Object} fileUploadOptions - middleware options.
* @returns {Function}
*/
const moveFromBuffer = (filePath, options, fileUploadOptions) => (resolve, reject) => {
debugLog(fileUploadOptions, `Moving uploaded buffer to ${filePath}`);
saveBufferToFile(options.buffer, filePath, promiseCallback(resolve, reject));
};
module.exports = (options, fileUploadOptions = {}) => {
// see: https://github.com/richardgirges/express-fileupload/issues/14
// firefox uploads empty file in case of cache miss when f5ing page.
// resulting in unexpected behavior. if there is no file data, the file is invalid.
// if (!fileUploadOptions.useTempFiles && !options.buffer.length) return;
// Create and return file object.
return {
name: options.name,
data: options.buffer,
size: options.size,
encoding: options.encoding,
tempFilePath: options.tempFilePath,
truncated: options.truncated,
mimetype: options.mimetype,
md5: options.hash,
mv: (filePath, callback) => {
// Define a propper move function.
const moveFunc = fileUploadOptions.useTempFiles
? moveFromTemp(filePath, options, fileUploadOptions)
: moveFromBuffer(filePath, options, fileUploadOptions);
// Create a folder for a file.
checkAndMakeDir(fileUploadOptions, filePath);
// If callback is passed in, use the callback API, otherwise return a promise.
return isFunc(callback) ? moveFunc(callback) : new Promise(moveFunc);
}
};
};

39
node_modules/express-fileupload/lib/index.js generated vendored Normal file
View file

@ -0,0 +1,39 @@
'use strict';
const path = require('path');
const processMultipart = require('./processMultipart');
const isEligibleRequest = require('./isEligibleRequest');
const { buildOptions, debugLog } = require('./utilities');
const busboy = require('busboy'); // eslint-disable-line no-unused-vars
const DEFAULT_OPTIONS = {
debug: false,
uploadTimeout: 60000,
fileHandler: false,
uriDecodeFileNames: false,
safeFileNames: false,
preserveExtension: false,
abortOnLimit: false,
responseOnLimit: 'File size limit has been reached',
limitHandler: false,
createParentPath: false,
parseNested: false,
useTempFiles: false,
tempFileDir: path.join(process.cwd(), 'tmp')
};
/**
* Expose the file upload middleware
* @param {DEFAULT_OPTIONS & busboy.BusboyConfig} options - Middleware options.
* @returns {Function} - express-fileupload middleware.
*/
module.exports = (options) => {
const uploadOptions = buildOptions(DEFAULT_OPTIONS, options);
return (req, res, next) => {
if (!isEligibleRequest(req)) {
debugLog(uploadOptions, 'Request is not eligible for file upload!');
return next();
}
processMultipart(uploadOptions, req, res, next);
};
};

View file

@ -0,0 +1,40 @@
const ACCEPTABLE_CONTENT_TYPE = /^multipart\/[\w'"()+-_?/:=,.]+(?:; ?[\w'"()+-_?/:=,.]*)+$/i;
const UNACCEPTABLE_METHODS = new Set(['GET', 'HEAD', 'DELETE', 'OPTIONS', 'CONNECT', 'TRACE']);
/**
* Ensures the request contains a content body
* @param {Object} req Express req object
* @returns {Boolean}
*/
const hasBody = (req) => {
return ('transfer-encoding' in req.headers) ||
('content-length' in req.headers && req.headers['content-length'] !== '0');
};
/**
* Ensures the request is not using a non-compliant multipart method
* such as GET or HEAD
* @param {Object} req Express req object
* @returns {Boolean}
*/
const hasAcceptableMethod = (req) => !UNACCEPTABLE_METHODS.has(req.method);
/**
* Ensures that only multipart requests are processed by express-fileupload
* ACCEPTABLE_CONTENT_TYPE REgex is based on the RFC 2046
* Validates special characters according to RFC 2046, section 5.1.1: '"()+_-=?/:
* Also checks for the presence of boundary in the header.
* @param {Object} req Express req object
* @returns {Boolean}
*/
const hasAcceptableContentType = (req) => {
const contType = req.headers['content-type'];
return contType.includes('boundary=') && ACCEPTABLE_CONTENT_TYPE.test(contType);
};
/**
* Ensures that the request in question is eligible for file uploads
* @param {Object} req Express req object
* @returns {Boolean}
*/
module.exports = (req) => hasBody(req) && hasAcceptableMethod(req) && hasAcceptableContentType(req);

42
node_modules/express-fileupload/lib/memHandler.js generated vendored Normal file
View file

@ -0,0 +1,42 @@
const crypto = require('crypto');
const { debugLog } = require('./utilities');
/**
* memHandler - In memory upload handler
* @param {Object} options
* @param {String} fieldname
* @param {String} filename
* @returns {Object}
*/
module.exports = (options, fieldname, filename) => {
const buffers = [];
const hash = crypto.createHash('md5');
let fileSize = 0;
let completed = false;
const getBuffer = () => Buffer.concat(buffers, fileSize);
return {
dataHandler: (data) => {
if (completed === true) {
debugLog(options, `Error: got ${fieldname}->${filename} data chunk for completed upload!`);
return;
}
buffers.push(data);
hash.update(data);
fileSize += data.length;
debugLog(options, `Uploading ${fieldname}->${filename}, bytes:${fileSize}...`);
},
getBuffer: getBuffer,
getFilePath: () => '',
getFileSize: () => fileSize,
getHash: () => hash.digest('hex'),
complete: () => {
debugLog(options, `Upload ${fieldname}->${filename} completed, bytes:${fileSize}.`);
completed = true;
return getBuffer();
},
cleanup: () => { completed = true; },
getWritePromise: () => Promise.resolve()
};
};

185
node_modules/express-fileupload/lib/processMultipart.js generated vendored Normal file
View file

@ -0,0 +1,185 @@
const Busboy = require('busboy');
const UploadTimer = require('./uploadtimer');
const fileFactory = require('./fileFactory');
const memHandler = require('./memHandler');
const tempFileHandler = require('./tempFileHandler');
const processNested = require('./processNested');
const {
isFunc,
debugLog,
buildFields,
buildOptions,
parseFileName
} = require('./utilities');
const waitFlushProperty = Symbol('wait flush property symbol');
/**
* Processes multipart request
* Builds a req.body object for fields
* Builds a req.files object for files
* @param {Object} options expressFileupload and Busboy options
* @param {Object} req Express request object
* @param {Object} res Express response object
* @param {Function} next Express next method
* @return {void}
*/
module.exports = (options, req, res, next) => {
req.files = null;
// Build busboy options and init busboy instance.
const busboyOptions = buildOptions(options, { headers: req.headers });
const busboy = Busboy(busboyOptions);
/**
* Closes connection with specified reason and http code.
* @param {number} code HTTP response code, default: 400.
* @param {*} reason Reason to close connection, default: 'Bad Request'.
*/
const closeConnection = (code, reason) => {
req.unpipe(busboy);
req.resume();
if (res.headersSent) {
debugLog(options, 'Headers already sent, can\'t close connection.');
return;
}
const resCode = code || 400;
const resReason = reason || 'Bad Request';
debugLog(options, `Closing connection with ${resCode}: ${resReason}`);
res.writeHead(resCode, { Connection: 'close' });
res.end(resReason);
};
// Express proxies sometimes attach multipart data to a buffer
if (req.body instanceof Buffer) {
req.body = Object.create(null);
}
// Build multipart req.body fields
busboy.on('field', (field, val) => req.body = buildFields(req.body, field, val));
// Build req.files fields
busboy.on('file', (field, file, info) => {
// Parse file name(cutting huge names, decoding, etc..).
const {filename:name, encoding, mimeType: mime} = info;
const filename = parseFileName(options, name);
// Define methods and handlers for upload process.
const {
dataHandler,
getFilePath,
getFileSize,
getHash,
complete,
cleanup,
getWritePromise
} = options.useTempFiles
? tempFileHandler(options, field, filename) // Upload into temporary file.
: memHandler(options, field, filename); // Upload into RAM.
const writePromise = options.useTempFiles
? getWritePromise().catch(err => {
req.unpipe(busboy);
req.resume();
cleanup();
next(err);
}) : getWritePromise();
// Define upload timer.
const uploadTimer = new UploadTimer(options.uploadTimeout, () => {
file.removeAllListeners('data');
file.resume();
// After destroy an error event will be emitted and file clean up will be done.
// In some cases file.destroy() doesn't exist, so we need to check this, see issue:
// https://github.com/richardgirges/express-fileupload/issues/259.
const err = new Error(`Upload timeout for ${field}->${filename}, bytes:${getFileSize()}`);
return isFunc(file.destroy) ? file.destroy(err) : file.emit('error', err);
});
file.on('limit', () => {
debugLog(options, `Size limit reached for ${field}->${filename}, bytes:${getFileSize()}`);
// Reset upload timer in case of file limit reached.
uploadTimer.clear();
// Run a user defined limit handler if it has been set.
if (isFunc(options.limitHandler)) {
options.limitHandler(req, res, next);
}
// Close connection with 413 code and do cleanup if abortOnLimit set(default: false).
if (options.abortOnLimit) {
debugLog(options, `Aborting upload because of size limit ${field}->${filename}.`);
closeConnection(413, options.responseOnLimit);
cleanup();
}
});
file.on('data', (data) => {
uploadTimer.set(); // Refresh upload timer each time new data chunk came.
dataHandler(data); // Handle new piece of data.
});
file.on('end', () => {
const size = getFileSize();
// Debug logging for file upload ending.
debugLog(options, `Upload finished ${field}->${filename}, bytes:${size}`);
// Reset upload timer in case of end event.
uploadTimer.clear();
// See https://github.com/richardgirges/express-fileupload/issues/191
// Do not add file instance to the req.files if original name and size are empty.
// Empty name and zero size indicates empty file field in the posted form.
if (!name && size === 0) {
if (options.useTempFiles) {
cleanup();
debugLog(options, `Removing the empty file ${field}->${filename}`);
}
return debugLog(options, `Don't add file instance if original name and size are empty`);
}
req.files = buildFields(req.files, field, fileFactory({
buffer: complete(),
name: filename,
tempFilePath: getFilePath(),
hash: getHash(),
size,
encoding,
truncated: file.truncated,
mimetype: mime
}, options));
if (!req[waitFlushProperty]) {
req[waitFlushProperty] = [];
}
req[waitFlushProperty].push(writePromise);
});
file.on('error', (err) => {
uploadTimer.clear(); // Reset upload timer in case of errors.
debugLog(options, err);
cleanup();
next();
});
// Debug logging for a new file upload.
debugLog(options, `New upload started ${field}->${filename}, bytes:${getFileSize()}`);
// Set new upload timeout for a new file.
uploadTimer.set();
});
busboy.on('finish', () => {
debugLog(options, `Busboy finished parsing request.`);
if (options.parseNested) {
req.body = processNested(req.body);
req.files = processNested(req.files);
}
if (!req[waitFlushProperty]) return next();
Promise.all(req[waitFlushProperty])
.then(() => {
delete req[waitFlushProperty];
next();
});
});
busboy.on('error', (err) => {
debugLog(options, `Busboy error`);
next(err);
});
req.pipe(busboy);
};

35
node_modules/express-fileupload/lib/processNested.js generated vendored Normal file
View file

@ -0,0 +1,35 @@
const { isSafeFromPollution } = require("./utilities");
module.exports = function(data){
if (!data || data.length < 1) return Object.create(null);
let d = Object.create(null),
keys = Object.keys(data);
for (let i = 0; i < keys.length; i++) {
let key = keys[i],
value = data[key],
current = d,
keyParts = key
.replace(new RegExp(/\[/g), '.')
.replace(new RegExp(/\]/g), '')
.split('.');
for (let index = 0; index < keyParts.length; index++){
let k = keyParts[index];
// Ensure we don't allow prototype pollution
if (!isSafeFromPollution(current, k)) {
continue;
}
if (index >= keyParts.length - 1){
current[k] = value;
} else {
if (!current[k]) current[k] = !isNaN(keyParts[index + 1]) ? [] : Object.create(null);
current = current[k];
}
}
}
return d;
};

64
node_modules/express-fileupload/lib/tempFileHandler.js generated vendored Normal file
View file

@ -0,0 +1,64 @@
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const {
debugLog,
checkAndMakeDir,
getTempFilename,
deleteFile
} = require('./utilities');
module.exports = (options, fieldname, filename) => {
const dir = path.normalize(options.tempFileDir);
const tempFilePath = path.join(dir, getTempFilename());
checkAndMakeDir({ createParentPath: true }, tempFilePath);
debugLog(options, `Temporary file path is ${tempFilePath}`);
const hash = crypto.createHash('md5');
let fileSize = 0;
let completed = false;
debugLog(options, `Opening write stream for ${fieldname}->${filename}...`);
const writeStream = fs.createWriteStream(tempFilePath);
const writePromise = new Promise((resolve, reject) => {
writeStream.on('finish', () => resolve());
writeStream.on('error', (err) => {
debugLog(options, `Error write temp file: ${err}`);
reject(err);
});
});
return {
dataHandler: (data) => {
if (completed === true) {
debugLog(options, `Error: got ${fieldname}->${filename} data chunk for completed upload!`);
return;
}
writeStream.write(data);
hash.update(data);
fileSize += data.length;
debugLog(options, `Uploading ${fieldname}->${filename}, bytes:${fileSize}...`);
},
getFilePath: () => tempFilePath,
getFileSize: () => fileSize,
getHash: () => hash.digest('hex'),
complete: () => {
completed = true;
debugLog(options, `Upload ${fieldname}->${filename} completed, bytes:${fileSize}.`);
if (writeStream !== false) writeStream.end();
// Return empty buff since data was uploaded into a temp file.
return Buffer.concat([]);
},
cleanup: () => {
completed = true;
debugLog(options, `Cleaning up temporary file ${tempFilePath}...`);
writeStream.end();
deleteFile(tempFilePath, err => (err
? debugLog(options, `Cleaning up temporary file ${tempFilePath} failed: ${err}`)
: debugLog(options, `Cleaning up temporary file ${tempFilePath} done.`)
));
},
getWritePromise: () => writePromise
};
};

26
node_modules/express-fileupload/lib/uploadtimer.js generated vendored Normal file
View file

@ -0,0 +1,26 @@
class UploadTimer {
/**
* @constructor
* @param {number} timeout - timer timeout in msecs.
* @param {Function} callback - callback to run when timeout reached.
*/
constructor(timeout = 0, callback = () => {}) {
this.timeout = timeout;
this.callback = callback;
this.timer = null;
}
clear() {
clearTimeout(this.timer);
}
set() {
// Do not start a timer if zero timeout or it hasn't been set.
if (!this.timeout) return false;
this.clear();
this.timer = setTimeout(this.callback, this.timeout);
return true;
}
}
module.exports = UploadTimer;

335
node_modules/express-fileupload/lib/utilities.js generated vendored Normal file
View file

@ -0,0 +1,335 @@
'use strict';
const fs = require('fs');
const path = require('path');
const { Readable } = require('stream');
// Parameters for safe file name parsing.
const SAFE_FILE_NAME_REGEX = /[^\w-]/g;
const MAX_EXTENSION_LENGTH = 3;
// Parameters to generate unique temporary file names:
const TEMP_COUNTER_MAX = 65536;
const TEMP_PREFIX = 'tmp';
let tempCounter = 0;
/**
* Logs message to console if debug option set to true.
* @param {Object} options - options object.
* @param {string} msg - message to log.
* @returns {boolean} - false if debug is off.
*/
const debugLog = (options, msg) => {
const opts = options || {};
if (!opts.debug) return false;
console.log(`Express-file-upload: ${msg}`); // eslint-disable-line
return true;
};
/**
* Generates unique temporary file name. e.g. tmp-5000-156788789789.
* @param {string} prefix - a prefix for generated unique file name.
* @returns {string}
*/
const getTempFilename = (prefix = TEMP_PREFIX) => {
tempCounter = tempCounter >= TEMP_COUNTER_MAX ? 1 : tempCounter + 1;
return `${prefix}-${tempCounter}-${Date.now()}`;
};
/**
* isFunc: Checks if argument is a function.
* @returns {boolean} - Returns true if argument is a function.
*/
const isFunc = func => func && func.constructor && func.call && func.apply ? true: false;
/**
* Set errorFunc to the same value as successFunc for callback mode.
* @returns {Function}
*/
const errorFunc = (resolve, reject) => isFunc(reject) ? reject : resolve;
/**
* Return a callback function for promise resole/reject args.
* Ensures that callback is called only once.
* @returns {Function}
*/
const promiseCallback = (resolve, reject) => {
let hasFired = false;
return (err) => {
if (hasFired) {
return;
}
hasFired = true;
return err ? errorFunc(resolve, reject)(err) : resolve();
};
};
/**
* Builds instance options from arguments objects(can't be arrow function).
* @returns {Object} - result options.
*/
const buildOptions = function() {
const result = {};
[...arguments].forEach(options => {
if (!options || typeof options !== 'object') return;
Object.keys(options).forEach(i => result[i] = options[i]);
});
return result;
};
// The default prototypes for both objects and arrays.
// Used by isSafeFromPollution
const OBJECT_PROTOTYPE_KEYS = Object.getOwnPropertyNames(Object.prototype);
const ARRAY_PROTOTYPE_KEYS = Object.getOwnPropertyNames(Array.prototype);
/**
* Determines whether a key insertion into an object could result in a prototype pollution
* @param {Object} base - The object whose insertion we are checking
* @param {string} key - The key that will be inserted
*/
const isSafeFromPollution = (base, key) => {
// We perform an instanceof check instead of Array.isArray as the former is more
// permissive for cases in which the object as an Array prototype but was not constructed
// via an Array constructor or literal.
const TOUCHES_ARRAY_PROTOTYPE = (base instanceof Array) && ARRAY_PROTOTYPE_KEYS.includes(key);
const TOUCHES_OBJECT_PROTOTYPE = OBJECT_PROTOTYPE_KEYS.includes(key);
return !TOUCHES_ARRAY_PROTOTYPE && !TOUCHES_OBJECT_PROTOTYPE;
};
/**
* Builds request fields (using to build req.body and req.files)
* @param {Object} instance - request object.
* @param {string} field - field name.
* @param {any} value - field value.
* @returns {Object}
*/
const buildFields = (instance, field, value) => {
// Do nothing if value is not set.
if (value === null || value === undefined) return instance;
instance = instance || Object.create(null);
if (!isSafeFromPollution(instance, field)) {
return instance;
}
// Non-array fields
if (!instance[field]) {
instance[field] = value;
return instance;
}
// Array fields
if (instance[field] instanceof Array) {
instance[field].push(value);
} else {
instance[field] = [instance[field], value];
}
return instance;
};
/**
* Creates a folder for file specified in the path variable
* @param {Object} fileUploadOptions
* @param {string} filePath
* @returns {boolean}
*/
const checkAndMakeDir = (fileUploadOptions, filePath) => {
// Check upload options were set.
if (!fileUploadOptions) return false;
if (!fileUploadOptions.createParentPath) return false;
// Check whether folder for the file exists.
if (!filePath) return false;
const parentPath = path.dirname(filePath);
// Create folder if it doesn't exist.
if (!fs.existsSync(parentPath)) fs.mkdirSync(parentPath, { recursive: true });
// Checks folder again and return a results.
return fs.existsSync(parentPath);
};
/**
* Deletes a file.
* @param {string} file - Path to the file to delete.
* @param {Function} callback
*/
const deleteFile = (file, callback) => fs.unlink(file, callback);
/**
* Copy file via streams
* @param {string} src - Path to the source file
* @param {string} dst - Path to the destination file.
*/
const copyFile = (src, dst, callback) => {
// cbCalled flag and runCb helps to run cb only once.
let cbCalled = false;
let runCb = (err) => {
if (cbCalled) return;
cbCalled = true;
callback(err);
};
// Create read stream
let readable = fs.createReadStream(src);
readable.on('error', runCb);
// Create write stream
let writable = fs.createWriteStream(dst);
writable.on('error', (err)=>{
readable.destroy();
runCb(err);
});
writable.on('close', () => runCb());
// Copy file via piping streams.
readable.pipe(writable);
};
/**
* moveFile: moves the file from src to dst.
* Firstly trying to rename the file if no luck copying it to dst and then deleteing src.
* @param {string} src - Path to the source file
* @param {string} dst - Path to the destination file.
* @param {Function} callback - A callback function with renamed flag.
*/
const moveFile = (src, dst, callback) => fs.rename(src, dst, (err) => {
if (err) {
// Try to copy file if rename didn't work.
copyFile(src, dst, (cpErr) => (cpErr ? callback(cpErr) : deleteFile(src, callback)));
return;
}
// File was renamed successfully: Add true to the callback to indicate that.
callback(null, true);
});
/**
* Save buffer data to a file.
* @param {Buffer} buffer - buffer to save to a file.
* @param {string} filePath - path to a file.
*/
const saveBufferToFile = (buffer, filePath, callback) => {
if (!Buffer.isBuffer(buffer)) {
return callback(new Error('buffer variable should be type of Buffer!'));
}
// Setup readable stream from buffer.
let streamData = buffer;
let readStream = Readable();
readStream._read = () => {
readStream.push(streamData);
streamData = null;
};
// Setup file system writable stream.
let fstream = fs.createWriteStream(filePath);
// console.log("Calling saveBuffer");
fstream.on('error', err => {
// console.log("err cb")
callback(err);
});
fstream.on('close', () => {
// console.log("close cb");
callback();
});
// Copy file via piping streams.
readStream.pipe(fstream);
};
/**
* Decodes uriEncoded file names.
* @param {Object} opts - middleware options.
* @param fileName {String} - file name to decode.
* @returns {String}
*/
const uriDecodeFileName = (opts, fileName) => {
if (!opts || !opts.uriDecodeFileNames) {
return fileName;
}
// Decode file name from URI with checking URI malformed errors.
// See Issue https://github.com/richardgirges/express-fileupload/issues/342.
try {
return decodeURIComponent(fileName);
} catch (err) {
const matcher = /(%[a-f0-9]{2})/gi;
return fileName.split(matcher)
.map((str) => {
try {
return decodeURIComponent(str);
} catch (err) {
return '';
}
})
.join('');
}
};
/**
* Parses filename and extension and returns object {name, extension}.
* @param {boolean|integer} preserveExtension - true/false or number of characters for extension.
* @param {string} fileName - file name to parse.
* @returns {Object} - { name, extension }.
*/
const parseFileNameExtension = (preserveExtension, fileName) => {
const preserveExtensionLength = parseInt(preserveExtension);
const result = {name: fileName, extension: ''};
if (!preserveExtension && preserveExtensionLength !== 0) return result;
// Define maximum extension length
const maxExtLength = isNaN(preserveExtensionLength)
? MAX_EXTENSION_LENGTH
: Math.abs(preserveExtensionLength);
const nameParts = fileName.split('.');
if (nameParts.length < 2) return result;
let extension = nameParts.pop();
if (
extension.length > maxExtLength &&
maxExtLength > 0
) {
nameParts[nameParts.length - 1] +=
'.' +
extension.substr(0, extension.length - maxExtLength);
extension = extension.substr(-maxExtLength);
}
result.extension = maxExtLength ? extension : '';
result.name = nameParts.join('.');
return result;
};
/**
* Parse file name and extension.
* @param {Object} opts - middleware options.
* @param {string} fileName - Uploaded file name.
* @returns {string}
*/
const parseFileName = (opts, fileName) => {
// Check fileName argument
if (!fileName || typeof fileName !== 'string') return getTempFilename();
// Cut off file name if it's lenght more then 255.
let parsedName = fileName.length <= 255 ? fileName : fileName.substr(0, 255);
// Decode file name if uriDecodeFileNames option set true.
parsedName = uriDecodeFileName(opts, parsedName);
// Stop parsing file name if safeFileNames options hasn't been set.
if (!opts.safeFileNames) return parsedName;
// Set regular expression for the file name.
const nameRegex = typeof opts.safeFileNames === 'object' && opts.safeFileNames instanceof RegExp
? opts.safeFileNames
: SAFE_FILE_NAME_REGEX;
// Parse file name extension.
let {name, extension} = parseFileNameExtension(opts.preserveExtension, parsedName);
if (extension.length) extension = '.' + extension.replace(nameRegex, '');
return name.replace(nameRegex, '').concat(extension);
};
module.exports = {
isFunc,
debugLog,
copyFile, // For testing purpose.
moveFile,
errorFunc,
deleteFile, // For testing purpose.
buildFields,
buildOptions,
parseFileName,
getTempFilename,
promiseCallback,
checkAndMakeDir,
saveBufferToFile,
uriDecodeFileName,
isSafeFromPollution
};

44
node_modules/express-fileupload/package.json generated vendored Normal file
View file

@ -0,0 +1,44 @@
{
"name": "express-fileupload",
"version": "1.4.2",
"author": "Richard Girges <richardgirges@gmail.com>",
"description": "Simple express file upload middleware that wraps around Busboy",
"main": "./lib/index",
"scripts": {
"pretest": "node ./test/pretests.js",
"posttest": "node ./test/posttests.js",
"test": "nyc --reporter=html --reporter=text mocha -- -R spec",
"lint": "eslint ./",
"lint:fix": "eslint --fix ./",
"coveralls": "nyc report --reporter=text-lcov | coveralls"
},
"dependencies": {
"busboy": "^1.6.0"
},
"engines": {
"node": ">=12.0.0"
},
"keywords": [
"express",
"file-upload",
"upload",
"forms",
"multipart",
"files",
"busboy",
"middleware"
],
"license": "MIT",
"repository": "richardgirges/express-fileupload",
"devDependencies": {
"coveralls": "^3.1.1",
"eslint": "^7.31.0",
"express": "^4.18.1",
"md5": "^2.3.0",
"mocha": "^10.0.0",
"nyc": "^15.1.0",
"rimraf": "^3.0.2",
"rnd-file": "^0.0.1",
"supertest": "^6.1.5"
}
}

View file

@ -0,0 +1,78 @@
'use strict';
const fs = require('fs');
const md5 = require('md5');
const path = require('path');
const assert = require('assert');
const server = require('./server');
const {isFunc} = require('../lib/utilities');
const fileFactory = require('../lib/fileFactory');
const mockFileName = 'basketball.png';
const mockFile = path.join(server.fileDir, mockFileName);
const mockBuffer = fs.readFileSync(mockFile);
const mockMd5 = md5(mockBuffer);
const mockFileOpts = {
name: mockFileName,
buffer: mockBuffer,
encoding: 'utf-8',
mimetype: 'image/png',
hash: mockMd5,
tempFilePath: mockFile
};
describe('fileFactory: Test of the fileFactory factory', function() {
beforeEach(() => server.clearUploadsDir());
it('return a file object', () => assert.ok(fileFactory(mockFileOpts)));
describe('Properties', function() {
it('contains the name property', () => {
assert.equal(fileFactory(mockFileOpts).name, mockFileName);
});
it('contains the data property', () => assert.ok(fileFactory(mockFileOpts).data));
it('contains the encoding property', () => {
assert.equal(fileFactory(mockFileOpts).encoding, 'utf-8');
});
it('contains the mimetype property', () => {
assert.equal(fileFactory(mockFileOpts).mimetype, 'image/png');
});
it('contains the md5 property', () => assert.equal(fileFactory(mockFileOpts).md5, mockMd5));
it('contains the mv method', () => assert.equal(isFunc(fileFactory(mockFileOpts).mv), true));
});
describe('File object behavior for in memory upload', function() {
const file = fileFactory(mockFileOpts);
it('move the file to the specified folder', (done) => {
file.mv(path.join(server.uploadDir, mockFileName), (err) => {
assert.ifError(err);
done();
});
});
it('reject the mv if the destination does not exists', (done) => {
file.mv(path.join(server.uploadDir, 'unknown', mockFileName), (err) => {
assert.ok(err);
done();
});
});
});
describe('File object behavior for upload into temporary file', function() {
const file = fileFactory(mockFileOpts, { useTempFiles: true });
it('move the file to the specified folder', (done) => {
file.mv(path.join(server.uploadDir, mockFileName), (err) => {
assert.ifError(err);
// Place back moved file.
fs.renameSync(path.join(server.uploadDir, mockFileName), mockFile);
done();
});
});
it('reject the mv if the destination does not exists', (done) => {
file.mv(path.join(server.uploadDir, 'unknown', mockFileName), (err) => {
assert.ok(err);
done();
});
});
});
});

View file

@ -0,0 +1,99 @@
'use strict';
const path = require('path');
const request = require('supertest');
const assert = require('assert');
const server = require('./server');
const clearUploadsDir = server.clearUploadsDir;
const fileDir = server.fileDir;
describe('fileLimitUloads: Test Single File Upload With File Size Limit', function() {
let app, limitHandlerRun;
beforeEach(function() {
clearUploadsDir();
});
describe('abort connection on limit reached', function() {
before(function() {
app = server.setup({
limits: {fileSize: 200 * 1024}, // set 200kb upload limit
abortOnLimit: true
});
});
it(`upload 'basketball.png' (~154kb) with 200kb size limit`, function(done) {
let filePath = path.join(fileDir, 'basketball.png');
request(app)
.post('/upload/single/truncated')
.attach('testFile', filePath)
.expect(200)
.end(done);
});
it(`fail when uploading 'car.png' (~269kb) with 200kb size limit`, function(done) {
let filePath = path.join(fileDir, 'car.png');
request(app)
.post('/upload/single/truncated')
.attach('testFile', filePath)
.expect(413)
.end((err) => {
// err.code === 'ECONNRESET' that means upload has been aborted.
done(err && err.code !== 'ECONNRESET' ? err : null);
});
});
});
describe('Run limitHandler on limit reached.', function(){
before(function() {
app = server.setup({
limits: {fileSize: 200 * 1024}, // set 200kb upload limit
limitHandler: (req, res) => { // set limit handler
res.writeHead(500, { Connection: 'close', 'Content-Type': 'application/json'});
res.end(JSON.stringify({response: 'Limit reached!'}));
limitHandlerRun = true;
}
});
});
it(`Run limit handler when uploading 'car.png' (~269kb) with 200kb size limit`, function(done) {
let filePath = path.join(fileDir, 'car.png');
limitHandlerRun = false;
request(app)
.post('/upload/single/truncated')
.attach('testFile', filePath)
.expect(500, {response: 'Limit reached!'})
.end(function(err) {
// err.code === 'ECONNRESET' that means upload has been aborted.
if (err && err.code !== 'ECONNRESET') return done(err);
if (!limitHandlerRun) return done('handler did not run');
done();
});
});
});
describe('pass truncated file to the next handler', function() {
before(function() {
app = server.setup({
limits: {fileSize: 200 * 1024} // set 200kb upload limit
});
});
it(`fail when uploading 'car.png' (~269kb) with 200kb size limit`, function(done) {
let filePath = path.join(fileDir, 'car.png');
request(app)
.post('/upload/single/truncated')
.attach('testFile', filePath)
.expect(400)
.end(function(err, res) {
assert.ok(res.error.text === 'File too big');
done();
});
});
});
});

View file

@ -0,0 +1,116 @@
'use strict';
const assert = require('assert');
const isEligibleRequest = require('../lib/isEligibleRequest');
describe('isEligibleRequest function tests', () => {
it('should return true if the request method is POST & headers set', () => {
const req = {
method: 'POST',
headers: {
'content-length': '768751',
'content-type': 'multipart/form-data; boundary=----WebKitFormBoundaryz2D47BnVMA7w5N36'
}
};
const result = isEligibleRequest(req);
assert.equal(result, true);
});
it('should return true if the request method is PUT & headers set', () => {
const req = {
method: 'PUT',
headers: {
'content-length': '768751',
'content-type': 'multipart/form-data; boundary=----WebKitFormBoundaryz2D47BnVMA7w5N36'
}
};
const result = isEligibleRequest(req);
assert.equal(result, true);
});
it('should return true if the request method is PATCH & headers set', () => {
const req = {
method: 'PATCH',
headers: {
'content-length': '768751',
'content-type': 'multipart/form-data; boundary=----WebKitFormBoundaryz2D47BnVMA7w5N36'
}
};
const result = isEligibleRequest(req);
assert.equal(result, true);
});
it('should return false if the request method is POST & content length 0', () => {
const req = {
method: 'POST',
headers: {
'content-length': '0',
'content-type': 'multipart/form-data; boundary=----WebKitFormBoundaryz2D47BnVMA7w5N36'
}
};
const result = isEligibleRequest(req);
assert.equal(result, false);
});
it('should return false if the request method is POST & no content-length', () => {
const req = {
method: 'POST',
headers: {
'content-type': 'multipart/form-data; boundary=----WebKitFormBoundaryz2D47BnVMA7w5N36'
}
};
const result = isEligibleRequest(req);
assert.equal(result, false);
});
it('should return false if the request method is POST & no boundary', () => {
const req = {
method: 'POST',
headers: {
'content-length': '768751',
'content-type': 'multipart/form-data; ----WebKitFormBoundaryz2D47BnVMA7w5N36'
}
};
const result = isEligibleRequest(req);
assert.equal(result, false);
});
it('should return false if the request method is not POST, PUT or PATCH', () => {
const req = {
method: 'GET',
headers: {
'content-length': '768751',
'content-type': 'multipart/form-data; boundary=----WebKitFormBoundaryz2D47BnVMA7w5N36'
}
};
const result = isEligibleRequest(req);
assert.equal(result, false);
});
it('should return false if the request method is not POST, PUT or PATCH', () => {
const req = {
method: 'DELETE',
headers: {
'content-length': '768751',
'content-type': 'multipart/form-data; boundary=----WebKitFormBoundaryz2D47BnVMA7w5N36'
}
};
const result = isEligibleRequest(req);
assert.equal(result, false);
});
it('should return false if the request method is not POST, PUT or PATCH', () => {
const req = {
method: 'OPTIONS',
headers: {
'content-length': '768751',
'content-type': 'multipart/form-data; boundary=----WebKitFormBoundaryz2D47BnVMA7w5N36'
}
};
const result = isEligibleRequest(req);
assert.equal(result, false);
});
it('should return false if the request method is not POST, PUT or PATCH', () => {
const req = {
method: 'HEAD',
headers: {
'content-length': '768751',
'content-type': 'multipart/form-data; boundary=----WebKitFormBoundaryz2D47BnVMA7w5N36'
}
};
const result = isEligibleRequest(req);
assert.equal(result, false);
});
});

View file

@ -0,0 +1,85 @@
'use strict';
const request = require('supertest');
const server = require('./server');
const app = server.setup();
let mockUser = {
firstName: 'Joe',
lastName: 'Schmo',
email: 'joe@mailinator.com'
};
let mockCars = [
'rsx',
'tsx',
'civic',
'integra'
];
describe('multipartFields: Test Multipart Form Single Field Submissions', function() {
it('submit multipart user data with POST', function(done) {
request(app)
.post('/fields/user')
.field('firstName', mockUser.firstName)
.field('lastName', mockUser.lastName)
.field('email', mockUser.email)
.expect('Content-Type', /json/)
.expect(200, {
firstName: mockUser.firstName,
lastName: mockUser.lastName,
email: mockUser.email
}, done);
});
it('submit multipart user data with PUT', function(done) {
request(app)
.post('/fields/user')
.field('firstName', mockUser.firstName)
.field('lastName', mockUser.lastName)
.field('email', mockUser.email)
.expect('Content-Type', /json/)
.expect(200, {
firstName: mockUser.firstName,
lastName: mockUser.lastName,
email: mockUser.email
}, done);
});
it('fail when user data submitted without multipart', function(done) {
request(app)
.post('/fields/user')
.send(mockUser)
.expect(400)
.end(done);
});
it('fail when user data not submitted', function(done) {
request(app)
.post('/fields/user')
.expect(400)
.end(done);
});
});
describe('multipartFields: Test Multipart Form Array Field Submissions', function() {
it('submit array of data with POST', function(done) {
let req = request(app).post('/fields/array');
for (let i = 0; i < mockCars.length; i++) {
req.field('testField', mockCars[i]);
}
req
.expect(200)
.end(function(err, res) {
if (err) {
return done(err);
}
let responseMatchesRequest = res.body.join(',') === mockCars.join(',');
done(responseMatchesRequest ? null : 'Data was returned as expected.');
});
});
});

View file

@ -0,0 +1,475 @@
'use strict';
const fs = require('fs');
const md5 = require('md5');
const path = require('path');
const request = require('supertest');
const server = require('./server');
const fileDir = server.fileDir;
const tempDir = server.tempDir;
const uploadDir = server.uploadDir;
const clearTempDir = server.clearTempDir;
const clearUploadsDir = server.clearUploadsDir;
const mockFiles = ['car.png', 'tree.png', 'basketball.png', 'emptyfile.txt'];
const mockUser = {
firstName: 'Joe',
lastName: 'Schmo',
email: 'joe@mailinator.com'
};
// Reset response body.uploadDir/uploadPath for testing.
const resetBodyUploadData = (res) => {
res.body.uploadDir = '';
res.body.uploadPath = '';
};
const genUploadResult = (fileName, filePath) => {
const fileStat = fs.statSync(filePath);
const fileBuffer = fs.readFileSync(filePath);
return {
name: fileName,
md5: md5(fileBuffer),
size: fileStat.size,
uploadDir: '',
uploadPath: ''
};
};
describe('multipartUploads: Test Directory Cleaning Method', function() {
it('emptied "uploads" directory', function(done) {
clearUploadsDir();
const filesFound = fs.readdirSync(uploadDir).length;
done(filesFound ? `Directory not empty. Found ${filesFound} files.` : null);
});
});
describe('multipartUploads: Test Single File Upload', function() {
const app = server.setup();
mockFiles.forEach((fileName) => {
const filePath = path.join(fileDir, fileName);
const uploadedFilePath = path.join(uploadDir, fileName);
const result = genUploadResult(fileName, filePath);
it(`upload ${fileName} with POST`, function(done) {
clearUploadsDir();
request(app)
.post('/upload/single')
.attach('testFile', filePath)
.expect(resetBodyUploadData)
.expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
});
it(`upload ${fileName} with PUT`, function(done) {
clearUploadsDir();
request(app)
.post('/upload/single')
.attach('testFile', filePath)
.expect(resetBodyUploadData)
.expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
});
});
it('fail when no files were attached', function(done) {
request(app)
.post('/upload/single')
.expect(400)
.end(done);
});
it('fail when using GET', function(done) {
request(app)
.get('/upload/single')
.attach('testFile', path.join(fileDir, mockFiles[0]))
.expect(400)
.end((err) => {
// err.code === 'ECONNRESET' that means upload has been aborted.
done(err && err.code !== 'ECONNRESET' ? err : null);
});
});
it('fail when using HEAD', function(done) {
request(app)
.head('/upload/single')
.attach('testFile', path.join(fileDir, mockFiles[0]))
.expect(400)
.end((err) => {
// err.code === 'ECONNRESET' that means upload has been aborted.
done(err && err.code !== 'ECONNRESET' ? err : null);
});
});
});
describe('multipartUploads: Test Single File Upload w/ .mv()', function() {
const app = server.setup();
mockFiles.forEach((fileName) => {
const filePath = path.join(fileDir, fileName);
const uploadedFilePath = path.join(uploadDir, fileName);
const result = genUploadResult(fileName, filePath);
it(`upload ${fileName} with POST w/ .mv()`, function(done) {
clearUploadsDir();
request(app)
.post('/upload/single')
.attach('testFile', filePath)
.expect(resetBodyUploadData)
.expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
});
it(`upload ${fileName} with PUT w/ .mv()`, function(done) {
clearUploadsDir();
request(app)
.post('/upload/single')
.attach('testFile', filePath)
.expect(resetBodyUploadData)
.expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
});
});
});
describe('multipartUploads: Test Single File Upload w/ useTempFiles option.', function() {
const app = server.setup({ useTempFiles: true, tempFileDir: tempDir });
mockFiles.forEach((fileName) => {
const filePath = path.join(fileDir, fileName);
const uploadedFilePath = path.join(uploadDir, fileName);
const result = genUploadResult(fileName, filePath);
it(`upload ${fileName} with POST`, function(done) {
clearUploadsDir();
request(app)
.post('/upload/single')
.attach('testFile', filePath)
.expect(resetBodyUploadData)
.expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
});
it(`upload ${fileName} with PUT`, function(done) {
clearUploadsDir();
request(app)
.post('/upload/single')
.attach('testFile', filePath)
.expect(resetBodyUploadData)
.expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
});
});
it('fail when no files were attached', function(done) {
request(app)
.post('/upload/single')
.expect(400)
.end(done);
});
it('fail when using GET', function(done) {
request(app)
.get('/upload/single')
.attach('testFile', path.join(fileDir, mockFiles[0]))
.expect(400)
.end((err) => {
// err.code === 'ECONNRESET' that means upload has been aborted.
done(err && err.code !== 'ECONNRESET' ? err : null);
});
});
it('fail when using HEAD', function(done) {
request(app)
.head('/upload/single')
.attach('testFile', path.join(fileDir, mockFiles[0]))
.expect(400)
.end((err) => {
// err.code === 'ECONNRESET' that means upload has been aborted.
done(err && err.code !== 'ECONNRESET' ? err : null);
});
});
});
describe('multipartUploads: Single File Upload w/ useTempFiles & empty tempFileDir.', function() {
const app = server.setup({ useTempFiles: true, tempFileDir: '' });
mockFiles.forEach((fileName) => {
const filePath = path.join(fileDir, fileName);
const uploadedFilePath = path.join(uploadDir, fileName);
const result = genUploadResult(fileName, filePath);
it(`upload ${fileName} with POST`, function(done) {
clearUploadsDir();
request(app)
.post('/upload/single')
.attach('testFile', filePath)
.expect(resetBodyUploadData)
.expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
});
});
});
describe('multipartUploads: Test Single File Upload w/ .mv() Promise', function() {
const app = server.setup();
mockFiles.forEach((fileName) => {
const filePath = path.join(fileDir, fileName);
const uploadedFilePath = path.join(uploadDir, fileName);
const result = genUploadResult(fileName, filePath);
it(`upload ${fileName} with POST w/ .mv() Promise`, function(done) {
clearUploadsDir();
request(app)
.post('/upload/single/promise')
.attach('testFile', filePath)
.expect(resetBodyUploadData)
.expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
});
it(`upload ${fileName} with PUT w/ .mv() Promise`, function(done) {
clearUploadsDir();
request(app)
.post('/upload/single/promise')
.attach('testFile', filePath)
.expect(resetBodyUploadData)
.expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
});
});
it('fail when no files were attached', function(done) {
request(app)
.post('/upload/single')
.expect(400)
.end(done);
});
it('fail when using GET', function(done) {
request(app)
.get('/upload/single')
.attach('testFile', path.join(fileDir, mockFiles[0]))
.expect(400)
.end((err) => {
// err.code === 'ECONNRESET' that means upload has been aborted.
done(err && err.code !== 'ECONNRESET' ? err : null);
});
});
it('fail when using HEAD', function(done) {
request(app)
.head('/upload/single')
.attach('testFile', path.join(fileDir, mockFiles[0]))
.expect(400)
.end((err) => {
// err.code === 'ECONNRESET' that means upload has been aborted.
done(err && err.code !== 'ECONNRESET' ? err : null);
});
});
});
describe('multipartUploads: Test Single File Upload w/ .mv() Promise & useTempFiles', function() {
const app = server.setup({ useTempFiles: true, tempFileDir: tempDir });
mockFiles.forEach((fileName) => {
const filePath = path.join(fileDir, fileName);
const uploadedFilePath = path.join(uploadDir, fileName);
const result = genUploadResult(fileName, filePath);
it(`upload ${fileName} with POST w/ .mv() Promise`, function(done) {
clearUploadsDir();
request(app)
.post('/upload/single/promise')
.attach('testFile', filePath)
.expect(resetBodyUploadData)
.expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
});
it(`upload ${fileName} with PUT w/ .mv() Promise`, function(done) {
clearUploadsDir();
request(app)
.post('/upload/single/promise')
.attach('testFile', filePath)
.expect(resetBodyUploadData)
.expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
});
});
it('fail when no files were attached', (done) => {
request(app)
.post('/upload/single')
.expect(400)
.end(done);
});
it('fail when using GET', (done) => {
request(app)
.get('/upload/single')
.attach('testFile', path.join(fileDir, mockFiles[0]))
.expect(400)
.end((err) => {
// err.code === 'ECONNRESET' that means upload has been aborted.
done(err && err.code !== 'ECONNRESET' ? err : null);
});
});
it('fail when using HEAD', (done) => {
request(app)
.head('/upload/single')
.attach('testFile', path.join(fileDir, mockFiles[0]))
.expect(400)
.end((err) => {
// err.code === 'ECONNRESET' that means upload has been aborted.
done(err && err.code !== 'ECONNRESET' ? err : null);
});
});
});
describe('multipartUploads: Test Multi-File Upload', function() {
const app = server.setup();
it('upload multiple files with POST', (done) => {
clearUploadsDir();
const req = request(app).post('/upload/multiple');
const expectedResult = [];
const expectedResultSorted = [];
const uploadedFilesPath = [];
mockFiles.forEach((fileName, index) => {
const filePath = path.join(fileDir, fileName);
req.attach(`testFile${index + 1}`, filePath);
uploadedFilesPath.push(path.join(uploadDir, fileName));
expectedResult.push(genUploadResult(fileName, filePath));
});
req
.expect((res) => {
res.body.forEach((fileInfo) => {
fileInfo.uploadDir = '';
fileInfo.uploadPath = '';
const index = mockFiles.indexOf(fileInfo.name);
expectedResultSorted.push(expectedResult[index]);
});
})
.expect(200, expectedResultSorted)
.end((err) => {
if (err) return done(err);
fs.stat(uploadedFilesPath[0], (err) => {
if (err) return done(err);
fs.stat(uploadedFilesPath[1], (err) => {
if (err) return done(err);
fs.stat(uploadedFilesPath[2], done);
});
});
});
});
});
describe('multipartUploads: Test File Array Upload', function() {
const app = server.setup();
it('upload array of files with POST', (done) => {
clearUploadsDir();
const req = request(app).post('/upload/array');
const expectedResult = [];
const expectedResultSorted = [];
const uploadedFilesPath = [];
mockFiles.forEach((fileName) => {
const filePath = path.join(fileDir, fileName);
uploadedFilesPath.push(path.join(uploadDir, fileName));
expectedResult.push(genUploadResult(fileName, filePath));
req.attach('testFiles', filePath);
});
req
.expect((res)=>{
res.body.forEach((fileInfo) => {
fileInfo.uploadDir = '';
fileInfo.uploadPath = '';
const index = mockFiles.indexOf(fileInfo.name);
expectedResultSorted.push(expectedResult[index]);
});
})
.expect(200, expectedResultSorted)
.end((err) => {
if (err) return done(err);
uploadedFilesPath.forEach((uploadedFilePath) => {
fs.statSync(uploadedFilePath);
});
done();
});
});
});
describe('multipartUploads: Test Upload With Fields', function() {
const app = server.setup();
mockFiles.forEach((fileName) => {
const filePath = path.join(fileDir, fileName);
const uploadedFilePath = path.join(uploadDir, fileName);
// Expected results
const result = genUploadResult(fileName, filePath);
result.firstName = mockUser.firstName;
result.lastName = mockUser.lastName;
result.email = mockUser.email;
it(`upload ${fileName} and submit fields at the same time with POST`, function(done) {
clearUploadsDir();
request(app)
.post('/upload/single/withfields')
.attach('testFile', filePath)
.field('firstName', mockUser.firstName)
.field('lastName', mockUser.lastName)
.field('email', mockUser.email)
.expect(resetBodyUploadData)
.expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
});
it(`upload ${fileName} and submit fields at the same time with PUT`, function(done) {
clearUploadsDir();
request(app)
.put('/upload/single/withfields')
.attach('testFile', filePath)
.field('firstName', mockUser.firstName)
.field('lastName', mockUser.lastName)
.field('email', mockUser.email)
.expect(resetBodyUploadData)
.expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
});
});
});
describe('multipartUploads: Test Aborting/Canceling during upload', function() {
this.timeout(4000); // Set timeout for async tests.
const uploadTimeout = 1000;
const app = server.setup({
useTempFiles: true,
tempFileDir: tempDir,
debug: true,
uploadTimeout
});
clearTempDir();
clearUploadsDir();
mockFiles.forEach((fileName) => {
const filePath = path.join(fileDir, fileName);
it(`Delete temp file if ${fileName} upload was aborted`, (done) => {
const req = request(app)
.post('/upload/single')
.attach('testFile', filePath)
.on('progress', (e) => {
const progress = (e.loaded * 100) / e.total;
// Aborting request, use req.req since it is original superagent request.
if (progress > 50) req.req.abort();
})
.end((err) => {
if (!err) return done(`Connection hasn't been aborted!`);
if (err.code !== 'ECONNRESET') return done(err);
// err.code === 'ECONNRESET' that means upload has been aborted.
// Checking temp directory after upload timeout.
setTimeout(() => {
fs.readdir(tempDir, (err, files) => {
if (err) return done(err);
return files.length ? done(`Temporary directory contains files!`) : done();
});
}, uploadTimeout * 2);
});
});
});
});

219
node_modules/express-fileupload/test/options.spec.js generated vendored Normal file
View file

@ -0,0 +1,219 @@
const fs = require('fs');
const path = require('path');
const request = require('supertest');
const server = require('./server');
const clearUploadsDir = server.clearUploadsDir;
const fileDir = server.fileDir;
const uploadDir = server.uploadDir;
describe('options: File Upload Options Tests', function() {
afterEach(function(done) {
clearUploadsDir();
done();
});
/**
* Upload the file for testing and verify the expected filename.
* @param {object} options The expressFileUpload options.
* @param {string} actualFileNameToUpload The name of the file to upload.
* @param {string} expectedFileNameOnFileSystem The name of the file after upload.
* @param {function} done The mocha continuation function.
*/
function executeFileUploadTestWalk(options,
actualFileNameToUpload,
expectedFileNameOnFileSystem,
done) {
request(server.setup(options))
.post('/upload/single')
.attach('testFile', path.join(fileDir, actualFileNameToUpload))
.expect(200)
.end(function(err) {
if (err) {
return done(err);
}
const uploadedFilePath = path.join(uploadDir, expectedFileNameOnFileSystem);
fs.stat(uploadedFilePath, done);
});
}
describe('Testing [safeFileNames] option to ensure:', function() {
it('Does nothing to your filename when disabled.',
function(done) {
const fileUploadOptions = {safeFileNames: false};
const actualFileName = 'my$Invalid#fileName.png123';
const expectedFileName = 'my$Invalid#fileName.png123';
executeFileUploadTestWalk(fileUploadOptions, actualFileName, expectedFileName, done);
});
it('Is disabled by default.',
function(done) {
const fileUploadOptions = null;
const actualFileName = 'my$Invalid#fileName.png123';
const expectedFileName = 'my$Invalid#fileName.png123';
executeFileUploadTestWalk(fileUploadOptions, actualFileName, expectedFileName, done);
});
it('Strips away all non-alphanumeric characters (excluding hyphens/underscores) when enabled.',
function(done) {
const fileUploadOptions = {safeFileNames: true};
const actualFileName = 'my$Invalid#fileName.png123';
const expectedFileName = 'myInvalidfileNamepng123';
executeFileUploadTestWalk(fileUploadOptions, actualFileName, expectedFileName, done);
});
it('Accepts a regex for stripping (decidedly) "invalid" characters from filename.',
function(done) {
const fileUploadOptions = {safeFileNames: /[$#]/g};
const actualFileName = 'my$Invalid#fileName.png123';
const expectedFileName = 'myInvalidfileName.png123';
executeFileUploadTestWalk(fileUploadOptions, actualFileName, expectedFileName, done);
});
});
describe('Testing [preserveExtension] option to ensure:', function() {
it('Does not preserve the extension of your filename when disabled.',
function(done) {
const fileUploadOptions = {safeFileNames: true, preserveExtension: false};
const actualFileName = 'my$Invalid#fileName.png123';
const expectedFileName = 'myInvalidfileNamepng123';
executeFileUploadTestWalk(fileUploadOptions, actualFileName, expectedFileName, done);
});
it('Is disabled by default.',
function(done) {
const fileUploadOptions = {safeFileNames: true};
const actualFileName = 'my$Invalid#fileName.png123';
const expectedFileName = 'myInvalidfileNamepng123';
executeFileUploadTestWalk(fileUploadOptions, actualFileName, expectedFileName, done);
});
it('Shortens your extension to the default(3) when enabled, if the extension found is larger.',
function(done) {
const fileUploadOptions = {safeFileNames: true, preserveExtension: true};
const actualFileName = 'my$Invalid#fileName.png123';
const expectedFileName = 'myInvalidfileNamepng.123';
executeFileUploadTestWalk(fileUploadOptions, actualFileName, expectedFileName, done);
});
it('Leaves your extension alone when enabled, if the extension found is <= default(3) length',
function(done) {
const fileUploadOptions = {safeFileNames: true, preserveExtension: true};
const actualFileName = 'car.png';
const expectedFileName = 'car.png';
executeFileUploadTestWalk(fileUploadOptions, actualFileName, expectedFileName, done);
});
it('Can be configured for an extension length > default(3).',
function(done) {
const fileUploadOptions = {safeFileNames: true, preserveExtension: 7};
const actualFileName = 'my$Invalid#fileName.png123';
const expectedFileName = 'myInvalidfileName.png123';
executeFileUploadTestWalk(fileUploadOptions, actualFileName, expectedFileName, done);
});
it('Can be configured for an extension length < default(3).',
function(done) {
const fileUploadOptions = {safeFileNames: true, preserveExtension: 2};
const actualFileName = 'my$Invalid#fileName.png123';
const expectedFileName = 'myInvalidfileNamepng1.23';
executeFileUploadTestWalk(fileUploadOptions, actualFileName, expectedFileName, done);
});
it('Will use the absolute value of your extension length when negative.',
function(done) {
const fileUploadOptions = {safeFileNames: true, preserveExtension: -5};
const actualFileName = 'my$Invalid#fileName.png123';
const expectedFileName = 'myInvalidfileNamep.ng123';
executeFileUploadTestWalk(fileUploadOptions, actualFileName, expectedFileName, done);
});
it('Will leave no extension when the extension length == 0.',
function(done) {
const fileUploadOptions = {safeFileNames: true, preserveExtension: 0};
const actualFileName = 'car.png';
const expectedFileName = 'car';
executeFileUploadTestWalk(fileUploadOptions, actualFileName, expectedFileName, done);
});
it('Will accept numbers as strings, if they can be resolved with parseInt.',
function(done) {
const fileUploadOptions = {safeFileNames: true, preserveExtension: '3'};
const actualFileName = 'my$Invalid#fileName.png123';
const expectedFileName = 'myInvalidfileNamepng.123';
executeFileUploadTestWalk(fileUploadOptions, actualFileName, expectedFileName, done);
});
it('Will be evaluated for truthy-ness if it cannot be parsed as an int.',
function(done) {
const fileUploadOptions = {safeFileNames: true, preserveExtension: 'not-a-#-but-truthy'};
const actualFileName = 'my$Invalid#fileName.png123';
const expectedFileName = 'myInvalidfileNamepng.123';
executeFileUploadTestWalk(fileUploadOptions, actualFileName, expectedFileName, done);
});
it('Will ignore any decimal amount when evaluating for extension length.',
function(done) {
const fileUploadOptions = {safeFileNames: true, preserveExtension: 4.98};
const actualFileName = 'my$Invalid#fileName.png123';
const expectedFileName = 'myInvalidfileNamepn.g123';
executeFileUploadTestWalk(fileUploadOptions, actualFileName, expectedFileName, done);
});
it('Only considers the last dotted part as the extension.',
function(done) {
const fileUploadOptions = {safeFileNames: true, preserveExtension: true};
const actualFileName = 'basket.ball.bp';
const expectedFileName = 'basketball.bp';
executeFileUploadTestWalk(fileUploadOptions, actualFileName, expectedFileName, done);
});
});
describe('Testing [parseNested] option to ensure:', function() {
it('When [parseNested] is enabled result are nested', function(done){
const app = server.setup({parseNested: true});
request(app)
.post('/fields/nested')
.field('name', 'John')
.field('hobbies[0]', 'Cinema')
.field('hobbies[1]', 'Bike')
.expect('Content-Type', /json/)
.expect(200, {
name: 'John',
hobbies: ['Cinema', 'Bike']
}, done);
});
it('When [parseNested] is disabled are flattened', function(done){
const app = server.setup({parseNested: false});
request(app)
.post('/fields/flattened')
.field('name', 'John')
.field('hobbies[0]', 'Cinema')
.field('hobbies[1]', 'Bike')
.expect('Content-Type', /json/)
.expect(200, {
name: 'John',
'hobbies[0]': 'Cinema',
'hobbies[1]': 'Bike'
}, done);
});
});
});

5
node_modules/express-fileupload/test/posttests.js generated vendored Normal file
View file

@ -0,0 +1,5 @@
const { clearFileDir } = require('./server');
console.log('clearing test files...');
clearFileDir();
console.log('done');

5
node_modules/express-fileupload/test/pretests.js generated vendored Normal file
View file

@ -0,0 +1,5 @@
const { createTestFiles } = require('./server');
console.log('creating test files...');
createTestFiles()
.then(() => console.log('done'));

View file

@ -0,0 +1,59 @@
'use strict';
const assert = require('assert');
const processNested = require('../lib/processNested');
describe('processNested: Test Convert Flatten object to Nested object', function() {
it('With no nested data', () => {
const data = {
'firstname': 'John',
'lastname': 'Doe',
'age': 22
},
excerpt = { firstname: 'John', lastname: 'Doe', age: 22 },
processed = processNested(data);
assert.deepEqual(processed, excerpt);
});
it('With nested data', () => {
const data = {
'firstname': 'John',
'lastname': 'Doe',
'age': 22,
'hobbies[0]': 'Cinema',
'hobbies[1]': 'Bike',
'address[line]': '78 Lynch Street',
'address[city]': 'Milwaukee',
'friends[0][name]': 'Jane',
'friends[0][lastname]': 'Doe',
'friends[1][name]': 'Joe',
'friends[1][lastname]': 'Doe'
},
excerpt = {
firstname: 'John',
lastname: 'Doe',
age: 22,
hobbies: [ 'Cinema', 'Bike' ],
address: { line: '78 Lynch Street', city: 'Milwaukee' },
friends: [
{ name: 'Jane', lastname: 'Doe' },
{ name: 'Joe', lastname: 'Doe' }
]
},
processed = processNested(data);
assert.deepEqual(processed, excerpt);
});
it('Do not allow prototype pollution', () => {
const pollutionOb1 = JSON.parse(`{"__proto__.POLLUTED1": "FOOBAR"}`);
const pollutionOb2 = JSON.parse(`{"constructor.prototype.POLLUTED2": "FOOBAR"}`);
processNested(pollutionOb1);
processNested(pollutionOb2);
assert.equal(global.POLLUTED1, undefined);
assert.equal(global.POLLUTED2, undefined);
});
});

295
node_modules/express-fileupload/test/server.js generated vendored Normal file
View file

@ -0,0 +1,295 @@
'use strict';
const fs = require('fs');
const path = require('path');
const rimraf = require('rimraf');
const randomFile = require('rnd-file');
const fileDir = path.join(__dirname, 'files');
const tempDir = path.join(__dirname, 'temp');
const uploadDir = path.join(__dirname, 'uploads');
const mockFiles = [
{ name: 'emptyfile.txt', size: 0 },
{ name: 'basket.ball.bp', size: 151 * 1024 },
{ name: 'basketball.png', size: 151 * 1024 },
{ name: 'car.png', size: 263 * 1024 },
{ name: 'my$Invalid#fileName.png123', size: 263 * 1024 },
{ name: 'tree.png', size: 266 * 1024 }
];
const clearDir = (dir) => {
try {
try {
const stats = fs.statSync(dir);
if (stats.isDirectory()) rimraf.sync(dir);
} catch (statsErr) {
if (statsErr.code !== 'ENOENT') {
throw statsErr;
}
}
fs.mkdirSync(dir, { recursive: true });
} catch (err) {
console.error(err); // eslint-disable-line
}
};
const createTestFiles = () => Promise.all(mockFiles.map((file) => {
return randomFile({ filePath: fileDir, fileName: file.name, fileSize: file.size });
}));
const getUploadedFileData = (file) => ({
md5: file.md5,
name: file.name,
size: file.size,
uploadPath: path.join(uploadDir, file.name),
uploadDir: uploadDir
});
const setup = (fileUploadOptions) => {
const express = require('express');
const expressFileupload = require('../lib/index');
const app = express();
app.use(expressFileupload(fileUploadOptions || {}));
app.all('/upload/single', (req, res) => {
if (!req.files) {
return res.status(400).send('No files were uploaded.');
}
const testFile = req.files.testFile;
const fileData = getUploadedFileData(testFile);
testFile.mv(fileData.uploadPath, (err) => {
if (err) {
console.log('ERR', err); // eslint-disable-line
return res.status(500).send(err);
}
res.json(fileData);
});
});
app.all('/upload/single/promise', (req, res) => {
if (!req.files) {
return res.status(400).send('No files were uploaded.');
}
const testFile = req.files.testFile;
const fileData = getUploadedFileData(testFile);
testFile
.mv(fileData.uploadPath)
.then(() => {
res.json(fileData);
})
.catch(err => {
res.status(500).send(err);
});
});
app.all('/upload/single/withfields', (req, res) => {
if (!req.files) {
return res.status(400).send('No files were uploaded.');
}
if (!req.body) {
return res.status(400).send('No request body found');
}
const fields = ['firstName', 'lastName', 'email'];
for (let i = 0; i < fields.length; i += 1) {
if (!req.body[fields[i]] || !req.body[fields[i]].trim()) {
return res.status(400).send(`Invalid field: ${fields[i]}`);
}
}
const testFile = req.files.testFile;
const fileData = getUploadedFileData(testFile);
fields.forEach((field) => { fileData[field] = req.body[field]; });
testFile.mv(fileData.uploadPath, (err) => {
if (err) {
return res.status(500).send(err);
}
res.json(fileData);
});
});
app.all('/upload/single/truncated', (req, res) => {
if (!req.files) {
return res.status(400).send('No files were uploaded.');
}
// status 400 to differentiate from ending the request in the on limit
return req.files.testFile.truncated
? res.status(400).send(`File too big`)
: res.status(200).send('Upload succeed');
});
app.all('/upload/multiple', function(req, res) {
if (!req.files) {
return res.status(400).send('No files were uploaded.');
}
const fileNames = ['testFile1', 'testFile2', 'testFile3'];
const testFiles = fileNames.map(file => req.files[file]);
for (let i = 0; i < testFiles.length; i += 1) {
if (!testFiles[i]) {
return res.status(400).send(`${fileNames[i]} was not uploaded!`);
}
}
const filesData = testFiles.map(file => getUploadedFileData(file));
testFiles[0].mv(filesData[0].uploadPath, (err) => {
if (err) {
return res.status(500).send(err);
}
testFiles[1].mv(filesData[1].uploadPath, (err) => {
if (err) {
return res.status(500).send(err);
}
testFiles[2].mv(filesData[2].uploadPath, (err) => {
if (err) {
return res.status(500).send(err);
}
res.json(filesData);
});
});
});
});
app.all('/upload/array', function(req, res) {
if (!req.files) {
return res.status(400).send('No files were uploaded.');
}
const testFiles = req.files.testFiles;
if (!testFiles) {
return res.status(400).send('No files were uploaded');
}
if (!Array.isArray(testFiles)) {
return res.status(400).send('Files were not uploaded as an array');
}
if (!testFiles.length) {
return res.status(400).send('Files array is empty');
}
const filesData = testFiles.map(file => getUploadedFileData(file));
let uploadCount = 0;
for (let i = 0; i < testFiles.length; i += 1) {
testFiles[i].mv(filesData[i].uploadPath, (err) => {
if (err) {
return res.status(500).send(err);
}
uploadCount += 1;
if (uploadCount === testFiles.length) {
res.json(filesData);
}
});
}
});
app.all('/fields/user', function(req, res) {
if (!req.body) {
return res.status(400).send('No request body found');
}
const fields = ['firstName', 'lastName', 'email'];
for (let i = 0; i < fields.length; i += 1) {
if (!req.body[fields[i]] || !req.body[fields[i]].trim()) {
return res.status(400).send(`Invalid field: ${fields[i]}`);
}
}
res.json({
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email
});
});
app.all('/fields/nested', function(req, res) {
if (!req.body) {
return res.status(400).send('No request body found');
}
if (!req.body.name || !req.body.name.trim()) {
return res.status(400).send('Invalid name');
}
if (!req.body.hobbies || !req.body.hobbies.length == 2) {
return res.status(400).send('Invalid hobbies');
}
res.json({
name: req.body.name,
hobbies: req.body.hobbies
});
});
app.all('/fields/flattened', function(req, res) {
if (!req.body) {
return res.status(400).send('No request body found');
}
if (!req.body.name || !req.body.name.trim()) {
return res.status(400).send('Invalid name');
}
if (!req.body['hobbies[0]'] || !req.body['hobbies[0]'].trim()) {
return res.status(400).send('Invalid hobbies[0]');
}
if (!req.body['hobbies[1]'] || !req.body['hobbies[1]'].trim()) {
return res.status(400).send('Invalid hobbies[1]');
}
res.json({
name: req.body.name,
'hobbies[0]': req.body['hobbies[0]'],
'hobbies[1]': req.body['hobbies[1]']
});
});
app.all('/fields/array', function(req, res) {
if (!req.body) {
return res.status(400).send('No request body found');
}
if (!req.body.testField) {
return res.status(400).send('Invalid field');
}
if (!Array.isArray(req.body.testField)) {
return res.status(400).send('Field is not an array');
}
res.json(req.body.testField);
});
return app;
};
module.exports = {
setup,
fileDir,
tempDir,
uploadDir,
clearFileDir: () => clearDir(fileDir),
clearTempDir: () => clearDir(tempDir),
clearUploadsDir: () => clearDir(uploadDir),
createTestFiles
};

126
node_modules/express-fileupload/test/tempFile.spec.js generated vendored Normal file
View file

@ -0,0 +1,126 @@
const fs = require('fs');
const md5 = require('md5');
const path = require('path');
const request = require('supertest');
const server = require('./server');
const clearUploadsDir = server.clearUploadsDir;
const fileDir = server.fileDir;
const uploadDir = server.uploadDir;
describe('tempFile: Test fileupload w/ useTempFiles.', function() {
afterEach(function(done) {
clearUploadsDir();
done();
});
/**
* Upload the file for testing and verify the expected filename.
* @param {object} options The expressFileUpload options.
* @param {string} actualFileNameToUpload The name of the file to upload.
* @param {string} expectedFileNameOnFileSystem The name of the file after upload.
* @param {function} done The mocha continuation function.
*/
function executeFileUploadTestWalk(
options,
actualFileNameToUpload,
expectedFileNameOnFileSystem,
done
) {
let filePath = path.join(fileDir, actualFileNameToUpload);
let fileBuffer = fs.readFileSync(filePath);
let fileHash = md5(fileBuffer);
let fileStat = fs.statSync(filePath);
let uploadedFilePath = path.join(uploadDir, expectedFileNameOnFileSystem);
request(
server.setup(options)
)
.post('/upload/single')
.attach('testFile', filePath)
.expect((res)=>{
res.body.uploadDir = '';
res.body.uploadPath = '';
})
.expect(200, {
name: expectedFileNameOnFileSystem,
md5: fileHash,
size: fileStat.size,
uploadDir: '',
uploadPath: ''
})
.end(function(err) {
if (err) {
return done(err);
}
fs.stat(uploadedFilePath, done);
});
}
describe('Testing [safeFileNames w/ useTempFiles] option to ensure:', function() {
it('Does nothing to your filename when disabled.', function(done) {
const fileUploadOptions = {
safeFileNames: false,
useTempFiles: true,
tempFileDir: '/tmp/'
};
const actualFileName = 'my$Invalid#fileName.png123';
const expectedFileName = 'my$Invalid#fileName.png123';
executeFileUploadTestWalk(
fileUploadOptions,
actualFileName,
expectedFileName,
done
);
});
it('Is disabled by default.', function(done) {
const fileUploadOptions = {
useTempFiles: true,
tempFileDir: '/tmp/'
};
const actualFileName = 'my$Invalid#fileName.png123';
const expectedFileName = 'my$Invalid#fileName.png123';
executeFileUploadTestWalk(
fileUploadOptions,
actualFileName,
expectedFileName,
done
);
});
it(
'Strips away all non-alphanumeric characters (excluding hyphens/underscores) when enabled.',
function(done) {
const fileUploadOptions = {
safeFileNames: true,
useTempFiles: true,
tempFileDir: '/tmp/'
};
const actualFileName = 'my$Invalid#fileName.png123';
const expectedFileName = 'myInvalidfileNamepng123';
executeFileUploadTestWalk(
fileUploadOptions,
actualFileName,
expectedFileName,
done
);
});
it(
'Accepts a regex for stripping (decidedly) "invalid" characters from filename.',
function(done) {
const fileUploadOptions = {
safeFileNames: /[$#]/g,
useTempFiles: true,
tempFileDir: '/tmp/'
};
const actualFileName = 'my$Invalid#fileName.png123';
const expectedFileName = 'myInvalidfileName.png123';
executeFileUploadTestWalk(
fileUploadOptions,
actualFileName,
expectedFileName,
done
);
});
});
});

View file

@ -0,0 +1,28 @@
'use strict';
const assert = require('assert');
const UploadTimer = require('../lib/uploadtimer');
describe('uploadTimer: Test UploadTimer class', () => {
it('It runs a callback function after specified timeout.', (done) => {
const uploadTimer = new UploadTimer(1000, done);
uploadTimer.set();
});
it('set method returns true if timeout specified.', () => {
const uploadTimer = new UploadTimer(1000);
assert.equal(uploadTimer.set(), true);
});
it('set method returns false if timeout has not specified.', () => {
const uploadTimer = new UploadTimer();
assert.equal(uploadTimer.set(), false);
});
it('set method returns false if zero timeout has specified.', () => {
const uploadTimer = new UploadTimer(0);
assert.equal(uploadTimer.set(), false);
});
});

491
node_modules/express-fileupload/test/utilities.spec.js generated vendored Normal file
View file

@ -0,0 +1,491 @@
'use strict';
const assert = require('assert');
const path = require('path');
const fs = require('fs');
const md5 = require('md5');
const server = require('./server');
const fileDir = server.fileDir;
const uploadDir = server.uploadDir;
const {
debugLog,
isFunc,
errorFunc,
getTempFilename,
buildOptions,
buildFields,
checkAndMakeDir,
deleteFile,
copyFile,
moveFile,
saveBufferToFile,
parseFileName,
uriDecodeFileName,
isSafeFromPollution
} = require('../lib/utilities');
const mockFile = 'basketball.png';
const mockBuffer = fs.readFileSync(path.join(fileDir, mockFile));
const mockHash = md5(mockBuffer);
const mockFileMove = 'car.png';
const mockBufferMove = fs.readFileSync(path.join(fileDir, mockFileMove));
const mockHashMove = md5(mockBufferMove);
describe('utilities: Test of the utilities functions', function() {
//debugLog tests
describe('Test debugLog function', () => {
let testMessage = 'Test message';
it('debugLog returns false if no options passed', () => {
assert.equal(debugLog(null, testMessage), false);
});
it('debugLog returns false if option debug is false', () => {
assert.equal(debugLog({debug: false}, testMessage), false);
});
it('debugLog returns true if option debug is true', () => {
assert.equal(debugLog({debug: true}, testMessage), true);
});
});
//isFunc tests
describe('Test isFunc function', () => {
it('isFunc returns true if function passed', () => assert.equal(isFunc(()=>{}), true));
it('isFunc returns false if null passed', function() {
assert.equal(isFunc(null), false);
});
it('isFunc returns false if undefined passed', function() {
assert.equal(isFunc(undefined), false);
});
it('isFunc returns false if object passed', function() {
assert.equal(isFunc({}), false);
});
it('isFunc returns false if array passed', function() {
assert.equal(isFunc([]), false);
});
});
//errorFunc tests
describe('Test errorFunc function', () => {
const resolve = () => 'success';
const reject = () => 'error';
it('errorFunc returns resolve if reject function has not been passed', () => {
let result = errorFunc(resolve);
assert.equal(result(), 'success');
});
it('errorFunc returns reject if reject function has been passed', () => {
let result = errorFunc(resolve, reject);
assert.equal(result(), 'error');
});
});
//getTempFilename tests
describe('Test getTempFilename function', () => {
const nameRegexp = /tmp-\d{1,5}-\d{1,}/;
it('getTempFilename result matches regexp /tmp-d{1,5}-d{1,}/', () => {
let errCounter = 0;
let tempName = '';
for (var i = 0; i < 65537; i++) {
tempName = getTempFilename();
if (!nameRegexp.test(tempName)) errCounter ++;
}
assert.equal(errCounter, 0);
});
it('getTempFilename current and previous results are not equal', () => {
let errCounter = 0;
let tempName = '';
let previousName = '';
for (var i = 0; i < 65537; i++) {
previousName = tempName;
tempName = getTempFilename();
if (previousName === tempName) errCounter ++;
}
assert.equal(errCounter, 0);
});
});
//parseFileName
describe('Test parseFileName function', () => {
it('Does nothing to your filename when disabled.', () => {
const opts = {safeFileNames: false};
const name = 'my$Invalid#fileName.png123';
const expected = 'my$Invalid#fileName.png123';
let result = parseFileName(opts, name);
assert.equal(result, expected);
});
it('Cuts of file name length if it more then 255 chars.', () => {
const name = 'a'.repeat(300);
const result = parseFileName({}, name);
assert.equal(result.length, 255);
});
it(
'Strips away all non-alphanumeric characters (excluding hyphens/underscores) when enabled.',
() => {
const opts = {safeFileNames: true};
const name = 'my$Invalid#fileName.png123';
const expected = 'myInvalidfileNamepng123';
let result = parseFileName(opts, name);
assert.equal(result, expected);
});
it(
'Strips away all non-alphanumeric chars when preserveExtension: true for a name without dots',
() => {
const opts = {safeFileNames: true, preserveExtension: true};
const name = 'my$Invalid#fileName';
const expected = 'myInvalidfileName';
let result = parseFileName(opts, name);
assert.equal(result, expected);
});
it('Accepts a regex for stripping (decidedly) "invalid" characters from filename.', () => {
const opts = {safeFileNames: /[$#]/g};
const name = 'my$Invalid#fileName.png123';
const expected = 'myInvalidfileName.png123';
let result = parseFileName(opts, name);
assert.equal(result, expected);
});
it(
'Returns correct filename if name contains dots characters and preserveExtension: true.',
() => {
const opts = {safeFileNames: true, preserveExtension: true};
const name = 'basket.ball.png';
const expected = 'basketball.png';
let result = parseFileName(opts, name);
assert.equal(result, expected);
});
it('Returns a temporary file name if name argument is empty.', () => {
const opts = {safeFileNames: false};
const result = parseFileName(opts);
assert.equal(typeof result, 'string');
});
});
//buildOptions tests
describe('Test buildOptions function', () => {
const source = { option1: '1', option2: '2' };
const sourceAddon = { option3: '3'};
const expected = { option1: '1', option2: '2' };
const expectedAddon = { option1: '1', option2: '2', option3: '3'};
it('buildOptions returns and equal object to the object which was paased', () => {
let result = buildOptions(source);
assert.deepStrictEqual(result, source);
});
it('buildOptions doesnt add non object or null arguments to the result', () => {
let result = buildOptions(source, 2, '3', null);
assert.deepStrictEqual(result, expected);
});
it('buildOptions adds value to the result from the several source argumets', () => {
let result = buildOptions(source, sourceAddon);
assert.deepStrictEqual(result, expectedAddon);
});
});
//buildFields tests
describe('Test buildFields function', () => {
it('buildFields does nothing if null value has been passed', () => {
let fields = null;
fields = buildFields(fields, 'test', null);
assert.equal(fields, null);
});
});
//checkAndMakeDir tests
describe('Test checkAndMakeDir function', () => {
//
it('checkAndMakeDir returns false if upload options object was not set', () => {
assert.equal(checkAndMakeDir(), false);
});
//
it('checkAndMakeDir returns false if upload option createParentPath was not set', () => {
assert.equal(checkAndMakeDir({}), false);
});
//
it('checkAndMakeDir returns false if filePath was not set', () => {
assert.equal(checkAndMakeDir({createParentPath: true}), false);
});
//
it('checkAndMakeDir return true if path to the file already exists', ()=>{
let dir = path.join(uploadDir, 'testfile');
assert.equal(checkAndMakeDir({createParentPath: true}, dir), true);
});
//
it('checkAndMakeDir creates a dir if path to the file not exists', ()=>{
let dir = path.join(uploadDir, 'testfolder', 'testfile');
assert.equal(checkAndMakeDir({createParentPath: true}, dir), true);
});
//
it('checkAndMakeDir creates a dir recursively if path to the file not exists', ()=>{
let dir = path.join(uploadDir, 'testfolder', 'testsubfolder', 'testfile');
assert.equal(checkAndMakeDir({createParentPath: true}, dir), true);
});
});
describe('Test moveFile function', function() {
beforeEach(() => server.clearUploadsDir());
it('Should rename a file and check a hash', function(done) {
const srcPath = path.join(fileDir, mockFileMove);
const dstPath = path.join(uploadDir, mockFileMove);
moveFile(srcPath, dstPath, function(err, renamed) {
if (err) {
return done(err);
}
fs.stat(dstPath, (err) => {
if (err) {
return done(err);
}
// Match source and destination files hash.
const fileBuffer = fs.readFileSync(dstPath);
const fileHash = md5(fileBuffer);
if (fileHash !== mockHashMove) {
return done(new Error('Hashes do not match'));
}
// Check that source file was deleted.
fs.stat(srcPath, (err) => {
if (err) {
return done(renamed ? null : new Error('Source file was not renamed'));
}
done(new Error('Source file was not deleted'));
});
});
});
});
});
//saveBufferToFile tests
describe('Test saveBufferToFile function', function(){
beforeEach(() => server.clearUploadsDir());
it('Save buffer to a file', function(done) {
let filePath = path.join(uploadDir, mockFile);
saveBufferToFile(mockBuffer, filePath, function(err){
if (err) {
return done(err);
}
fs.stat(filePath, done);
});
});
it('Failed if not a buffer passed', function(done) {
let filePath = path.join(uploadDir, mockFile);
saveBufferToFile(undefined, filePath, function(err){
if (err) {
return done();
}
});
});
it('Failed if wrong path passed', function(done) {
let filePath = '';
saveBufferToFile(mockFile, filePath, function(err){
if (err) {
return done();
}
});
});
});
describe('Test deleteFile function', function(){
beforeEach(() => server.clearUploadsDir());
it('Failed if nonexistent file passed', function(done){
let filePath = path.join(uploadDir, getTempFilename());
deleteFile(filePath, function(err){
if (err) {
return done();
}
});
});
it('Delete a file', function(done){
let srcPath = path.join(fileDir, mockFile);
let dstPath = path.join(uploadDir, getTempFilename());
// copy a file
copyFile(srcPath, dstPath, function(err){
if (err) {
return done(err);
}
fs.stat(dstPath, (err)=>{
if (err) {
return done(err);
}
// delete a file
deleteFile(dstPath, function(err){
if (err) {
return done(err);
}
fs.stat(dstPath, (err)=> {
return err ? done() : done(new Error('File was not deleted'));
});
});
});
});
});
});
describe('Test copyFile function', function() {
beforeEach(() => server.clearUploadsDir());
it('Copy a file and check a hash', function(done) {
let srcPath = path.join(fileDir, mockFile);
let dstPath = path.join(uploadDir, mockFile);
copyFile(srcPath, dstPath, function(err){
if (err) {
return done(err);
}
fs.stat(dstPath, (err)=>{
if (err){
return done(err);
}
// Match source and destination files hash.
let fileBuffer = fs.readFileSync(dstPath);
let fileHash = md5(fileBuffer);
return fileHash === mockHash ? done() : done(new Error('Hashes do not match'));
});
});
});
it('Failed if wrong source file path passed', function(done){
let srcPath = path.join(fileDir, 'unknown');
let dstPath = path.join(uploadDir, mockFile);
copyFile(srcPath, dstPath, function(err){
if (err) {
return done();
}
});
});
it('Failed if wrong destination file path passed', function(done){
let srcPath = path.join(fileDir, 'unknown');
let dstPath = path.join('unknown', 'unknown');
copyFile(srcPath, dstPath, function(err){
if (err) {
return done();
}
});
});
});
describe('Test uriDecodeFileName function', function() {
const testData = [
{ enc: 'test%22filename', dec: 'test"filename' },
{ enc: 'test%60filename', dec: 'test`filename' },
{ enc: '%3Fx%3Dtest%22filename', dec: '?x=test"filename'},
{ enc: 'bug_bounty_upload_%91%91and%92.txt', dec: 'bug_bounty_upload_and.txt'}
];
// Test decoding if uriDecodeFileNames: true.
testData.forEach((testName) => {
const opts = { uriDecodeFileNames: true };
it(`Return ${testName.dec} for input ${testName.enc} if uriDecodeFileNames: true`, () => {
assert.equal(uriDecodeFileName(opts, testName.enc), testName.dec);
});
});
// Test decoding if uriDecodeFileNames: false.
testData.forEach((testName) => {
const opts = { uriDecodeFileNames: false };
it(`Return ${testName.enc} for input ${testName.enc} if uriDecodeFileNames: false`, () => {
assert.equal(uriDecodeFileName(opts, testName.enc), testName.enc);
});
});
});
describe('Test for no prototype pollution in buildFields', function() {
const prototypeFields = [
{ name: '__proto__', data: {} },
{ name: 'constructor', data: {} },
{ name: 'toString', data: {} }
];
const nonPrototypeFields = [
{ name: 'a', data: {} },
{ name: 'b', data: {} }
];
let fieldObject = undefined;
[...prototypeFields, ...nonPrototypeFields].forEach((field) => {
fieldObject = buildFields(fieldObject, field.name, field.data);
});
it(`Has ${nonPrototypeFields.length} keys`, () => {
assert.equal(Object.keys(fieldObject).length, nonPrototypeFields.length);
});
it(`Has null as its prototype`, () => {
assert.equal(Object.getPrototypeOf(fieldObject), null);
});
prototypeFields.forEach((field) => {
it(`${field.name} property is not an array`, () => {
// Note, Array.isArray is an insufficient test due to it returning false
// for Objects with an array prototype.
assert.equal(fieldObject[field.name] instanceof Array, false);
});
});
});
describe('Test for correct detection of prototype pollution', function() {
const validInsertions = [
{ base: {}, key: 'a' },
{ base: { a: 1 }, key: 'a' },
{ base: { __proto__: { a: 1 } }, key: 'a' },
{ base: [1], key: 0 },
{ base: { __proto__: [1] }, key: 0 }
];
const invalidInsertions = [
{ base: {}, key: '__proto__' },
{ base: {}, key: 'constructor' },
{ base: [1], key: '__proto__' },
{ base: [1], key: 'length' },
{ base: { __proto__: [1] }, key: 'length' }
];
validInsertions.forEach((insertion) => {
it(`Key ${insertion.key} should be valid for ${JSON.stringify(insertion.base)}`, () => {
assert.equal(isSafeFromPollution(insertion.base, insertion.key), true);
});
});
invalidInsertions.forEach((insertion) => {
it(`Key ${insertion.key} should not be valid for ${JSON.stringify(insertion.base)}`, () => {
assert.equal(isSafeFromPollution(insertion.base, insertion.key), false);
});
});
});
});

View file

@ -0,0 +1,657 @@
1.20.1 / 2022-10-06
===================
* deps: qs@6.11.0
* perf: remove unnecessary object clone
1.20.0 / 2022-04-02
===================
* Fix error message for json parse whitespace in `strict`
* Fix internal error when inflated body exceeds limit
* Prevent loss of async hooks context
* Prevent hanging when request already read
* deps: depd@2.0.0
- Replace internal `eval` usage with `Function` constructor
- Use instance methods on `process` to check for listeners
* deps: http-errors@2.0.0
- deps: depd@2.0.0
- deps: statuses@2.0.1
* deps: on-finished@2.4.1
* deps: qs@6.10.3
* deps: raw-body@2.5.1
- deps: http-errors@2.0.0
1.19.2 / 2022-02-15
===================
* deps: bytes@3.1.2
* deps: qs@6.9.7
* Fix handling of `__proto__` keys
* deps: raw-body@2.4.3
- deps: bytes@3.1.2
1.19.1 / 2021-12-10
===================
* deps: bytes@3.1.1
* deps: http-errors@1.8.1
- deps: inherits@2.0.4
- deps: toidentifier@1.0.1
- deps: setprototypeof@1.2.0
* deps: qs@6.9.6
* deps: raw-body@2.4.2
- deps: bytes@3.1.1
- deps: http-errors@1.8.1
* deps: safe-buffer@5.2.1
* deps: type-is@~1.6.18
1.19.0 / 2019-04-25
===================
* deps: bytes@3.1.0
- Add petabyte (`pb`) support
* deps: http-errors@1.7.2
- Set constructor name when possible
- deps: setprototypeof@1.1.1
- deps: statuses@'>= 1.5.0 < 2'
* deps: iconv-lite@0.4.24
- Added encoding MIK
* deps: qs@6.7.0
- Fix parsing array brackets after index
* deps: raw-body@2.4.0
- deps: bytes@3.1.0
- deps: http-errors@1.7.2
- deps: iconv-lite@0.4.24
* deps: type-is@~1.6.17
- deps: mime-types@~2.1.24
- perf: prevent internal `throw` on invalid type
1.18.3 / 2018-05-14
===================
* Fix stack trace for strict json parse error
* deps: depd@~1.1.2
- perf: remove argument reassignment
* deps: http-errors@~1.6.3
- deps: depd@~1.1.2
- deps: setprototypeof@1.1.0
- deps: statuses@'>= 1.3.1 < 2'
* deps: iconv-lite@0.4.23
- Fix loading encoding with year appended
- Fix deprecation warnings on Node.js 10+
* deps: qs@6.5.2
* deps: raw-body@2.3.3
- deps: http-errors@1.6.3
- deps: iconv-lite@0.4.23
* deps: type-is@~1.6.16
- deps: mime-types@~2.1.18
1.18.2 / 2017-09-22
===================
* deps: debug@2.6.9
* perf: remove argument reassignment
1.18.1 / 2017-09-12
===================
* deps: content-type@~1.0.4
- perf: remove argument reassignment
- perf: skip parameter parsing when no parameters
* deps: iconv-lite@0.4.19
- Fix ISO-8859-1 regression
- Update Windows-1255
* deps: qs@6.5.1
- Fix parsing & compacting very deep objects
* deps: raw-body@2.3.2
- deps: iconv-lite@0.4.19
1.18.0 / 2017-09-08
===================
* Fix JSON strict violation error to match native parse error
* Include the `body` property on verify errors
* Include the `type` property on all generated errors
* Use `http-errors` to set status code on errors
* deps: bytes@3.0.0
* deps: debug@2.6.8
* deps: depd@~1.1.1
- Remove unnecessary `Buffer` loading
* deps: http-errors@~1.6.2
- deps: depd@1.1.1
* deps: iconv-lite@0.4.18
- Add support for React Native
- Add a warning if not loaded as utf-8
- Fix CESU-8 decoding in Node.js 8
- Improve speed of ISO-8859-1 encoding
* deps: qs@6.5.0
* deps: raw-body@2.3.1
- Use `http-errors` for standard emitted errors
- deps: bytes@3.0.0
- deps: iconv-lite@0.4.18
- perf: skip buffer decoding on overage chunk
* perf: prevent internal `throw` when missing charset
1.17.2 / 2017-05-17
===================
* deps: debug@2.6.7
- Fix `DEBUG_MAX_ARRAY_LENGTH`
- deps: ms@2.0.0
* deps: type-is@~1.6.15
- deps: mime-types@~2.1.15
1.17.1 / 2017-03-06
===================
* deps: qs@6.4.0
- Fix regression parsing keys starting with `[`
1.17.0 / 2017-03-01
===================
* deps: http-errors@~1.6.1
- Make `message` property enumerable for `HttpError`s
- deps: setprototypeof@1.0.3
* deps: qs@6.3.1
- Fix compacting nested arrays
1.16.1 / 2017-02-10
===================
* deps: debug@2.6.1
- Fix deprecation messages in WebStorm and other editors
- Undeprecate `DEBUG_FD` set to `1` or `2`
1.16.0 / 2017-01-17
===================
* deps: debug@2.6.0
- Allow colors in workers
- Deprecated `DEBUG_FD` environment variable
- Fix error when running under React Native
- Use same color for same namespace
- deps: ms@0.7.2
* deps: http-errors@~1.5.1
- deps: inherits@2.0.3
- deps: setprototypeof@1.0.2
- deps: statuses@'>= 1.3.1 < 2'
* deps: iconv-lite@0.4.15
- Added encoding MS-31J
- Added encoding MS-932
- Added encoding MS-936
- Added encoding MS-949
- Added encoding MS-950
- Fix GBK/GB18030 handling of Euro character
* deps: qs@6.2.1
- Fix array parsing from skipping empty values
* deps: raw-body@~2.2.0
- deps: iconv-lite@0.4.15
* deps: type-is@~1.6.14
- deps: mime-types@~2.1.13
1.15.2 / 2016-06-19
===================
* deps: bytes@2.4.0
* deps: content-type@~1.0.2
- perf: enable strict mode
* deps: http-errors@~1.5.0
- Use `setprototypeof` module to replace `__proto__` setting
- deps: statuses@'>= 1.3.0 < 2'
- perf: enable strict mode
* deps: qs@6.2.0
* deps: raw-body@~2.1.7
- deps: bytes@2.4.0
- perf: remove double-cleanup on happy path
* deps: type-is@~1.6.13
- deps: mime-types@~2.1.11
1.15.1 / 2016-05-05
===================
* deps: bytes@2.3.0
- Drop partial bytes on all parsed units
- Fix parsing byte string that looks like hex
* deps: raw-body@~2.1.6
- deps: bytes@2.3.0
* deps: type-is@~1.6.12
- deps: mime-types@~2.1.10
1.15.0 / 2016-02-10
===================
* deps: http-errors@~1.4.0
- Add `HttpError` export, for `err instanceof createError.HttpError`
- deps: inherits@2.0.1
- deps: statuses@'>= 1.2.1 < 2'
* deps: qs@6.1.0
* deps: type-is@~1.6.11
- deps: mime-types@~2.1.9
1.14.2 / 2015-12-16
===================
* deps: bytes@2.2.0
* deps: iconv-lite@0.4.13
* deps: qs@5.2.0
* deps: raw-body@~2.1.5
- deps: bytes@2.2.0
- deps: iconv-lite@0.4.13
* deps: type-is@~1.6.10
- deps: mime-types@~2.1.8
1.14.1 / 2015-09-27
===================
* Fix issue where invalid charset results in 400 when `verify` used
* deps: iconv-lite@0.4.12
- Fix CESU-8 decoding in Node.js 4.x
* deps: raw-body@~2.1.4
- Fix masking critical errors from `iconv-lite`
- deps: iconv-lite@0.4.12
* deps: type-is@~1.6.9
- deps: mime-types@~2.1.7
1.14.0 / 2015-09-16
===================
* Fix JSON strict parse error to match syntax errors
* Provide static `require` analysis in `urlencoded` parser
* deps: depd@~1.1.0
- Support web browser loading
* deps: qs@5.1.0
* deps: raw-body@~2.1.3
- Fix sync callback when attaching data listener causes sync read
* deps: type-is@~1.6.8
- Fix type error when given invalid type to match against
- deps: mime-types@~2.1.6
1.13.3 / 2015-07-31
===================
* deps: type-is@~1.6.6
- deps: mime-types@~2.1.4
1.13.2 / 2015-07-05
===================
* deps: iconv-lite@0.4.11
* deps: qs@4.0.0
- Fix dropping parameters like `hasOwnProperty`
- Fix user-visible incompatibilities from 3.1.0
- Fix various parsing edge cases
* deps: raw-body@~2.1.2
- Fix error stack traces to skip `makeError`
- deps: iconv-lite@0.4.11
* deps: type-is@~1.6.4
- deps: mime-types@~2.1.2
- perf: enable strict mode
- perf: remove argument reassignment
1.13.1 / 2015-06-16
===================
* deps: qs@2.4.2
- Downgraded from 3.1.0 because of user-visible incompatibilities
1.13.0 / 2015-06-14
===================
* Add `statusCode` property on `Error`s, in addition to `status`
* Change `type` default to `application/json` for JSON parser
* Change `type` default to `application/x-www-form-urlencoded` for urlencoded parser
* Provide static `require` analysis
* Use the `http-errors` module to generate errors
* deps: bytes@2.1.0
- Slight optimizations
* deps: iconv-lite@0.4.10
- The encoding UTF-16 without BOM now defaults to UTF-16LE when detection fails
- Leading BOM is now removed when decoding
* deps: on-finished@~2.3.0
- Add defined behavior for HTTP `CONNECT` requests
- Add defined behavior for HTTP `Upgrade` requests
- deps: ee-first@1.1.1
* deps: qs@3.1.0
- Fix dropping parameters like `hasOwnProperty`
- Fix various parsing edge cases
- Parsed object now has `null` prototype
* deps: raw-body@~2.1.1
- Use `unpipe` module for unpiping requests
- deps: iconv-lite@0.4.10
* deps: type-is@~1.6.3
- deps: mime-types@~2.1.1
- perf: reduce try block size
- perf: remove bitwise operations
* perf: enable strict mode
* perf: remove argument reassignment
* perf: remove delete call
1.12.4 / 2015-05-10
===================
* deps: debug@~2.2.0
* deps: qs@2.4.2
- Fix allowing parameters like `constructor`
* deps: on-finished@~2.2.1
* deps: raw-body@~2.0.1
- Fix a false-positive when unpiping in Node.js 0.8
- deps: bytes@2.0.1
* deps: type-is@~1.6.2
- deps: mime-types@~2.0.11
1.12.3 / 2015-04-15
===================
* Slight efficiency improvement when not debugging
* deps: depd@~1.0.1
* deps: iconv-lite@0.4.8
- Add encoding alias UNICODE-1-1-UTF-7
* deps: raw-body@1.3.4
- Fix hanging callback if request aborts during read
- deps: iconv-lite@0.4.8
1.12.2 / 2015-03-16
===================
* deps: qs@2.4.1
- Fix error when parameter `hasOwnProperty` is present
1.12.1 / 2015-03-15
===================
* deps: debug@~2.1.3
- Fix high intensity foreground color for bold
- deps: ms@0.7.0
* deps: type-is@~1.6.1
- deps: mime-types@~2.0.10
1.12.0 / 2015-02-13
===================
* add `debug` messages
* accept a function for the `type` option
* use `content-type` to parse `Content-Type` headers
* deps: iconv-lite@0.4.7
- Gracefully support enumerables on `Object.prototype`
* deps: raw-body@1.3.3
- deps: iconv-lite@0.4.7
* deps: type-is@~1.6.0
- fix argument reassignment
- fix false-positives in `hasBody` `Transfer-Encoding` check
- support wildcard for both type and subtype (`*/*`)
- deps: mime-types@~2.0.9
1.11.0 / 2015-01-30
===================
* make internal `extended: true` depth limit infinity
* deps: type-is@~1.5.6
- deps: mime-types@~2.0.8
1.10.2 / 2015-01-20
===================
* deps: iconv-lite@0.4.6
- Fix rare aliases of single-byte encodings
* deps: raw-body@1.3.2
- deps: iconv-lite@0.4.6
1.10.1 / 2015-01-01
===================
* deps: on-finished@~2.2.0
* deps: type-is@~1.5.5
- deps: mime-types@~2.0.7
1.10.0 / 2014-12-02
===================
* make internal `extended: true` array limit dynamic
1.9.3 / 2014-11-21
==================
* deps: iconv-lite@0.4.5
- Fix Windows-31J and X-SJIS encoding support
* deps: qs@2.3.3
- Fix `arrayLimit` behavior
* deps: raw-body@1.3.1
- deps: iconv-lite@0.4.5
* deps: type-is@~1.5.3
- deps: mime-types@~2.0.3
1.9.2 / 2014-10-27
==================
* deps: qs@2.3.2
- Fix parsing of mixed objects and values
1.9.1 / 2014-10-22
==================
* deps: on-finished@~2.1.1
- Fix handling of pipelined requests
* deps: qs@2.3.0
- Fix parsing of mixed implicit and explicit arrays
* deps: type-is@~1.5.2
- deps: mime-types@~2.0.2
1.9.0 / 2014-09-24
==================
* include the charset in "unsupported charset" error message
* include the encoding in "unsupported content encoding" error message
* deps: depd@~1.0.0
1.8.4 / 2014-09-23
==================
* fix content encoding to be case-insensitive
1.8.3 / 2014-09-19
==================
* deps: qs@2.2.4
- Fix issue with object keys starting with numbers truncated
1.8.2 / 2014-09-15
==================
* deps: depd@0.4.5
1.8.1 / 2014-09-07
==================
* deps: media-typer@0.3.0
* deps: type-is@~1.5.1
1.8.0 / 2014-09-05
==================
* make empty-body-handling consistent between chunked requests
- empty `json` produces `{}`
- empty `raw` produces `new Buffer(0)`
- empty `text` produces `''`
- empty `urlencoded` produces `{}`
* deps: qs@2.2.3
- Fix issue where first empty value in array is discarded
* deps: type-is@~1.5.0
- fix `hasbody` to be true for `content-length: 0`
1.7.0 / 2014-09-01
==================
* add `parameterLimit` option to `urlencoded` parser
* change `urlencoded` extended array limit to 100
* respond with 413 when over `parameterLimit` in `urlencoded`
1.6.7 / 2014-08-29
==================
* deps: qs@2.2.2
- Remove unnecessary cloning
1.6.6 / 2014-08-27
==================
* deps: qs@2.2.0
- Array parsing fix
- Performance improvements
1.6.5 / 2014-08-16
==================
* deps: on-finished@2.1.0
1.6.4 / 2014-08-14
==================
* deps: qs@1.2.2
1.6.3 / 2014-08-10
==================
* deps: qs@1.2.1
1.6.2 / 2014-08-07
==================
* deps: qs@1.2.0
- Fix parsing array of objects
1.6.1 / 2014-08-06
==================
* deps: qs@1.1.0
- Accept urlencoded square brackets
- Accept empty values in implicit array notation
1.6.0 / 2014-08-05
==================
* deps: qs@1.0.2
- Complete rewrite
- Limits array length to 20
- Limits object depth to 5
- Limits parameters to 1,000
1.5.2 / 2014-07-27
==================
* deps: depd@0.4.4
- Work-around v8 generating empty stack traces
1.5.1 / 2014-07-26
==================
* deps: depd@0.4.3
- Fix exception when global `Error.stackTraceLimit` is too low
1.5.0 / 2014-07-20
==================
* deps: depd@0.4.2
- Add `TRACE_DEPRECATION` environment variable
- Remove non-standard grey color from color output
- Support `--no-deprecation` argument
- Support `--trace-deprecation` argument
* deps: iconv-lite@0.4.4
- Added encoding UTF-7
* deps: raw-body@1.3.0
- deps: iconv-lite@0.4.4
- Added encoding UTF-7
- Fix `Cannot switch to old mode now` error on Node.js 0.10+
* deps: type-is@~1.3.2
1.4.3 / 2014-06-19
==================
* deps: type-is@1.3.1
- fix global variable leak
1.4.2 / 2014-06-19
==================
* deps: type-is@1.3.0
- improve type parsing
1.4.1 / 2014-06-19
==================
* fix urlencoded extended deprecation message
1.4.0 / 2014-06-19
==================
* add `text` parser
* add `raw` parser
* check accepted charset in content-type (accepts utf-8)
* check accepted encoding in content-encoding (accepts identity)
* deprecate `bodyParser()` middleware; use `.json()` and `.urlencoded()` as needed
* deprecate `urlencoded()` without provided `extended` option
* lazy-load urlencoded parsers
* parsers split into files for reduced mem usage
* support gzip and deflate bodies
- set `inflate: false` to turn off
* deps: raw-body@1.2.2
- Support all encodings from `iconv-lite`
1.3.1 / 2014-06-11
==================
* deps: type-is@1.2.1
- Switch dependency from mime to mime-types@1.0.0
1.3.0 / 2014-05-31
==================
* add `extended` option to urlencoded parser
1.2.2 / 2014-05-27
==================
* deps: raw-body@1.1.6
- assert stream encoding on node.js 0.8
- assert stream encoding on node.js < 0.10.6
- deps: bytes@1
1.2.1 / 2014-05-26
==================
* invoke `next(err)` after request fully read
- prevents hung responses and socket hang ups
1.2.0 / 2014-05-11
==================
* add `verify` option
* deps: type-is@1.2.0
- support suffix matching
1.1.2 / 2014-05-11
==================
* improve json parser speed
1.1.1 / 2014-05-11
==================
* fix repeated limit parsing with every request
1.1.0 / 2014-05-10
==================
* add `type` option
* deps: pin for safety and consistency
1.0.2 / 2014-04-14
==================
* use `type-is` module
1.0.1 / 2014-03-20
==================
* lower default limits to 100kb

23
node_modules/express/node_modules/body-parser/LICENSE generated vendored Normal file
View file

@ -0,0 +1,23 @@
(The MIT License)
Copyright (c) 2014 Jonathan Ong <me@jongleberry.com>
Copyright (c) 2014-2015 Douglas Christopher Wilson <doug@somethingdoug.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

464
node_modules/express/node_modules/body-parser/README.md generated vendored Normal file
View file

@ -0,0 +1,464 @@
# body-parser
[![NPM Version][npm-image]][npm-url]
[![NPM Downloads][downloads-image]][downloads-url]
[![Build Status][github-actions-ci-image]][github-actions-ci-url]
[![Test Coverage][coveralls-image]][coveralls-url]
Node.js body parsing middleware.
Parse incoming request bodies in a middleware before your handlers, available
under the `req.body` property.
**Note** As `req.body`'s shape is based on user-controlled input, all
properties and values in this object are untrusted and should be validated
before trusting. For example, `req.body.foo.toString()` may fail in multiple
ways, for example the `foo` property may not be there or may not be a string,
and `toString` may not be a function and instead a string or other user input.
[Learn about the anatomy of an HTTP transaction in Node.js](https://nodejs.org/en/docs/guides/anatomy-of-an-http-transaction/).
_This does not handle multipart bodies_, due to their complex and typically
large nature. For multipart bodies, you may be interested in the following
modules:
* [busboy](https://www.npmjs.org/package/busboy#readme) and
[connect-busboy](https://www.npmjs.org/package/connect-busboy#readme)
* [multiparty](https://www.npmjs.org/package/multiparty#readme) and
[connect-multiparty](https://www.npmjs.org/package/connect-multiparty#readme)
* [formidable](https://www.npmjs.org/package/formidable#readme)
* [multer](https://www.npmjs.org/package/multer#readme)
This module provides the following parsers:
* [JSON body parser](#bodyparserjsonoptions)
* [Raw body parser](#bodyparserrawoptions)
* [Text body parser](#bodyparsertextoptions)
* [URL-encoded form body parser](#bodyparserurlencodedoptions)
Other body parsers you might be interested in:
- [body](https://www.npmjs.org/package/body#readme)
- [co-body](https://www.npmjs.org/package/co-body#readme)
## Installation
```sh
$ npm install body-parser
```
## API
```js
var bodyParser = require('body-parser')
```
The `bodyParser` object exposes various factories to create middlewares. All
middlewares will populate the `req.body` property with the parsed body when
the `Content-Type` request header matches the `type` option, or an empty
object (`{}`) if there was no body to parse, the `Content-Type` was not matched,
or an error occurred.
The various errors returned by this module are described in the
[errors section](#errors).
### bodyParser.json([options])
Returns middleware that only parses `json` and only looks at requests where
the `Content-Type` header matches the `type` option. This parser accepts any
Unicode encoding of the body and supports automatic inflation of `gzip` and
`deflate` encodings.
A new `body` object containing the parsed data is populated on the `request`
object after the middleware (i.e. `req.body`).
#### Options
The `json` function takes an optional `options` object that may contain any of
the following keys:
##### inflate
When set to `true`, then deflated (compressed) bodies will be inflated; when
`false`, deflated bodies are rejected. Defaults to `true`.
##### limit
Controls the maximum request body size. If this is a number, then the value
specifies the number of bytes; if it is a string, the value is passed to the
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
to `'100kb'`.
##### reviver
The `reviver` option is passed directly to `JSON.parse` as the second
argument. You can find more information on this argument
[in the MDN documentation about JSON.parse](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#Example.3A_Using_the_reviver_parameter).
##### strict
When set to `true`, will only accept arrays and objects; when `false` will
accept anything `JSON.parse` accepts. Defaults to `true`.
##### type
The `type` option is used to determine what media type the middleware will
parse. This option can be a string, array of strings, or a function. If not a
function, `type` option is passed directly to the
[type-is](https://www.npmjs.org/package/type-is#readme) library and this can
be an extension name (like `json`), a mime type (like `application/json`), or
a mime type with a wildcard (like `*/*` or `*/json`). If a function, the `type`
option is called as `fn(req)` and the request is parsed if it returns a truthy
value. Defaults to `application/json`.
##### verify
The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`,
where `buf` is a `Buffer` of the raw request body and `encoding` is the
encoding of the request. The parsing can be aborted by throwing an error.
### bodyParser.raw([options])
Returns middleware that parses all bodies as a `Buffer` and only looks at
requests where the `Content-Type` header matches the `type` option. This
parser supports automatic inflation of `gzip` and `deflate` encodings.
A new `body` object containing the parsed data is populated on the `request`
object after the middleware (i.e. `req.body`). This will be a `Buffer` object
of the body.
#### Options
The `raw` function takes an optional `options` object that may contain any of
the following keys:
##### inflate
When set to `true`, then deflated (compressed) bodies will be inflated; when
`false`, deflated bodies are rejected. Defaults to `true`.
##### limit
Controls the maximum request body size. If this is a number, then the value
specifies the number of bytes; if it is a string, the value is passed to the
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
to `'100kb'`.
##### type
The `type` option is used to determine what media type the middleware will
parse. This option can be a string, array of strings, or a function.
If not a function, `type` option is passed directly to the
[type-is](https://www.npmjs.org/package/type-is#readme) library and this
can be an extension name (like `bin`), a mime type (like
`application/octet-stream`), or a mime type with a wildcard (like `*/*` or
`application/*`). If a function, the `type` option is called as `fn(req)`
and the request is parsed if it returns a truthy value. Defaults to
`application/octet-stream`.
##### verify
The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`,
where `buf` is a `Buffer` of the raw request body and `encoding` is the
encoding of the request. The parsing can be aborted by throwing an error.
### bodyParser.text([options])
Returns middleware that parses all bodies as a string and only looks at
requests where the `Content-Type` header matches the `type` option. This
parser supports automatic inflation of `gzip` and `deflate` encodings.
A new `body` string containing the parsed data is populated on the `request`
object after the middleware (i.e. `req.body`). This will be a string of the
body.
#### Options
The `text` function takes an optional `options` object that may contain any of
the following keys:
##### defaultCharset
Specify the default character set for the text content if the charset is not
specified in the `Content-Type` header of the request. Defaults to `utf-8`.
##### inflate
When set to `true`, then deflated (compressed) bodies will be inflated; when
`false`, deflated bodies are rejected. Defaults to `true`.
##### limit
Controls the maximum request body size. If this is a number, then the value
specifies the number of bytes; if it is a string, the value is passed to the
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
to `'100kb'`.
##### type
The `type` option is used to determine what media type the middleware will
parse. This option can be a string, array of strings, or a function. If not
a function, `type` option is passed directly to the
[type-is](https://www.npmjs.org/package/type-is#readme) library and this can
be an extension name (like `txt`), a mime type (like `text/plain`), or a mime
type with a wildcard (like `*/*` or `text/*`). If a function, the `type`
option is called as `fn(req)` and the request is parsed if it returns a
truthy value. Defaults to `text/plain`.
##### verify
The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`,
where `buf` is a `Buffer` of the raw request body and `encoding` is the
encoding of the request. The parsing can be aborted by throwing an error.
### bodyParser.urlencoded([options])
Returns middleware that only parses `urlencoded` bodies and only looks at
requests where the `Content-Type` header matches the `type` option. This
parser accepts only UTF-8 encoding of the body and supports automatic
inflation of `gzip` and `deflate` encodings.
A new `body` object containing the parsed data is populated on the `request`
object after the middleware (i.e. `req.body`). This object will contain
key-value pairs, where the value can be a string or array (when `extended` is
`false`), or any type (when `extended` is `true`).
#### Options
The `urlencoded` function takes an optional `options` object that may contain
any of the following keys:
##### extended
The `extended` option allows to choose between parsing the URL-encoded data
with the `querystring` library (when `false`) or the `qs` library (when
`true`). The "extended" syntax allows for rich objects and arrays to be
encoded into the URL-encoded format, allowing for a JSON-like experience
with URL-encoded. For more information, please
[see the qs library](https://www.npmjs.org/package/qs#readme).
Defaults to `true`, but using the default has been deprecated. Please
research into the difference between `qs` and `querystring` and choose the
appropriate setting.
##### inflate
When set to `true`, then deflated (compressed) bodies will be inflated; when
`false`, deflated bodies are rejected. Defaults to `true`.
##### limit
Controls the maximum request body size. If this is a number, then the value
specifies the number of bytes; if it is a string, the value is passed to the
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
to `'100kb'`.
##### parameterLimit
The `parameterLimit` option controls the maximum number of parameters that
are allowed in the URL-encoded data. If a request contains more parameters
than this value, a 413 will be returned to the client. Defaults to `1000`.
##### type
The `type` option is used to determine what media type the middleware will
parse. This option can be a string, array of strings, or a function. If not
a function, `type` option is passed directly to the
[type-is](https://www.npmjs.org/package/type-is#readme) library and this can
be an extension name (like `urlencoded`), a mime type (like
`application/x-www-form-urlencoded`), or a mime type with a wildcard (like
`*/x-www-form-urlencoded`). If a function, the `type` option is called as
`fn(req)` and the request is parsed if it returns a truthy value. Defaults
to `application/x-www-form-urlencoded`.
##### verify
The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`,
where `buf` is a `Buffer` of the raw request body and `encoding` is the
encoding of the request. The parsing can be aborted by throwing an error.
## Errors
The middlewares provided by this module create errors using the
[`http-errors` module](https://www.npmjs.com/package/http-errors). The errors
will typically have a `status`/`statusCode` property that contains the suggested
HTTP response code, an `expose` property to determine if the `message` property
should be displayed to the client, a `type` property to determine the type of
error without matching against the `message`, and a `body` property containing
the read body, if available.
The following are the common errors created, though any error can come through
for various reasons.
### content encoding unsupported
This error will occur when the request had a `Content-Encoding` header that
contained an encoding but the "inflation" option was set to `false`. The
`status` property is set to `415`, the `type` property is set to
`'encoding.unsupported'`, and the `charset` property will be set to the
encoding that is unsupported.
### entity parse failed
This error will occur when the request contained an entity that could not be
parsed by the middleware. The `status` property is set to `400`, the `type`
property is set to `'entity.parse.failed'`, and the `body` property is set to
the entity value that failed parsing.
### entity verify failed
This error will occur when the request contained an entity that could not be
failed verification by the defined `verify` option. The `status` property is
set to `403`, the `type` property is set to `'entity.verify.failed'`, and the
`body` property is set to the entity value that failed verification.
### request aborted
This error will occur when the request is aborted by the client before reading
the body has finished. The `received` property will be set to the number of
bytes received before the request was aborted and the `expected` property is
set to the number of expected bytes. The `status` property is set to `400`
and `type` property is set to `'request.aborted'`.
### request entity too large
This error will occur when the request body's size is larger than the "limit"
option. The `limit` property will be set to the byte limit and the `length`
property will be set to the request body's length. The `status` property is
set to `413` and the `type` property is set to `'entity.too.large'`.
### request size did not match content length
This error will occur when the request's length did not match the length from
the `Content-Length` header. This typically occurs when the request is malformed,
typically when the `Content-Length` header was calculated based on characters
instead of bytes. The `status` property is set to `400` and the `type` property
is set to `'request.size.invalid'`.
### stream encoding should not be set
This error will occur when something called the `req.setEncoding` method prior
to this middleware. This module operates directly on bytes only and you cannot
call `req.setEncoding` when using this module. The `status` property is set to
`500` and the `type` property is set to `'stream.encoding.set'`.
### stream is not readable
This error will occur when the request is no longer readable when this middleware
attempts to read it. This typically means something other than a middleware from
this module read the request body already and the middleware was also configured to
read the same request. The `status` property is set to `500` and the `type`
property is set to `'stream.not.readable'`.
### too many parameters
This error will occur when the content of the request exceeds the configured
`parameterLimit` for the `urlencoded` parser. The `status` property is set to
`413` and the `type` property is set to `'parameters.too.many'`.
### unsupported charset "BOGUS"
This error will occur when the request had a charset parameter in the
`Content-Type` header, but the `iconv-lite` module does not support it OR the
parser does not support it. The charset is contained in the message as well
as in the `charset` property. The `status` property is set to `415`, the
`type` property is set to `'charset.unsupported'`, and the `charset` property
is set to the charset that is unsupported.
### unsupported content encoding "bogus"
This error will occur when the request had a `Content-Encoding` header that
contained an unsupported encoding. The encoding is contained in the message
as well as in the `encoding` property. The `status` property is set to `415`,
the `type` property is set to `'encoding.unsupported'`, and the `encoding`
property is set to the encoding that is unsupported.
## Examples
### Express/Connect top-level generic
This example demonstrates adding a generic JSON and URL-encoded parser as a
top-level middleware, which will parse the bodies of all incoming requests.
This is the simplest setup.
```js
var express = require('express')
var bodyParser = require('body-parser')
var app = express()
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())
app.use(function (req, res) {
res.setHeader('Content-Type', 'text/plain')
res.write('you posted:\n')
res.end(JSON.stringify(req.body, null, 2))
})
```
### Express route-specific
This example demonstrates adding body parsers specifically to the routes that
need them. In general, this is the most recommended way to use body-parser with
Express.
```js
var express = require('express')
var bodyParser = require('body-parser')
var app = express()
// create application/json parser
var jsonParser = bodyParser.json()
// create application/x-www-form-urlencoded parser
var urlencodedParser = bodyParser.urlencoded({ extended: false })
// POST /login gets urlencoded bodies
app.post('/login', urlencodedParser, function (req, res) {
res.send('welcome, ' + req.body.username)
})
// POST /api/users gets JSON bodies
app.post('/api/users', jsonParser, function (req, res) {
// create user in req.body
})
```
### Change accepted type for parsers
All the parsers accept a `type` option which allows you to change the
`Content-Type` that the middleware will parse.
```js
var express = require('express')
var bodyParser = require('body-parser')
var app = express()
// parse various different custom JSON types as JSON
app.use(bodyParser.json({ type: 'application/*+json' }))
// parse some custom thing into a Buffer
app.use(bodyParser.raw({ type: 'application/vnd.custom-type' }))
// parse an HTML body into a string
app.use(bodyParser.text({ type: 'text/html' }))
```
## License
[MIT](LICENSE)
[npm-image]: https://img.shields.io/npm/v/body-parser.svg
[npm-url]: https://npmjs.org/package/body-parser
[coveralls-image]: https://img.shields.io/coveralls/expressjs/body-parser/master.svg
[coveralls-url]: https://coveralls.io/r/expressjs/body-parser?branch=master
[downloads-image]: https://img.shields.io/npm/dm/body-parser.svg
[downloads-url]: https://npmjs.org/package/body-parser
[github-actions-ci-image]: https://img.shields.io/github/workflow/status/expressjs/body-parser/ci/master?label=ci
[github-actions-ci-url]: https://github.com/expressjs/body-parser/actions/workflows/ci.yml

View file

@ -0,0 +1,25 @@
# Security Policies and Procedures
## Reporting a Bug
The Express team and community take all security bugs seriously. Thank you
for improving the security of Express. We appreciate your efforts and
responsible disclosure and will make every effort to acknowledge your
contributions.
Report security bugs by emailing the current owner(s) of `body-parser`. This
information can be found in the npm registry using the command
`npm owner ls body-parser`.
If unsure or unable to get the information from the above, open an issue
in the [project issue tracker](https://github.com/expressjs/body-parser/issues)
asking for the current contact information.
To ensure the timely response to your report, please ensure that the entirety
of the report is contained within the email body and not solely behind a web
link or an attachment.
At least one owner will acknowledge your email within 48 hours, and will send a
more detailed response within 48 hours indicating the next steps in handling
your report. After the initial reply to your report, the owners will
endeavor to keep you informed of the progress towards a fix and full
announcement, and may ask for additional information or guidance.

156
node_modules/express/node_modules/body-parser/index.js generated vendored Normal file
View file

@ -0,0 +1,156 @@
/*!
* body-parser
* Copyright(c) 2014-2015 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict'
/**
* Module dependencies.
* @private
*/
var deprecate = require('depd')('body-parser')
/**
* Cache of loaded parsers.
* @private
*/
var parsers = Object.create(null)
/**
* @typedef Parsers
* @type {function}
* @property {function} json
* @property {function} raw
* @property {function} text
* @property {function} urlencoded
*/
/**
* Module exports.
* @type {Parsers}
*/
exports = module.exports = deprecate.function(bodyParser,
'bodyParser: use individual json/urlencoded middlewares')
/**
* JSON parser.
* @public
*/
Object.defineProperty(exports, 'json', {
configurable: true,
enumerable: true,
get: createParserGetter('json')
})
/**
* Raw parser.
* @public
*/
Object.defineProperty(exports, 'raw', {
configurable: true,
enumerable: true,
get: createParserGetter('raw')
})
/**
* Text parser.
* @public
*/
Object.defineProperty(exports, 'text', {
configurable: true,
enumerable: true,
get: createParserGetter('text')
})
/**
* URL-encoded parser.
* @public
*/
Object.defineProperty(exports, 'urlencoded', {
configurable: true,
enumerable: true,
get: createParserGetter('urlencoded')
})
/**
* Create a middleware to parse json and urlencoded bodies.
*
* @param {object} [options]
* @return {function}
* @deprecated
* @public
*/
function bodyParser (options) {
// use default type for parsers
var opts = Object.create(options || null, {
type: {
configurable: true,
enumerable: true,
value: undefined,
writable: true
}
})
var _urlencoded = exports.urlencoded(opts)
var _json = exports.json(opts)
return function bodyParser (req, res, next) {
_json(req, res, function (err) {
if (err) return next(err)
_urlencoded(req, res, next)
})
}
}
/**
* Create a getter for loading a parser.
* @private
*/
function createParserGetter (name) {
return function get () {
return loadParser(name)
}
}
/**
* Load a parser module.
* @private
*/
function loadParser (parserName) {
var parser = parsers[parserName]
if (parser !== undefined) {
return parser
}
// this uses a switch for static require analysis
switch (parserName) {
case 'json':
parser = require('./lib/types/json')
break
case 'raw':
parser = require('./lib/types/raw')
break
case 'text':
parser = require('./lib/types/text')
break
case 'urlencoded':
parser = require('./lib/types/urlencoded')
break
}
// store to prevent invoking require()
return (parsers[parserName] = parser)
}

View file

@ -0,0 +1,205 @@
/*!
* body-parser
* Copyright(c) 2014-2015 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict'
/**
* Module dependencies.
* @private
*/
var createError = require('http-errors')
var destroy = require('destroy')
var getBody = require('raw-body')
var iconv = require('iconv-lite')
var onFinished = require('on-finished')
var unpipe = require('unpipe')
var zlib = require('zlib')
/**
* Module exports.
*/
module.exports = read
/**
* Read a request into a buffer and parse.
*
* @param {object} req
* @param {object} res
* @param {function} next
* @param {function} parse
* @param {function} debug
* @param {object} options
* @private
*/
function read (req, res, next, parse, debug, options) {
var length
var opts = options
var stream
// flag as parsed
req._body = true
// read options
var encoding = opts.encoding !== null
? opts.encoding
: null
var verify = opts.verify
try {
// get the content stream
stream = contentstream(req, debug, opts.inflate)
length = stream.length
stream.length = undefined
} catch (err) {
return next(err)
}
// set raw-body options
opts.length = length
opts.encoding = verify
? null
: encoding
// assert charset is supported
if (opts.encoding === null && encoding !== null && !iconv.encodingExists(encoding)) {
return next(createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
charset: encoding.toLowerCase(),
type: 'charset.unsupported'
}))
}
// read body
debug('read body')
getBody(stream, opts, function (error, body) {
if (error) {
var _error
if (error.type === 'encoding.unsupported') {
// echo back charset
_error = createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
charset: encoding.toLowerCase(),
type: 'charset.unsupported'
})
} else {
// set status code on error
_error = createError(400, error)
}
// unpipe from stream and destroy
if (stream !== req) {
unpipe(req)
destroy(stream, true)
}
// read off entire request
dump(req, function onfinished () {
next(createError(400, _error))
})
return
}
// verify
if (verify) {
try {
debug('verify body')
verify(req, res, body, encoding)
} catch (err) {
next(createError(403, err, {
body: body,
type: err.type || 'entity.verify.failed'
}))
return
}
}
// parse
var str = body
try {
debug('parse body')
str = typeof body !== 'string' && encoding !== null
? iconv.decode(body, encoding)
: body
req.body = parse(str)
} catch (err) {
next(createError(400, err, {
body: str,
type: err.type || 'entity.parse.failed'
}))
return
}
next()
})
}
/**
* Get the content stream of the request.
*
* @param {object} req
* @param {function} debug
* @param {boolean} [inflate=true]
* @return {object}
* @api private
*/
function contentstream (req, debug, inflate) {
var encoding = (req.headers['content-encoding'] || 'identity').toLowerCase()
var length = req.headers['content-length']
var stream
debug('content-encoding "%s"', encoding)
if (inflate === false && encoding !== 'identity') {
throw createError(415, 'content encoding unsupported', {
encoding: encoding,
type: 'encoding.unsupported'
})
}
switch (encoding) {
case 'deflate':
stream = zlib.createInflate()
debug('inflate body')
req.pipe(stream)
break
case 'gzip':
stream = zlib.createGunzip()
debug('gunzip body')
req.pipe(stream)
break
case 'identity':
stream = req
stream.length = length
break
default:
throw createError(415, 'unsupported content encoding "' + encoding + '"', {
encoding: encoding,
type: 'encoding.unsupported'
})
}
return stream
}
/**
* Dump the contents of a request.
*
* @param {object} req
* @param {function} callback
* @api private
*/
function dump (req, callback) {
if (onFinished.isFinished(req)) {
callback(null)
} else {
onFinished(req, callback)
req.resume()
}
}

View file

@ -0,0 +1,236 @@
/*!
* body-parser
* Copyright(c) 2014 Jonathan Ong
* Copyright(c) 2014-2015 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict'
/**
* Module dependencies.
* @private
*/
var bytes = require('bytes')
var contentType = require('content-type')
var createError = require('http-errors')
var debug = require('debug')('body-parser:json')
var read = require('../read')
var typeis = require('type-is')
/**
* Module exports.
*/
module.exports = json
/**
* RegExp to match the first non-space in a string.
*
* Allowed whitespace is defined in RFC 7159:
*
* ws = *(
* %x20 / ; Space
* %x09 / ; Horizontal tab
* %x0A / ; Line feed or New line
* %x0D ) ; Carriage return
*/
var FIRST_CHAR_REGEXP = /^[\x20\x09\x0a\x0d]*([^\x20\x09\x0a\x0d])/ // eslint-disable-line no-control-regex
/**
* Create a middleware to parse JSON bodies.
*
* @param {object} [options]
* @return {function}
* @public
*/
function json (options) {
var opts = options || {}
var limit = typeof opts.limit !== 'number'
? bytes.parse(opts.limit || '100kb')
: opts.limit
var inflate = opts.inflate !== false
var reviver = opts.reviver
var strict = opts.strict !== false
var type = opts.type || 'application/json'
var verify = opts.verify || false
if (verify !== false && typeof verify !== 'function') {
throw new TypeError('option verify must be function')
}
// create the appropriate type checking function
var shouldParse = typeof type !== 'function'
? typeChecker(type)
: type
function parse (body) {
if (body.length === 0) {
// special-case empty json body, as it's a common client-side mistake
// TODO: maybe make this configurable or part of "strict" option
return {}
}
if (strict) {
var first = firstchar(body)
if (first !== '{' && first !== '[') {
debug('strict violation')
throw createStrictSyntaxError(body, first)
}
}
try {
debug('parse json')
return JSON.parse(body, reviver)
} catch (e) {
throw normalizeJsonSyntaxError(e, {
message: e.message,
stack: e.stack
})
}
}
return function jsonParser (req, res, next) {
if (req._body) {
debug('body already parsed')
next()
return
}
req.body = req.body || {}
// skip requests without bodies
if (!typeis.hasBody(req)) {
debug('skip empty body')
next()
return
}
debug('content-type %j', req.headers['content-type'])
// determine if request should be parsed
if (!shouldParse(req)) {
debug('skip parsing')
next()
return
}
// assert charset per RFC 7159 sec 8.1
var charset = getCharset(req) || 'utf-8'
if (charset.slice(0, 4) !== 'utf-') {
debug('invalid charset')
next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', {
charset: charset,
type: 'charset.unsupported'
}))
return
}
// read
read(req, res, next, parse, debug, {
encoding: charset,
inflate: inflate,
limit: limit,
verify: verify
})
}
}
/**
* Create strict violation syntax error matching native error.
*
* @param {string} str
* @param {string} char
* @return {Error}
* @private
*/
function createStrictSyntaxError (str, char) {
var index = str.indexOf(char)
var partial = index !== -1
? str.substring(0, index) + '#'
: ''
try {
JSON.parse(partial); /* istanbul ignore next */ throw new SyntaxError('strict violation')
} catch (e) {
return normalizeJsonSyntaxError(e, {
message: e.message.replace('#', char),
stack: e.stack
})
}
}
/**
* Get the first non-whitespace character in a string.
*
* @param {string} str
* @return {function}
* @private
*/
function firstchar (str) {
var match = FIRST_CHAR_REGEXP.exec(str)
return match
? match[1]
: undefined
}
/**
* Get the charset of a request.
*
* @param {object} req
* @api private
*/
function getCharset (req) {
try {
return (contentType.parse(req).parameters.charset || '').toLowerCase()
} catch (e) {
return undefined
}
}
/**
* Normalize a SyntaxError for JSON.parse.
*
* @param {SyntaxError} error
* @param {object} obj
* @return {SyntaxError}
*/
function normalizeJsonSyntaxError (error, obj) {
var keys = Object.getOwnPropertyNames(error)
for (var i = 0; i < keys.length; i++) {
var key = keys[i]
if (key !== 'stack' && key !== 'message') {
delete error[key]
}
}
// replace stack before message for Node.js 0.10 and below
error.stack = obj.stack.replace(error.message, obj.message)
error.message = obj.message
return error
}
/**
* Get the simple type checker.
*
* @param {string} type
* @return {function}
*/
function typeChecker (type) {
return function checkType (req) {
return Boolean(typeis(req, type))
}
}

View file

@ -0,0 +1,101 @@
/*!
* body-parser
* Copyright(c) 2014-2015 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict'
/**
* Module dependencies.
*/
var bytes = require('bytes')
var debug = require('debug')('body-parser:raw')
var read = require('../read')
var typeis = require('type-is')
/**
* Module exports.
*/
module.exports = raw
/**
* Create a middleware to parse raw bodies.
*
* @param {object} [options]
* @return {function}
* @api public
*/
function raw (options) {
var opts = options || {}
var inflate = opts.inflate !== false
var limit = typeof opts.limit !== 'number'
? bytes.parse(opts.limit || '100kb')
: opts.limit
var type = opts.type || 'application/octet-stream'
var verify = opts.verify || false
if (verify !== false && typeof verify !== 'function') {
throw new TypeError('option verify must be function')
}
// create the appropriate type checking function
var shouldParse = typeof type !== 'function'
? typeChecker(type)
: type
function parse (buf) {
return buf
}
return function rawParser (req, res, next) {
if (req._body) {
debug('body already parsed')
next()
return
}
req.body = req.body || {}
// skip requests without bodies
if (!typeis.hasBody(req)) {
debug('skip empty body')
next()
return
}
debug('content-type %j', req.headers['content-type'])
// determine if request should be parsed
if (!shouldParse(req)) {
debug('skip parsing')
next()
return
}
// read
read(req, res, next, parse, debug, {
encoding: null,
inflate: inflate,
limit: limit,
verify: verify
})
}
}
/**
* Get the simple type checker.
*
* @param {string} type
* @return {function}
*/
function typeChecker (type) {
return function checkType (req) {
return Boolean(typeis(req, type))
}
}

View file

@ -0,0 +1,121 @@
/*!
* body-parser
* Copyright(c) 2014-2015 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict'
/**
* Module dependencies.
*/
var bytes = require('bytes')
var contentType = require('content-type')
var debug = require('debug')('body-parser:text')
var read = require('../read')
var typeis = require('type-is')
/**
* Module exports.
*/
module.exports = text
/**
* Create a middleware to parse text bodies.
*
* @param {object} [options]
* @return {function}
* @api public
*/
function text (options) {
var opts = options || {}
var defaultCharset = opts.defaultCharset || 'utf-8'
var inflate = opts.inflate !== false
var limit = typeof opts.limit !== 'number'
? bytes.parse(opts.limit || '100kb')
: opts.limit
var type = opts.type || 'text/plain'
var verify = opts.verify || false
if (verify !== false && typeof verify !== 'function') {
throw new TypeError('option verify must be function')
}
// create the appropriate type checking function
var shouldParse = typeof type !== 'function'
? typeChecker(type)
: type
function parse (buf) {
return buf
}
return function textParser (req, res, next) {
if (req._body) {
debug('body already parsed')
next()
return
}
req.body = req.body || {}
// skip requests without bodies
if (!typeis.hasBody(req)) {
debug('skip empty body')
next()
return
}
debug('content-type %j', req.headers['content-type'])
// determine if request should be parsed
if (!shouldParse(req)) {
debug('skip parsing')
next()
return
}
// get charset
var charset = getCharset(req) || defaultCharset
// read
read(req, res, next, parse, debug, {
encoding: charset,
inflate: inflate,
limit: limit,
verify: verify
})
}
}
/**
* Get the charset of a request.
*
* @param {object} req
* @api private
*/
function getCharset (req) {
try {
return (contentType.parse(req).parameters.charset || '').toLowerCase()
} catch (e) {
return undefined
}
}
/**
* Get the simple type checker.
*
* @param {string} type
* @return {function}
*/
function typeChecker (type) {
return function checkType (req) {
return Boolean(typeis(req, type))
}
}

View file

@ -0,0 +1,284 @@
/*!
* body-parser
* Copyright(c) 2014 Jonathan Ong
* Copyright(c) 2014-2015 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict'
/**
* Module dependencies.
* @private
*/
var bytes = require('bytes')
var contentType = require('content-type')
var createError = require('http-errors')
var debug = require('debug')('body-parser:urlencoded')
var deprecate = require('depd')('body-parser')
var read = require('../read')
var typeis = require('type-is')
/**
* Module exports.
*/
module.exports = urlencoded
/**
* Cache of parser modules.
*/
var parsers = Object.create(null)
/**
* Create a middleware to parse urlencoded bodies.
*
* @param {object} [options]
* @return {function}
* @public
*/
function urlencoded (options) {
var opts = options || {}
// notice because option default will flip in next major
if (opts.extended === undefined) {
deprecate('undefined extended: provide extended option')
}
var extended = opts.extended !== false
var inflate = opts.inflate !== false
var limit = typeof opts.limit !== 'number'
? bytes.parse(opts.limit || '100kb')
: opts.limit
var type = opts.type || 'application/x-www-form-urlencoded'
var verify = opts.verify || false
if (verify !== false && typeof verify !== 'function') {
throw new TypeError('option verify must be function')
}
// create the appropriate query parser
var queryparse = extended
? extendedparser(opts)
: simpleparser(opts)
// create the appropriate type checking function
var shouldParse = typeof type !== 'function'
? typeChecker(type)
: type
function parse (body) {
return body.length
? queryparse(body)
: {}
}
return function urlencodedParser (req, res, next) {
if (req._body) {
debug('body already parsed')
next()
return
}
req.body = req.body || {}
// skip requests without bodies
if (!typeis.hasBody(req)) {
debug('skip empty body')
next()
return
}
debug('content-type %j', req.headers['content-type'])
// determine if request should be parsed
if (!shouldParse(req)) {
debug('skip parsing')
next()
return
}
// assert charset
var charset = getCharset(req) || 'utf-8'
if (charset !== 'utf-8') {
debug('invalid charset')
next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', {
charset: charset,
type: 'charset.unsupported'
}))
return
}
// read
read(req, res, next, parse, debug, {
debug: debug,
encoding: charset,
inflate: inflate,
limit: limit,
verify: verify
})
}
}
/**
* Get the extended query parser.
*
* @param {object} options
*/
function extendedparser (options) {
var parameterLimit = options.parameterLimit !== undefined
? options.parameterLimit
: 1000
var parse = parser('qs')
if (isNaN(parameterLimit) || parameterLimit < 1) {
throw new TypeError('option parameterLimit must be a positive number')
}
if (isFinite(parameterLimit)) {
parameterLimit = parameterLimit | 0
}
return function queryparse (body) {
var paramCount = parameterCount(body, parameterLimit)
if (paramCount === undefined) {
debug('too many parameters')
throw createError(413, 'too many parameters', {
type: 'parameters.too.many'
})
}
var arrayLimit = Math.max(100, paramCount)
debug('parse extended urlencoding')
return parse(body, {
allowPrototypes: true,
arrayLimit: arrayLimit,
depth: Infinity,
parameterLimit: parameterLimit
})
}
}
/**
* Get the charset of a request.
*
* @param {object} req
* @api private
*/
function getCharset (req) {
try {
return (contentType.parse(req).parameters.charset || '').toLowerCase()
} catch (e) {
return undefined
}
}
/**
* Count the number of parameters, stopping once limit reached
*
* @param {string} body
* @param {number} limit
* @api private
*/
function parameterCount (body, limit) {
var count = 0
var index = 0
while ((index = body.indexOf('&', index)) !== -1) {
count++
index++
if (count === limit) {
return undefined
}
}
return count
}
/**
* Get parser for module name dynamically.
*
* @param {string} name
* @return {function}
* @api private
*/
function parser (name) {
var mod = parsers[name]
if (mod !== undefined) {
return mod.parse
}
// this uses a switch for static require analysis
switch (name) {
case 'qs':
mod = require('qs')
break
case 'querystring':
mod = require('querystring')
break
}
// store to prevent invoking require()
parsers[name] = mod
return mod.parse
}
/**
* Get the simple query parser.
*
* @param {object} options
*/
function simpleparser (options) {
var parameterLimit = options.parameterLimit !== undefined
? options.parameterLimit
: 1000
var parse = parser('querystring')
if (isNaN(parameterLimit) || parameterLimit < 1) {
throw new TypeError('option parameterLimit must be a positive number')
}
if (isFinite(parameterLimit)) {
parameterLimit = parameterLimit | 0
}
return function queryparse (body) {
var paramCount = parameterCount(body, parameterLimit)
if (paramCount === undefined) {
debug('too many parameters')
throw createError(413, 'too many parameters', {
type: 'parameters.too.many'
})
}
debug('parse urlencoding')
return parse(body, undefined, undefined, { maxKeys: parameterLimit })
}
}
/**
* Get the simple type checker.
*
* @param {string} type
* @return {function}
*/
function typeChecker (type) {
return function checkType (req) {
return Boolean(typeis(req, type))
}
}

View file

@ -0,0 +1,56 @@
{
"name": "body-parser",
"description": "Node.js body parsing middleware",
"version": "1.20.1",
"contributors": [
"Douglas Christopher Wilson <doug@somethingdoug.com>",
"Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)"
],
"license": "MIT",
"repository": "expressjs/body-parser",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"devDependencies": {
"eslint": "8.24.0",
"eslint-config-standard": "14.1.1",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-markdown": "3.0.0",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-promise": "6.0.1",
"eslint-plugin-standard": "4.1.0",
"methods": "1.1.2",
"mocha": "10.0.0",
"nyc": "15.1.0",
"safe-buffer": "5.2.1",
"supertest": "6.3.0"
},
"files": [
"lib/",
"LICENSE",
"HISTORY.md",
"SECURITY.md",
"index.js"
],
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
},
"scripts": {
"lint": "eslint .",
"test": "mocha --require test/support/env --reporter spec --check-leaks --bail test/",
"test-ci": "nyc --reporter=lcov --reporter=text npm test",
"test-cov": "nyc --reporter=html --reporter=text npm test"
}
}

303
node_modules/express/node_modules/raw-body/HISTORY.md generated vendored Normal file
View file

@ -0,0 +1,303 @@
2.5.1 / 2022-02-28
==================
* Fix error on early async hooks implementations
2.5.0 / 2022-02-21
==================
* Prevent loss of async hooks context
* Prevent hanging when stream is not readable
* deps: http-errors@2.0.0
- deps: depd@2.0.0
- deps: statuses@2.0.1
2.4.3 / 2022-02-14
==================
* deps: bytes@3.1.2
2.4.2 / 2021-11-16
==================
* deps: bytes@3.1.1
* deps: http-errors@1.8.1
- deps: setprototypeof@1.2.0
- deps: toidentifier@1.0.1
2.4.1 / 2019-06-25
==================
* deps: http-errors@1.7.3
- deps: inherits@2.0.4
2.4.0 / 2019-04-17
==================
* deps: bytes@3.1.0
- Add petabyte (`pb`) support
* deps: http-errors@1.7.2
- Set constructor name when possible
- deps: setprototypeof@1.1.1
- deps: statuses@'>= 1.5.0 < 2'
* deps: iconv-lite@0.4.24
- Added encoding MIK
2.3.3 / 2018-05-08
==================
* deps: http-errors@1.6.3
- deps: depd@~1.1.2
- deps: setprototypeof@1.1.0
- deps: statuses@'>= 1.3.1 < 2'
* deps: iconv-lite@0.4.23
- Fix loading encoding with year appended
- Fix deprecation warnings on Node.js 10+
2.3.2 / 2017-09-09
==================
* deps: iconv-lite@0.4.19
- Fix ISO-8859-1 regression
- Update Windows-1255
2.3.1 / 2017-09-07
==================
* deps: bytes@3.0.0
* deps: http-errors@1.6.2
- deps: depd@1.1.1
* perf: skip buffer decoding on overage chunk
2.3.0 / 2017-08-04
==================
* Add TypeScript definitions
* Use `http-errors` for standard emitted errors
* deps: bytes@2.5.0
* deps: iconv-lite@0.4.18
- Add support for React Native
- Add a warning if not loaded as utf-8
- Fix CESU-8 decoding in Node.js 8
- Improve speed of ISO-8859-1 encoding
2.2.0 / 2017-01-02
==================
* deps: iconv-lite@0.4.15
- Added encoding MS-31J
- Added encoding MS-932
- Added encoding MS-936
- Added encoding MS-949
- Added encoding MS-950
- Fix GBK/GB18030 handling of Euro character
2.1.7 / 2016-06-19
==================
* deps: bytes@2.4.0
* perf: remove double-cleanup on happy path
2.1.6 / 2016-03-07
==================
* deps: bytes@2.3.0
- Drop partial bytes on all parsed units
- Fix parsing byte string that looks like hex
2.1.5 / 2015-11-30
==================
* deps: bytes@2.2.0
* deps: iconv-lite@0.4.13
2.1.4 / 2015-09-27
==================
* Fix masking critical errors from `iconv-lite`
* deps: iconv-lite@0.4.12
- Fix CESU-8 decoding in Node.js 4.x
2.1.3 / 2015-09-12
==================
* Fix sync callback when attaching data listener causes sync read
- Node.js 0.10 compatibility issue
2.1.2 / 2015-07-05
==================
* Fix error stack traces to skip `makeError`
* deps: iconv-lite@0.4.11
- Add encoding CESU-8
2.1.1 / 2015-06-14
==================
* Use `unpipe` module for unpiping requests
2.1.0 / 2015-05-28
==================
* deps: iconv-lite@0.4.10
- Improved UTF-16 endianness detection
- Leading BOM is now removed when decoding
- The encoding UTF-16 without BOM now defaults to UTF-16LE when detection fails
2.0.2 / 2015-05-21
==================
* deps: bytes@2.1.0
- Slight optimizations
2.0.1 / 2015-05-10
==================
* Fix a false-positive when unpiping in Node.js 0.8
2.0.0 / 2015-05-08
==================
* Return a promise without callback instead of thunk
* deps: bytes@2.0.1
- units no longer case sensitive when parsing
1.3.4 / 2015-04-15
==================
* Fix hanging callback if request aborts during read
* deps: iconv-lite@0.4.8
- Add encoding alias UNICODE-1-1-UTF-7
1.3.3 / 2015-02-08
==================
* deps: iconv-lite@0.4.7
- Gracefully support enumerables on `Object.prototype`
1.3.2 / 2015-01-20
==================
* deps: iconv-lite@0.4.6
- Fix rare aliases of single-byte encodings
1.3.1 / 2014-11-21
==================
* deps: iconv-lite@0.4.5
- Fix Windows-31J and X-SJIS encoding support
1.3.0 / 2014-07-20
==================
* Fully unpipe the stream on error
- Fixes `Cannot switch to old mode now` error on Node.js 0.10+
1.2.3 / 2014-07-20
==================
* deps: iconv-lite@0.4.4
- Added encoding UTF-7
1.2.2 / 2014-06-19
==================
* Send invalid encoding error to callback
1.2.1 / 2014-06-15
==================
* deps: iconv-lite@0.4.3
- Added encodings UTF-16BE and UTF-16 with BOM
1.2.0 / 2014-06-13
==================
* Passing string as `options` interpreted as encoding
* Support all encodings from `iconv-lite`
1.1.7 / 2014-06-12
==================
* use `string_decoder` module from npm
1.1.6 / 2014-05-27
==================
* check encoding for old streams1
* support node.js < 0.10.6
1.1.5 / 2014-05-14
==================
* bump bytes
1.1.4 / 2014-04-19
==================
* allow true as an option
* bump bytes
1.1.3 / 2014-03-02
==================
* fix case when length=null
1.1.2 / 2013-12-01
==================
* be less strict on state.encoding check
1.1.1 / 2013-11-27
==================
* add engines
1.1.0 / 2013-11-27
==================
* add err.statusCode and err.type
* allow for encoding option to be true
* pause the stream instead of dumping on error
* throw if the stream's encoding is set
1.0.1 / 2013-11-19
==================
* dont support streams1, throw if dev set encoding
1.0.0 / 2013-11-17
==================
* rename `expected` option to `length`
0.2.0 / 2013-11-15
==================
* republish
0.1.1 / 2013-11-15
==================
* use bytes
0.1.0 / 2013-11-11
==================
* generator support
0.0.3 / 2013-10-10
==================
* update repo
0.0.2 / 2013-09-14
==================
* dump stream on bad headers
* listen to events after defining received and buffers
0.0.1 / 2013-09-14
==================
* Initial release

22
node_modules/express/node_modules/raw-body/LICENSE generated vendored Normal file
View file

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2013-2014 Jonathan Ong <me@jongleberry.com>
Copyright (c) 2014-2022 Douglas Christopher Wilson <doug@somethingdoug.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

223
node_modules/express/node_modules/raw-body/README.md generated vendored Normal file
View file

@ -0,0 +1,223 @@
# raw-body
[![NPM Version][npm-image]][npm-url]
[![NPM Downloads][downloads-image]][downloads-url]
[![Node.js Version][node-version-image]][node-version-url]
[![Build status][github-actions-ci-image]][github-actions-ci-url]
[![Test coverage][coveralls-image]][coveralls-url]
Gets the entire buffer of a stream either as a `Buffer` or a string.
Validates the stream's length against an expected length and maximum limit.
Ideal for parsing request bodies.
## Install
This is a [Node.js](https://nodejs.org/en/) module available through the
[npm registry](https://www.npmjs.com/). Installation is done using the
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
```sh
$ npm install raw-body
```
### TypeScript
This module includes a [TypeScript](https://www.typescriptlang.org/)
declaration file to enable auto complete in compatible editors and type
information for TypeScript projects. This module depends on the Node.js
types, so install `@types/node`:
```sh
$ npm install @types/node
```
## API
```js
var getRawBody = require('raw-body')
```
### getRawBody(stream, [options], [callback])
**Returns a promise if no callback specified and global `Promise` exists.**
Options:
- `length` - The length of the stream.
If the contents of the stream do not add up to this length,
an `400` error code is returned.
- `limit` - The byte limit of the body.
This is the number of bytes or any string format supported by
[bytes](https://www.npmjs.com/package/bytes),
for example `1000`, `'500kb'` or `'3mb'`.
If the body ends up being larger than this limit,
a `413` error code is returned.
- `encoding` - The encoding to use to decode the body into a string.
By default, a `Buffer` instance will be returned when no encoding is specified.
Most likely, you want `utf-8`, so setting `encoding` to `true` will decode as `utf-8`.
You can use any type of encoding supported by [iconv-lite](https://www.npmjs.org/package/iconv-lite#readme).
You can also pass a string in place of options to just specify the encoding.
If an error occurs, the stream will be paused, everything unpiped,
and you are responsible for correctly disposing the stream.
For HTTP requests, you may need to finish consuming the stream if
you want to keep the socket open for future requests. For streams
that use file descriptors, you should `stream.destroy()` or
`stream.close()` to prevent leaks.
## Errors
This module creates errors depending on the error condition during reading.
The error may be an error from the underlying Node.js implementation, but is
otherwise an error created by this module, which has the following attributes:
* `limit` - the limit in bytes
* `length` and `expected` - the expected length of the stream
* `received` - the received bytes
* `encoding` - the invalid encoding
* `status` and `statusCode` - the corresponding status code for the error
* `type` - the error type
### Types
The errors from this module have a `type` property which allows for the programmatic
determination of the type of error returned.
#### encoding.unsupported
This error will occur when the `encoding` option is specified, but the value does
not map to an encoding supported by the [iconv-lite](https://www.npmjs.org/package/iconv-lite#readme)
module.
#### entity.too.large
This error will occur when the `limit` option is specified, but the stream has
an entity that is larger.
#### request.aborted
This error will occur when the request stream is aborted by the client before
reading the body has finished.
#### request.size.invalid
This error will occur when the `length` option is specified, but the stream has
emitted more bytes.
#### stream.encoding.set
This error will occur when the given stream has an encoding set on it, making it
a decoded stream. The stream should not have an encoding set and is expected to
emit `Buffer` objects.
#### stream.not.readable
This error will occur when the given stream is not readable.
## Examples
### Simple Express example
```js
var contentType = require('content-type')
var express = require('express')
var getRawBody = require('raw-body')
var app = express()
app.use(function (req, res, next) {
getRawBody(req, {
length: req.headers['content-length'],
limit: '1mb',
encoding: contentType.parse(req).parameters.charset
}, function (err, string) {
if (err) return next(err)
req.text = string
next()
})
})
// now access req.text
```
### Simple Koa example
```js
var contentType = require('content-type')
var getRawBody = require('raw-body')
var koa = require('koa')
var app = koa()
app.use(function * (next) {
this.text = yield getRawBody(this.req, {
length: this.req.headers['content-length'],
limit: '1mb',
encoding: contentType.parse(this.req).parameters.charset
})
yield next
})
// now access this.text
```
### Using as a promise
To use this library as a promise, simply omit the `callback` and a promise is
returned, provided that a global `Promise` is defined.
```js
var getRawBody = require('raw-body')
var http = require('http')
var server = http.createServer(function (req, res) {
getRawBody(req)
.then(function (buf) {
res.statusCode = 200
res.end(buf.length + ' bytes submitted')
})
.catch(function (err) {
res.statusCode = 500
res.end(err.message)
})
})
server.listen(3000)
```
### Using with TypeScript
```ts
import * as getRawBody from 'raw-body';
import * as http from 'http';
const server = http.createServer((req, res) => {
getRawBody(req)
.then((buf) => {
res.statusCode = 200;
res.end(buf.length + ' bytes submitted');
})
.catch((err) => {
res.statusCode = err.statusCode;
res.end(err.message);
});
});
server.listen(3000);
```
## License
[MIT](LICENSE)
[npm-image]: https://img.shields.io/npm/v/raw-body.svg
[npm-url]: https://npmjs.org/package/raw-body
[node-version-image]: https://img.shields.io/node/v/raw-body.svg
[node-version-url]: https://nodejs.org/en/download/
[coveralls-image]: https://img.shields.io/coveralls/stream-utils/raw-body/master.svg
[coveralls-url]: https://coveralls.io/r/stream-utils/raw-body?branch=master
[downloads-image]: https://img.shields.io/npm/dm/raw-body.svg
[downloads-url]: https://npmjs.org/package/raw-body
[github-actions-ci-image]: https://img.shields.io/github/workflow/status/stream-utils/raw-body/ci/master?label=ci
[github-actions-ci-url]: https://github.com/jshttp/stream-utils/raw-body?query=workflow%3Aci

24
node_modules/express/node_modules/raw-body/SECURITY.md generated vendored Normal file
View file

@ -0,0 +1,24 @@
# Security Policies and Procedures
## Reporting a Bug
The `raw-body` team and community take all security bugs seriously. Thank you
for improving the security of Express. We appreciate your efforts and
responsible disclosure and will make every effort to acknowledge your
contributions.
Report security bugs by emailing the current owners of `raw-body`. This information
can be found in the npm registry using the command `npm owner ls raw-body`.
If unsure or unable to get the information from the above, open an issue
in the [project issue tracker](https://github.com/stream-utils/raw-body/issues)
asking for the current contact information.
To ensure the timely response to your report, please ensure that the entirety
of the report is contained within the email body and not solely behind a web
link or an attachment.
At least one owner will acknowledge your email within 48 hours, and will send a
more detailed response within 48 hours indicating the next steps in handling
your report. After the initial reply to your report, the owners will
endeavor to keep you informed of the progress towards a fix and full
announcement, and may ask for additional information or guidance.

87
node_modules/express/node_modules/raw-body/index.d.ts generated vendored Normal file
View file

@ -0,0 +1,87 @@
import { Readable } from 'stream';
declare namespace getRawBody {
export type Encoding = string | true;
export interface Options {
/**
* The expected length of the stream.
*/
length?: number | string | null;
/**
* The byte limit of the body. This is the number of bytes or any string
* format supported by `bytes`, for example `1000`, `'500kb'` or `'3mb'`.
*/
limit?: number | string | null;
/**
* The encoding to use to decode the body into a string. By default, a
* `Buffer` instance will be returned when no encoding is specified. Most
* likely, you want `utf-8`, so setting encoding to `true` will decode as
* `utf-8`. You can use any type of encoding supported by `iconv-lite`.
*/
encoding?: Encoding | null;
}
export interface RawBodyError extends Error {
/**
* The limit in bytes.
*/
limit?: number;
/**
* The expected length of the stream.
*/
length?: number;
expected?: number;
/**
* The received bytes.
*/
received?: number;
/**
* The encoding.
*/
encoding?: string;
/**
* The corresponding status code for the error.
*/
status: number;
statusCode: number;
/**
* The error type.
*/
type: string;
}
}
/**
* Gets the entire buffer of a stream either as a `Buffer` or a string.
* Validates the stream's length against an expected length and maximum
* limit. Ideal for parsing request bodies.
*/
declare function getRawBody(
stream: Readable,
callback: (err: getRawBody.RawBodyError, body: Buffer) => void
): void;
declare function getRawBody(
stream: Readable,
options: (getRawBody.Options & { encoding: getRawBody.Encoding }) | getRawBody.Encoding,
callback: (err: getRawBody.RawBodyError, body: string) => void
): void;
declare function getRawBody(
stream: Readable,
options: getRawBody.Options,
callback: (err: getRawBody.RawBodyError, body: Buffer) => void
): void;
declare function getRawBody(
stream: Readable,
options: (getRawBody.Options & { encoding: getRawBody.Encoding }) | getRawBody.Encoding
): Promise<string>;
declare function getRawBody(
stream: Readable,
options?: getRawBody.Options
): Promise<Buffer>;
export = getRawBody;

329
node_modules/express/node_modules/raw-body/index.js generated vendored Normal file
View file

@ -0,0 +1,329 @@
/*!
* raw-body
* Copyright(c) 2013-2014 Jonathan Ong
* Copyright(c) 2014-2022 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict'
/**
* Module dependencies.
* @private
*/
var asyncHooks = tryRequireAsyncHooks()
var bytes = require('bytes')
var createError = require('http-errors')
var iconv = require('iconv-lite')
var unpipe = require('unpipe')
/**
* Module exports.
* @public
*/
module.exports = getRawBody
/**
* Module variables.
* @private
*/
var ICONV_ENCODING_MESSAGE_REGEXP = /^Encoding not recognized: /
/**
* Get the decoder for a given encoding.
*
* @param {string} encoding
* @private
*/
function getDecoder (encoding) {
if (!encoding) return null
try {
return iconv.getDecoder(encoding)
} catch (e) {
// error getting decoder
if (!ICONV_ENCODING_MESSAGE_REGEXP.test(e.message)) throw e
// the encoding was not found
throw createError(415, 'specified encoding unsupported', {
encoding: encoding,
type: 'encoding.unsupported'
})
}
}
/**
* Get the raw body of a stream (typically HTTP).
*
* @param {object} stream
* @param {object|string|function} [options]
* @param {function} [callback]
* @public
*/
function getRawBody (stream, options, callback) {
var done = callback
var opts = options || {}
if (options === true || typeof options === 'string') {
// short cut for encoding
opts = {
encoding: options
}
}
if (typeof options === 'function') {
done = options
opts = {}
}
// validate callback is a function, if provided
if (done !== undefined && typeof done !== 'function') {
throw new TypeError('argument callback must be a function')
}
// require the callback without promises
if (!done && !global.Promise) {
throw new TypeError('argument callback is required')
}
// get encoding
var encoding = opts.encoding !== true
? opts.encoding
: 'utf-8'
// convert the limit to an integer
var limit = bytes.parse(opts.limit)
// convert the expected length to an integer
var length = opts.length != null && !isNaN(opts.length)
? parseInt(opts.length, 10)
: null
if (done) {
// classic callback style
return readStream(stream, encoding, length, limit, wrap(done))
}
return new Promise(function executor (resolve, reject) {
readStream(stream, encoding, length, limit, function onRead (err, buf) {
if (err) return reject(err)
resolve(buf)
})
})
}
/**
* Halt a stream.
*
* @param {Object} stream
* @private
*/
function halt (stream) {
// unpipe everything from the stream
unpipe(stream)
// pause stream
if (typeof stream.pause === 'function') {
stream.pause()
}
}
/**
* Read the data from the stream.
*
* @param {object} stream
* @param {string} encoding
* @param {number} length
* @param {number} limit
* @param {function} callback
* @public
*/
function readStream (stream, encoding, length, limit, callback) {
var complete = false
var sync = true
// check the length and limit options.
// note: we intentionally leave the stream paused,
// so users should handle the stream themselves.
if (limit !== null && length !== null && length > limit) {
return done(createError(413, 'request entity too large', {
expected: length,
length: length,
limit: limit,
type: 'entity.too.large'
}))
}
// streams1: assert request encoding is buffer.
// streams2+: assert the stream encoding is buffer.
// stream._decoder: streams1
// state.encoding: streams2
// state.decoder: streams2, specifically < 0.10.6
var state = stream._readableState
if (stream._decoder || (state && (state.encoding || state.decoder))) {
// developer error
return done(createError(500, 'stream encoding should not be set', {
type: 'stream.encoding.set'
}))
}
if (typeof stream.readable !== 'undefined' && !stream.readable) {
return done(createError(500, 'stream is not readable', {
type: 'stream.not.readable'
}))
}
var received = 0
var decoder
try {
decoder = getDecoder(encoding)
} catch (err) {
return done(err)
}
var buffer = decoder
? ''
: []
// attach listeners
stream.on('aborted', onAborted)
stream.on('close', cleanup)
stream.on('data', onData)
stream.on('end', onEnd)
stream.on('error', onEnd)
// mark sync section complete
sync = false
function done () {
var args = new Array(arguments.length)
// copy arguments
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i]
}
// mark complete
complete = true
if (sync) {
process.nextTick(invokeCallback)
} else {
invokeCallback()
}
function invokeCallback () {
cleanup()
if (args[0]) {
// halt the stream on error
halt(stream)
}
callback.apply(null, args)
}
}
function onAborted () {
if (complete) return
done(createError(400, 'request aborted', {
code: 'ECONNABORTED',
expected: length,
length: length,
received: received,
type: 'request.aborted'
}))
}
function onData (chunk) {
if (complete) return
received += chunk.length
if (limit !== null && received > limit) {
done(createError(413, 'request entity too large', {
limit: limit,
received: received,
type: 'entity.too.large'
}))
} else if (decoder) {
buffer += decoder.write(chunk)
} else {
buffer.push(chunk)
}
}
function onEnd (err) {
if (complete) return
if (err) return done(err)
if (length !== null && received !== length) {
done(createError(400, 'request size did not match content length', {
expected: length,
length: length,
received: received,
type: 'request.size.invalid'
}))
} else {
var string = decoder
? buffer + (decoder.end() || '')
: Buffer.concat(buffer)
done(null, string)
}
}
function cleanup () {
buffer = null
stream.removeListener('aborted', onAborted)
stream.removeListener('data', onData)
stream.removeListener('end', onEnd)
stream.removeListener('error', onEnd)
stream.removeListener('close', cleanup)
}
}
/**
* Try to require async_hooks
* @private
*/
function tryRequireAsyncHooks () {
try {
return require('async_hooks')
} catch (e) {
return {}
}
}
/**
* Wrap function with async resource, if possible.
* AsyncResource.bind static method backported.
* @private
*/
function wrap (fn) {
var res
// create anonymous resource
if (asyncHooks.AsyncResource) {
res = new asyncHooks.AsyncResource(fn.name || 'bound-anonymous-fn')
}
// incompatible node.js
if (!res || !res.runInAsyncScope) {
return fn
}
// return bound function
return res.runInAsyncScope.bind(res, fn, null)
}

View file

@ -0,0 +1,49 @@
{
"name": "raw-body",
"description": "Get and validate the raw body of a readable stream.",
"version": "2.5.1",
"author": "Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)",
"contributors": [
"Douglas Christopher Wilson <doug@somethingdoug.com>",
"Raynos <raynos2@gmail.com>"
],
"license": "MIT",
"repository": "stream-utils/raw-body",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"devDependencies": {
"bluebird": "3.7.2",
"eslint": "7.32.0",
"eslint-config-standard": "14.1.1",
"eslint-plugin-import": "2.25.4",
"eslint-plugin-markdown": "2.2.1",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-promise": "5.2.0",
"eslint-plugin-standard": "4.1.0",
"mocha": "9.2.1",
"nyc": "15.1.0",
"readable-stream": "2.3.7",
"safe-buffer": "5.2.1"
},
"engines": {
"node": ">= 0.8"
},
"files": [
"HISTORY.md",
"LICENSE",
"README.md",
"SECURITY.md",
"index.d.ts",
"index.js"
],
"scripts": {
"lint": "eslint .",
"test": "mocha --trace-deprecation --reporter spec --bail --check-leaks test/",
"test-ci": "nyc --reporter=lcovonly --reporter=text npm test",
"test-cov": "nyc --reporter=html --reporter=text npm test"
}
}

5
node_modules/raw-body/HISTORY.md generated vendored
View file

@ -1,3 +1,8 @@
2.5.2 / 2023-02-21
==================
* Fix error message for non-stream argument
2.5.1 / 2022-02-28 2.5.1 / 2022-02-28
================== ==================

2
node_modules/raw-body/README.md generated vendored
View file

@ -219,5 +219,5 @@ server.listen(3000);
[coveralls-url]: https://coveralls.io/r/stream-utils/raw-body?branch=master [coveralls-url]: https://coveralls.io/r/stream-utils/raw-body?branch=master
[downloads-image]: https://img.shields.io/npm/dm/raw-body.svg [downloads-image]: https://img.shields.io/npm/dm/raw-body.svg
[downloads-url]: https://npmjs.org/package/raw-body [downloads-url]: https://npmjs.org/package/raw-body
[github-actions-ci-image]: https://img.shields.io/github/workflow/status/stream-utils/raw-body/ci/master?label=ci [github-actions-ci-image]: https://img.shields.io/github/actions/workflow/status/stream-utils/raw-body/ci.yml?branch=master&label=ci
[github-actions-ci-url]: https://github.com/jshttp/stream-utils/raw-body?query=workflow%3Aci [github-actions-ci-url]: https://github.com/jshttp/stream-utils/raw-body?query=workflow%3Aci

7
node_modules/raw-body/index.js generated vendored
View file

@ -69,6 +69,13 @@ function getRawBody (stream, options, callback) {
var done = callback var done = callback
var opts = options || {} var opts = options || {}
// light validation
if (stream === undefined) {
throw new TypeError('argument stream is required')
} else if (typeof stream !== 'object' || stream === null || typeof stream.on !== 'function') {
throw new TypeError('argument stream must be a stream')
}
if (options === true || typeof options === 'string') { if (options === true || typeof options === 'string') {
// short cut for encoding // short cut for encoding
opts = { opts = {

14
node_modules/raw-body/package.json generated vendored
View file

@ -1,7 +1,7 @@
{ {
"name": "raw-body", "name": "raw-body",
"description": "Get and validate the raw body of a readable stream.", "description": "Get and validate the raw body of a readable stream.",
"version": "2.5.1", "version": "2.5.2",
"author": "Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)", "author": "Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)",
"contributors": [ "contributors": [
"Douglas Christopher Wilson <doug@somethingdoug.com>", "Douglas Christopher Wilson <doug@somethingdoug.com>",
@ -17,14 +17,14 @@
}, },
"devDependencies": { "devDependencies": {
"bluebird": "3.7.2", "bluebird": "3.7.2",
"eslint": "7.32.0", "eslint": "8.34.0",
"eslint-config-standard": "14.1.1", "eslint-config-standard": "15.0.1",
"eslint-plugin-import": "2.25.4", "eslint-plugin-import": "2.27.5",
"eslint-plugin-markdown": "2.2.1", "eslint-plugin-markdown": "3.0.0",
"eslint-plugin-node": "11.1.0", "eslint-plugin-node": "11.1.0",
"eslint-plugin-promise": "5.2.0", "eslint-plugin-promise": "6.1.1",
"eslint-plugin-standard": "4.1.0", "eslint-plugin-standard": "4.1.0",
"mocha": "9.2.1", "mocha": "10.2.0",
"nyc": "15.1.0", "nyc": "15.1.0",
"readable-stream": "2.3.7", "readable-stream": "2.3.7",
"safe-buffer": "5.2.1" "safe-buffer": "5.2.1"

5
node_modules/streamsearch/.eslintrc.js generated vendored Normal file
View file

@ -0,0 +1,5 @@
'use strict';
module.exports = {
extends: '@mscdex/eslint-config',
};

24
node_modules/streamsearch/.github/workflows/ci.yml generated vendored Normal file
View file

@ -0,0 +1,24 @@
name: CI
on:
pull_request:
push:
branches: [ master ]
jobs:
tests-linux:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [10.x, 12.x, 14.x, 16.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Install module
run: npm install
- name: Run tests
run: npm test

23
node_modules/streamsearch/.github/workflows/lint.yml generated vendored Normal file
View file

@ -0,0 +1,23 @@
name: lint
on:
pull_request:
push:
branches: [ master ]
env:
NODE_VERSION: 16.x
jobs:
lint-js:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@v1
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install ESLint + ESLint configs/plugins
run: npm install --only=dev
- name: Lint files
run: npm run lint

19
node_modules/streamsearch/LICENSE generated vendored Normal file
View file

@ -0,0 +1,19 @@
Copyright Brian White. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

95
node_modules/streamsearch/README.md generated vendored Normal file
View file

@ -0,0 +1,95 @@
Description
===========
streamsearch is a module for [node.js](http://nodejs.org/) that allows searching a stream using the Boyer-Moore-Horspool algorithm.
This module is based heavily on the Streaming Boyer-Moore-Horspool C++ implementation by Hongli Lai [here](https://github.com/FooBarWidget/boyer-moore-horspool).
Requirements
============
* [node.js](http://nodejs.org/) -- v10.0.0 or newer
Installation
============
npm install streamsearch
Example
=======
```js
const { inspect } = require('util');
const StreamSearch = require('streamsearch');
const needle = Buffer.from('\r\n');
const ss = new StreamSearch(needle, (isMatch, data, start, end) => {
if (data)
console.log('data: ' + inspect(data.toString('latin1', start, end)));
if (isMatch)
console.log('match!');
});
const chunks = [
'foo',
' bar',
'\r',
'\n',
'baz, hello\r',
'\n world.',
'\r\n Node.JS rules!!\r\n\r\n',
];
for (const chunk of chunks)
ss.push(Buffer.from(chunk));
// output:
//
// data: 'foo'
// data: ' bar'
// match!
// data: 'baz, hello'
// match!
// data: ' world.'
// match!
// data: ' Node.JS rules!!'
// match!
// data: ''
// match!
```
API
===
Properties
----------
* **maxMatches** - < _integer_ > - The maximum number of matches. Defaults to `Infinity`.
* **matches** - < _integer_ > - The current match count.
Functions
---------
* **(constructor)**(< _mixed_ >needle, < _function_ >callback) - Creates and returns a new instance for searching for a _Buffer_ or _string_ `needle`. `callback` is called any time there is non-matching data and/or there is a needle match. `callback` will be called with the following arguments:
1. `isMatch` - _boolean_ - Indicates whether a match has been found
2. `data` - _mixed_ - If set, this contains data that did not match the needle.
3. `start` - _integer_ - The index in `data` where the non-matching data begins (inclusive).
4. `end` - _integer_ - The index in `data` where the non-matching data ends (exclusive).
5. `isSafeData` - _boolean_ - Indicates if it is safe to store a reference to `data` (e.g. as-is or via `data.slice()`) or not, as in some cases `data` may point to a Buffer whose contents change over time.
* **destroy**() - _(void)_ - Emits any last remaining unmatched data that may still be buffered and then resets internal state.
* **push**(< _Buffer_ >chunk) - _integer_ - Processes `chunk`, searching for a match. The return value is the last processed index in `chunk` + 1.
* **reset**() - _(void)_ - Resets internal state. Useful for when you wish to start searching a new/different stream for example.

267
node_modules/streamsearch/lib/sbmh.js generated vendored Normal file
View file

@ -0,0 +1,267 @@
'use strict';
/*
Based heavily on the Streaming Boyer-Moore-Horspool C++ implementation
by Hongli Lai at: https://github.com/FooBarWidget/boyer-moore-horspool
*/
function memcmp(buf1, pos1, buf2, pos2, num) {
for (let i = 0; i < num; ++i) {
if (buf1[pos1 + i] !== buf2[pos2 + i])
return false;
}
return true;
}
class SBMH {
constructor(needle, cb) {
if (typeof cb !== 'function')
throw new Error('Missing match callback');
if (typeof needle === 'string')
needle = Buffer.from(needle);
else if (!Buffer.isBuffer(needle))
throw new Error(`Expected Buffer for needle, got ${typeof needle}`);
const needleLen = needle.length;
this.maxMatches = Infinity;
this.matches = 0;
this._cb = cb;
this._lookbehindSize = 0;
this._needle = needle;
this._bufPos = 0;
this._lookbehind = Buffer.allocUnsafe(needleLen);
// Initialize occurrence table.
this._occ = [
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen, needleLen, needleLen,
needleLen, needleLen, needleLen, needleLen
];
// Populate occurrence table with analysis of the needle, ignoring the last
// letter.
if (needleLen > 1) {
for (let i = 0; i < needleLen - 1; ++i)
this._occ[needle[i]] = needleLen - 1 - i;
}
}
reset() {
this.matches = 0;
this._lookbehindSize = 0;
this._bufPos = 0;
}
push(chunk, pos) {
let result;
if (!Buffer.isBuffer(chunk))
chunk = Buffer.from(chunk, 'latin1');
const chunkLen = chunk.length;
this._bufPos = pos || 0;
while (result !== chunkLen && this.matches < this.maxMatches)
result = feed(this, chunk);
return result;
}
destroy() {
const lbSize = this._lookbehindSize;
if (lbSize)
this._cb(false, this._lookbehind, 0, lbSize, false);
this.reset();
}
}
function feed(self, data) {
const len = data.length;
const needle = self._needle;
const needleLen = needle.length;
// Positive: points to a position in `data`
// pos == 3 points to data[3]
// Negative: points to a position in the lookbehind buffer
// pos == -2 points to lookbehind[lookbehindSize - 2]
let pos = -self._lookbehindSize;
const lastNeedleCharPos = needleLen - 1;
const lastNeedleChar = needle[lastNeedleCharPos];
const end = len - needleLen;
const occ = self._occ;
const lookbehind = self._lookbehind;
if (pos < 0) {
// Lookbehind buffer is not empty. Perform Boyer-Moore-Horspool
// search with character lookup code that considers both the
// lookbehind buffer and the current round's haystack data.
//
// Loop until
// there is a match.
// or until
// we've moved past the position that requires the
// lookbehind buffer. In this case we switch to the
// optimized loop.
// or until
// the character to look at lies outside the haystack.
while (pos < 0 && pos <= end) {
const nextPos = pos + lastNeedleCharPos;
const ch = (nextPos < 0
? lookbehind[self._lookbehindSize + nextPos]
: data[nextPos]);
if (ch === lastNeedleChar
&& matchNeedle(self, data, pos, lastNeedleCharPos)) {
self._lookbehindSize = 0;
++self.matches;
if (pos > -self._lookbehindSize)
self._cb(true, lookbehind, 0, self._lookbehindSize + pos, false);
else
self._cb(true, undefined, 0, 0, true);
return (self._bufPos = pos + needleLen);
}
pos += occ[ch];
}
// No match.
// There's too few data for Boyer-Moore-Horspool to run,
// so let's use a different algorithm to skip as much as
// we can.
// Forward pos until
// the trailing part of lookbehind + data
// looks like the beginning of the needle
// or until
// pos == 0
while (pos < 0 && !matchNeedle(self, data, pos, len - pos))
++pos;
if (pos < 0) {
// Cut off part of the lookbehind buffer that has
// been processed and append the entire haystack
// into it.
const bytesToCutOff = self._lookbehindSize + pos;
if (bytesToCutOff > 0) {
// The cut off data is guaranteed not to contain the needle.
self._cb(false, lookbehind, 0, bytesToCutOff, false);
}
self._lookbehindSize -= bytesToCutOff;
lookbehind.copy(lookbehind, 0, bytesToCutOff, self._lookbehindSize);
lookbehind.set(data, self._lookbehindSize);
self._lookbehindSize += len;
self._bufPos = len;
return len;
}
// Discard lookbehind buffer.
self._cb(false, lookbehind, 0, self._lookbehindSize, false);
self._lookbehindSize = 0;
}
pos += self._bufPos;
const firstNeedleChar = needle[0];
// Lookbehind buffer is now empty. Perform Boyer-Moore-Horspool
// search with optimized character lookup code that only considers
// the current round's haystack data.
while (pos <= end) {
const ch = data[pos + lastNeedleCharPos];
if (ch === lastNeedleChar
&& data[pos] === firstNeedleChar
&& memcmp(needle, 0, data, pos, lastNeedleCharPos)) {
++self.matches;
if (pos > 0)
self._cb(true, data, self._bufPos, pos, true);
else
self._cb(true, undefined, 0, 0, true);
return (self._bufPos = pos + needleLen);
}
pos += occ[ch];
}
// There was no match. If there's trailing haystack data that we cannot
// match yet using the Boyer-Moore-Horspool algorithm (because the trailing
// data is less than the needle size) then match using a modified
// algorithm that starts matching from the beginning instead of the end.
// Whatever trailing data is left after running this algorithm is added to
// the lookbehind buffer.
while (pos < len) {
if (data[pos] !== firstNeedleChar
|| !memcmp(data, pos, needle, 0, len - pos)) {
++pos;
continue;
}
data.copy(lookbehind, 0, pos, len);
self._lookbehindSize = len - pos;
break;
}
// Everything until `pos` is guaranteed not to contain needle data.
if (pos > 0)
self._cb(false, data, self._bufPos, pos < len ? pos : len, true);
self._bufPos = len;
return len;
}
function matchNeedle(self, data, pos, len) {
const lb = self._lookbehind;
const lbSize = self._lookbehindSize;
const needle = self._needle;
for (let i = 0; i < len; ++i, ++pos) {
const ch = (pos < 0 ? lb[lbSize + pos] : data[pos]);
if (ch !== needle[i])
return false;
}
return true;
}
module.exports = SBMH;

34
node_modules/streamsearch/package.json generated vendored Normal file
View file

@ -0,0 +1,34 @@
{
"name": "streamsearch",
"version": "1.1.0",
"author": "Brian White <mscdex@mscdex.net>",
"description": "Streaming Boyer-Moore-Horspool searching for node.js",
"main": "./lib/sbmh.js",
"engines": {
"node": ">=10.0.0"
},
"devDependencies": {
"@mscdex/eslint-config": "^1.1.0",
"eslint": "^7.32.0"
},
"scripts": {
"test": "node test/test.js",
"lint": "eslint --cache --report-unused-disable-directives --ext=.js .eslintrc.js lib test",
"lint:fix": "npm run lint -- --fix"
},
"keywords": [
"stream",
"horspool",
"boyer-moore-horspool",
"boyer-moore",
"search"
],
"licenses": [{
"type": "MIT",
"url": "http://github.com/mscdex/streamsearch/raw/master/LICENSE"
}],
"repository": {
"type": "git",
"url": "http://github.com/mscdex/streamsearch.git"
}
}

70
node_modules/streamsearch/test/test.js generated vendored Normal file
View file

@ -0,0 +1,70 @@
'use strict';
const assert = require('assert');
const StreamSearch = require('../lib/sbmh.js');
[
{
needle: '\r\n',
chunks: [
'foo',
' bar',
'\r',
'\n',
'baz, hello\r',
'\n world.',
'\r\n Node.JS rules!!\r\n\r\n',
],
expect: [
[false, 'foo'],
[false, ' bar'],
[ true, null],
[false, 'baz, hello'],
[ true, null],
[false, ' world.'],
[ true, null],
[ true, ' Node.JS rules!!'],
[ true, ''],
],
},
{
needle: '---foobarbaz',
chunks: [
'---foobarbaz',
'asdf',
'\r\n',
'---foobarba',
'---foobar',
'ba',
'\r\n---foobarbaz--\r\n',
],
expect: [
[ true, null],
[false, 'asdf'],
[false, '\r\n'],
[false, '---foobarba'],
[false, '---foobarba'],
[ true, '\r\n'],
[false, '--\r\n'],
],
},
].forEach((test, i) => {
console.log(`Running test #${i + 1}`);
const { needle, chunks, expect } = test;
const results = [];
const ss = new StreamSearch(Buffer.from(needle),
(isMatch, data, start, end) => {
if (data)
data = data.toString('latin1', start, end);
else
data = null;
results.push([isMatch, data]);
});
for (const chunk of chunks)
ss.push(Buffer.from(chunk));
assert.deepStrictEqual(results, expect);
});

765
package-lock.json generated Normal file
View file

@ -0,0 +1,765 @@
{
"name": "skillsgames-server",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "skillsgames-server",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"body-parser": "^1.20.2",
"express": "^4.18.2",
"express-fileupload": "^1.4.2",
"fs": "^0.0.1-security",
"js-yaml": "^4.1.0",
"mysql": "^2.18.1"
}
},
"node_modules/accepts": {
"version": "1.3.8",
"license": "MIT",
"dependencies": {
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/argparse": {
"version": "2.0.1",
"license": "Python-2.0"
},
"node_modules/array-flatten": {
"version": "1.1.1",
"license": "MIT"
},
"node_modules/bignumber.js": {
"version": "9.0.0",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/body-parser": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
"dependencies": {
"streamsearch": "^1.1.0"
},
"engines": {
"node": ">=10.16.0"
}
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/call-bind": {
"version": "1.0.5",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.1",
"set-function-length": "^1.1.1"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/content-disposition": {
"version": "0.5.4",
"license": "MIT",
"dependencies": {
"safe-buffer": "5.2.1"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/content-type": {
"version": "1.0.5",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie": {
"version": "0.5.0",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie-signature": {
"version": "1.0.6",
"license": "MIT"
},
"node_modules/core-util-is": {
"version": "1.0.3",
"license": "MIT"
},
"node_modules/debug": {
"version": "2.6.9",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/define-data-property": {
"version": "1.1.1",
"license": "MIT",
"dependencies": {
"get-intrinsic": "^1.2.1",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/depd": {
"version": "2.0.0",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/destroy": {
"version": "1.2.0",
"license": "MIT",
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"license": "MIT"
},
"node_modules/encodeurl": {
"version": "1.0.2",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"license": "MIT"
},
"node_modules/etag": {
"version": "1.8.1",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express": {
"version": "4.18.2",
"license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.1",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.5.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.2.0",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.7",
"qs": "6.11.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.18.0",
"serve-static": "1.15.0",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"engines": {
"node": ">= 0.10.0"
}
},
"node_modules/express-fileupload": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.4.2.tgz",
"integrity": "sha512-vk+9cK595jP03T+YgoYPAebynVCZuUBtW1JkyJnitQnWzlONHdxdAIm9yo99V4viTEftq7MUfzuqmWyqWGzMIg==",
"dependencies": {
"busboy": "^1.6.0"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/express/node_modules/body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/express/node_modules/raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/finalhandler": {
"version": "1.2.0",
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"statuses": "2.0.1",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fresh": {
"version": "0.5.2",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fs": {
"version": "0.0.1-security",
"license": "ISC"
},
"node_modules/function-bind": {
"version": "1.1.2",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.2.2",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/gopd": {
"version": "1.0.1",
"license": "MIT",
"dependencies": {
"get-intrinsic": "^1.1.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-property-descriptors": {
"version": "1.0.1",
"license": "MIT",
"dependencies": {
"get-intrinsic": "^1.2.2"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-proto": {
"version": "1.0.1",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.0.3",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.0",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/http-errors": {
"version": "2.0.0",
"license": "MIT",
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"license": "ISC"
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"license": "MIT",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/isarray": {
"version": "1.0.0",
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.0",
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/media-typer": {
"version": "0.3.0",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
"license": "MIT"
},
"node_modules/methods": {
"version": "1.1.2",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"license": "MIT",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ms": {
"version": "2.0.0",
"license": "MIT"
},
"node_modules/mysql": {
"version": "2.18.1",
"license": "MIT",
"dependencies": {
"bignumber.js": "9.0.0",
"readable-stream": "2.3.7",
"safe-buffer": "5.1.2",
"sqlstring": "2.3.1"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mysql/node_modules/safe-buffer": {
"version": "5.1.2",
"license": "MIT"
},
"node_modules/negotiator": {
"version": "0.6.3",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/object-inspect": {
"version": "1.13.1",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/on-finished": {
"version": "2.4.1",
"license": "MIT",
"dependencies": {
"ee-first": "1.1.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/path-to-regexp": {
"version": "0.1.7",
"license": "MIT"
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"license": "MIT"
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"license": "MIT",
"dependencies": {
"forwarded": "0.2.0",
"ipaddr.js": "1.9.1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/qs": {
"version": "6.11.0",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.0.4"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/range-parser": {
"version": "1.2.1",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/raw-body": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/readable-stream": {
"version": "2.3.7",
"license": "MIT",
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/readable-stream/node_modules/safe-buffer": {
"version": "5.1.2",
"license": "MIT"
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/send": {
"version": "0.18.0",
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"mime": "1.6.0",
"ms": "2.1.3",
"on-finished": "2.4.1",
"range-parser": "~1.2.1",
"statuses": "2.0.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
"license": "MIT"
},
"node_modules/serve-static": {
"version": "1.15.0",
"license": "MIT",
"dependencies": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.18.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/set-function-length": {
"version": "1.1.1",
"license": "MIT",
"dependencies": {
"define-data-property": "^1.1.1",
"get-intrinsic": "^1.2.1",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"license": "ISC"
},
"node_modules/side-channel": {
"version": "1.0.4",
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2",
"object-inspect": "^1.9.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/sqlstring": {
"version": "2.3.1",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/statuses": {
"version": "2.0.1",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/string_decoder": {
"version": "1.1.1",
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/string_decoder/node_modules/safe-buffer": {
"version": "5.1.2",
"license": "MIT"
},
"node_modules/toidentifier": {
"version": "1.0.1",
"license": "MIT",
"engines": {
"node": ">=0.6"
}
},
"node_modules/type-is": {
"version": "1.6.18",
"license": "MIT",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/unpipe": {
"version": "1.0.0",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"license": "MIT"
},
"node_modules/utils-merge": {
"version": "1.0.1",
"license": "MIT",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/vary": {
"version": "1.1.2",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
}
}
}

View file

@ -9,7 +9,9 @@
"author": "Paul Black", "author": "Paul Black",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"body-parser": "^1.20.2",
"express": "^4.18.2", "express": "^4.18.2",
"express-fileupload": "^1.4.2",
"fs": "^0.0.1-security", "fs": "^0.0.1-security",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"mysql": "^2.18.1" "mysql": "^2.18.1"

View file

@ -1,4 +1,5 @@
const express = require("express"); const express = require("express");
const bodyParser = require('body-parser');
const mysql = require("mysql"); const mysql = require("mysql");
const fs = require("fs"); const fs = require("fs");
const yaml = require("js-yaml"); const yaml = require("js-yaml");
@ -7,9 +8,9 @@ const app = express();
const port = 3000; const port = 3000;
// Read database configuration from config.yaml // Read database configuration from config.yaml
const dbConfig = yaml.load(fs.readFileSync("config.yaml", "utf8")); const config = yaml.load(fs.readFileSync("config.yaml", "utf8"));
const db = mysql.createConnection(dbConfig.database); const db = mysql.createConnection(config.database);
db.connect((err) => { db.connect((err) => {
if (err) { if (err) {
@ -19,9 +20,23 @@ db.connect((err) => {
console.log("Connected to the database"); console.log("Connected to the database");
}); });
// Middleware for authentication
const authenticate = (req, res, next) => {
const providedPassword = req.headers.authorization;
if (providedPassword === config.app.password) {
next();
} else {
res.status(401).json({ error: 'Unauthorized' });
}
};
// Serve static files (HTML, CSS, JavaScript) // Serve static files (HTML, CSS, JavaScript)
app.use(express.static("public")); app.use(express.static("public"));
// json data parser
app.use(bodyParser.json());
// Define a route to retrieve game data // Define a route to retrieve game data
app.get("/games", (req, res) => { app.get("/games", (req, res) => {
const sql = "SELECT * FROM games"; const sql = "SELECT * FROM games";
@ -38,6 +53,31 @@ app.get("/games", (req, res) => {
}); });
}); });
// Endpoint to add a game
app.post('/add-game', authenticate, (req, res) => {
const { name, publishedLink, year, state, education, level, place, name1, name2 } = req.body;
// SQL query to insert data into the games table
const sql = `
INSERT INTO games (name, publishedLink, year, state, education, level, place, name1, name2)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`;
// Array of values to be inserted into the query
const values = [name, publishedLink, year, state, education, level, place, name1, name2];
// Execute the SQL query
db.query(sql, values, (error, results) => {
if (error) {
console.error('Error executing SQL query:', error);
res.status(500).json({ error: 'Internal Server Error' });
} else {
console.log('Data inserted into the games table:', results);
res.json({ message: 'Game added successfully!' });
}
});
});
app.listen(port, () => { app.listen(port, () => {
console.log(`Server is running on port ${port}`); console.log(`Server is running on port ${port}`);
}); });

Some files were not shown because too many files have changed in this diff Show more