@aikidosec/firewall
Version:
Zen by Aikido is an embedded Application Firewall that autonomously protects Node.js apps against common and critical attacks, provides rate limiting, detects malicious traffic (including bots), and more.
96 lines (95 loc) • 3.69 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.readBodyStream = readBodyStream;
const stream_1 = require("stream");
const getMaxBodySize_1 = require("../../helpers/getMaxBodySize");
const replaceRequestBody_1 = require("./replaceRequestBody");
const form_parsing_1 = require("../../helpers/form-parsing");
const getBodyDataType_1 = require("../../agent/api-discovery/getBodyDataType");
const tryParseJSON_1 = require("../../helpers/tryParseJSON");
const AgentSingleton_1 = require("../../agent/AgentSingleton");
async function readBodyStream(req, res, agent) {
let bodyText = "";
let bodyFields = [];
let bodySize = 0;
const maxBodySize = (0, getMaxBodySize_1.getMaxBodySize)();
const stream = new stream_1.PassThrough();
let busboy = undefined;
if (req.headers["content-type"] !== undefined) {
const bodyType = (0, getBodyDataType_1.getBodyDataType)(req.headers);
if (bodyType === "form-data" || bodyType === "form-urlencoded") {
busboy = new form_parsing_1.Busboy({
headers: req.headers,
});
busboy.on("error", (err) => {
var _a;
(_a = (0, AgentSingleton_1.getInstance)()) === null || _a === void 0 ? void 0 : _a.log(`Error parsing form data body: ${err instanceof Error ? err.message : String(err)}`);
});
busboy.on("field", (fieldname, val) => {
if (typeof val !== "string") {
return;
}
if (val.includes('"')) {
const decodedVal = (0, tryParseJSON_1.tryParseJSON)(val);
if (decodedVal !== undefined) {
bodyFields.push({ name: fieldname, value: decodedVal });
return;
}
}
bodyFields.push({ name: fieldname, value: val });
});
}
}
try {
for await (const chunk of req) {
if (bodySize + chunk.length > maxBodySize) {
res.statusCode = 413;
res.end("This request was aborted by Aikido firewall because the body size exceeded the maximum allowed size. Use AIKIDO_MAX_BODY_SIZE_MB to increase the limit.", () => {
req.destroy();
});
agent.getInspectionStatistics().onAbortedRequest();
return {
success: false,
};
}
bodySize += chunk.length;
bodyText += chunk.toString();
stream.push(chunk);
busboy === null || busboy === void 0 ? void 0 : busboy.write(chunk);
}
}
catch {
res.statusCode = 500;
res.end("Aikido firewall encountered an error while reading the request body.", () => {
req.destroy();
});
busboy === null || busboy === void 0 ? void 0 : busboy.end();
return {
success: false,
};
}
// End the stream
stream.push(null);
busboy === null || busboy === void 0 ? void 0 : busboy.end();
// Ensure the body stream can be read again by the application
(0, replaceRequestBody_1.replaceRequestBody)(req, stream);
if (bodyFields.length > 0) {
return {
success: true,
body: {
fields: bodyFields,
},
};
}
const parsedBodyText = (0, tryParseJSON_1.tryParseJSON)(bodyText);
if (parsedBodyText) {
return {
success: true,
body: parsedBodyText,
};
}
return {
success: true,
body: undefined,
};
}