@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.
143 lines (142 loc) • 5.41 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.FunctionsFramework = void 0;
exports.getFlushEveryMS = getFlushEveryMS;
exports.getTimeoutInMS = getTimeoutInMS;
exports.createCloudFunctionWrapper = createCloudFunctionWrapper;
const AgentSingleton_1 = require("../agent/AgentSingleton");
const Context_1 = require("../agent/Context");
const wrapExport_1 = require("../agent/hooks/wrapExport");
const buildRouteFromURL_1 = require("../helpers/buildRouteFromURL");
const shouldDiscoverRoute_1 = require("./http-server/shouldDiscoverRoute");
const isPlainObject_1 = require("../helpers/isPlainObject");
function getFlushEveryMS() {
if (process.env.AIKIDO_CLOUD_FUNCTION_FLUSH_EVERY_MS) {
const parsed = parseInt(process.env.AIKIDO_CLOUD_FUNCTION_FLUSH_EVERY_MS, 10);
// Minimum is 1 minute
if (!isNaN(parsed) && parsed >= 60 * 1000) {
return parsed;
}
}
return 10 * 60 * 1000; // 10 minutes
}
function getTimeoutInMS() {
if (process.env.AIKIDO_CLOUD_FUNCTION_TIMEOUT_MS) {
const parsed = parseInt(process.env.AIKIDO_CLOUD_FUNCTION_TIMEOUT_MS, 10);
// Minimum is 1 second
if (!isNaN(parsed) && parsed >= 1000) {
return parsed;
}
}
return 1000; // 1 second
}
function createCloudFunctionWrapper(fn) {
const agent = (0, AgentSingleton_1.getInstance)();
let lastFlushStatsAt = undefined;
let startupEventSent = false;
return async (req, res) => {
// Send startup event on first invocation
if (agent && !startupEventSent) {
startupEventSent = true;
try {
await agent.onStart(getTimeoutInMS());
}
catch (err) {
// eslint-disable-next-line no-console
console.error(`Aikido: Failed to start agent: ${err.message}`);
}
}
const url = req.protocol + "://" + req.get("host") + req.originalUrl;
return await (0, Context_1.runWithContext)({
method: req.method,
remoteAddress: req.ip,
body: req.body ? req.body : undefined,
url: url,
headers: req.headers,
query: req.query,
/* c8 ignore next */
cookies: req.cookies ? req.cookies : {},
routeParams: {},
source: "cloud-function/http",
route: (0, buildRouteFromURL_1.buildRouteFromURL)(url),
}, async () => {
try {
return await fn(req, res);
}
finally {
const context = (0, Context_1.getContext)();
if (agent && context) {
incrementStatsAndDiscoverAPISpec(context, agent, res.statusCode);
await agent.getPendingEvents().waitUntilSent(getTimeoutInMS());
if (lastFlushStatsAt === undefined ||
lastFlushStatsAt + getFlushEveryMS() < performance.now()) {
await agent.flushStats(getTimeoutInMS());
lastFlushStatsAt = performance.now();
}
}
}
});
};
}
function incrementStatsAndDiscoverAPISpec(context, agent, statusCode) {
if (context.remoteAddress &&
agent.getConfig().isBypassedIP(context.remoteAddress)) {
return;
}
if (context.route && context.method && Number.isInteger(statusCode)) {
const shouldDiscover = (0, shouldDiscoverRoute_1.shouldDiscoverRoute)({
statusCode: statusCode,
method: context.method,
route: context.route,
});
if (shouldDiscover) {
agent.onRouteExecute(context);
}
if (context.remoteAddress &&
!context.blockedDueToIPOrBot &&
agent.getAttackWaveDetector().check(context)) {
agent.onDetectedAttackWave({
request: context,
});
agent.getInspectionStatistics().onAttackWaveDetected();
}
}
const stats = agent.getInspectionStatistics();
stats.onRequest();
}
class FunctionsFramework {
onRequire(exports, pkgInfo) {
(0, wrapExport_1.wrapExport)(exports, "http", pkgInfo, {
kind: undefined,
modifyArgs: (args) => {
if (args.length === 2 && typeof args[1] === "function") {
const httpFunction = args[1];
args[1] = createCloudFunctionWrapper(httpFunction);
}
return args;
},
});
}
wrap(hooks) {
hooks
.addPackage("@google-cloud/functions-framework")
.withVersion("^4.0.0 || ^3.0.0")
.onRequire((exports, pkgInfo) => {
this.onRequire(exports, pkgInfo);
})
.addFileInstrumentation({
path: "build/src/function_registry.js",
functions: [],
accessLocalVariables: {
names: ["module.exports"],
cb: (vars, pkgInfo) => {
if (vars.length > 0 && (0, isPlainObject_1.isPlainObject)(vars[0])) {
const exports = vars[0];
this.onRequire(exports, pkgInfo);
}
},
},
});
}
}
exports.FunctionsFramework = FunctionsFramework;