@aikidosec/runtime
Advanced tools
Comparing version 1.5.10 to 1.5.11
@@ -24,2 +24,3 @@ import { API } from "./api/API"; | ||
private wrappedPackages; | ||
private timeoutInMS; | ||
private statistics; | ||
@@ -59,2 +60,3 @@ constructor(block: boolean, logger: Logger, api: API, token: Token | undefined, serverless: string | undefined); | ||
private heartbeat; | ||
private sendHeartbeat; | ||
/** | ||
@@ -67,2 +69,3 @@ * Starts a heartbeat when not in serverless mode : Make contact with api every x seconds. | ||
onFailedToWrapMethod(module: string, name: string): void; | ||
flushStats(timeoutInMS: number): void; | ||
} |
@@ -29,2 +29,3 @@ "use strict"; | ||
this.wrappedPackages = {}; | ||
this.timeoutInMS = 5000; | ||
this.statistics = new InspectionStatistics_1.InspectionStatistics({ | ||
@@ -68,3 +69,3 @@ maxPerfSamplesInMemory: 5000, | ||
agent: this.getAgentInfo(), | ||
}) | ||
}, this.timeoutInMS) | ||
.catch(() => { | ||
@@ -110,3 +111,3 @@ this.logger.log("Failed to report started event"); | ||
agent: this.getAgentInfo(), | ||
}) | ||
}, this.timeoutInMS) | ||
.catch(() => { | ||
@@ -120,3 +121,8 @@ this.logger.log("Failed to report attack"); | ||
*/ | ||
heartbeat() { | ||
heartbeat(timeoutInMS = this.timeoutInMS) { | ||
this.sendHeartbeat(timeoutInMS).catch(() => { | ||
this.logger.log("Failed to do heartbeat"); | ||
}); | ||
} | ||
async sendHeartbeat(timeoutInMS) { | ||
if (this.token) { | ||
@@ -127,4 +133,3 @@ this.logger.log("Heartbeat..."); | ||
this.statistics.reset(); | ||
this.api | ||
.report(this.token, { | ||
await this.api.report(this.token, { | ||
type: "heartbeat", | ||
@@ -139,6 +144,3 @@ time: Date.now(), | ||
}, | ||
}) | ||
.catch(() => { | ||
this.logger.log("Failed to do heartbeat"); | ||
}); | ||
}, timeoutInMS); | ||
} | ||
@@ -236,3 +238,7 @@ } | ||
} | ||
flushStats(timeoutInMS) { | ||
this.statistics.forceCompress(); | ||
this.heartbeat(timeoutInMS); | ||
} | ||
} | ||
exports.Agent = Agent; |
@@ -11,4 +11,4 @@ import { Event } from "./Event"; | ||
export interface API { | ||
report(token: Token, event: Event): Promise<APIResult>; | ||
report(token: Token, event: Event, timeoutInMS: number): Promise<APIResult>; | ||
} | ||
export {}; |
@@ -6,7 +6,6 @@ import { API, APIResult } from "./API"; | ||
private readonly reportingUrl; | ||
private readonly timeoutInMS; | ||
constructor(reportingUrl: URL, timeoutInMS?: number); | ||
constructor(reportingUrl: URL); | ||
private toAPIResult; | ||
private fetch; | ||
report(token: Token, event: Event): Promise<APIResult>; | ||
report(token: Token, event: Event, timeoutInMS: number): Promise<APIResult>; | ||
} |
@@ -7,5 +7,4 @@ "use strict"; | ||
class APIFetch { | ||
constructor(reportingUrl, timeoutInMS = 5000) { | ||
constructor(reportingUrl) { | ||
this.reportingUrl = reportingUrl; | ||
this.timeoutInMS = timeoutInMS; | ||
} | ||
@@ -45,3 +44,3 @@ toAPIResult(response) { | ||
} | ||
async report(token, event) { | ||
async report(token, event, timeoutInMS) { | ||
const abort = new AbortController(); | ||
@@ -61,3 +60,3 @@ return await Promise.race([ | ||
resolve({ success: false, error: "timeout" }); | ||
}, this.timeoutInMS)), | ||
}, timeoutInMS)), | ||
]); | ||
@@ -64,0 +63,0 @@ } |
@@ -6,7 +6,8 @@ import { Token } from "./Token"; | ||
private result; | ||
private readonly events; | ||
private events; | ||
constructor(result?: APIResult); | ||
setResult(result: APIResult): void; | ||
report(token: Token, event: Event): Promise<APIResult>; | ||
report(token: Token, event: Event, timeoutInMS: number): Promise<APIResult>; | ||
clear(): void; | ||
getEvents(): Event[]; | ||
} |
@@ -12,6 +12,9 @@ "use strict"; | ||
} | ||
async report(token, event) { | ||
async report(token, event, timeoutInMS) { | ||
this.events.push(event); | ||
return this.result; | ||
} | ||
clear() { | ||
this.events = []; | ||
} | ||
getEvents() { | ||
@@ -18,0 +21,0 @@ return this.events; |
@@ -14,4 +14,4 @@ import { API, APIResult } from "./API"; | ||
constructor(api: API, { maxEventsPerInterval, intervalInMs }: ThrottleOptions); | ||
report(token: Token, event: Event): Promise<APIResult>; | ||
report(token: Token, event: Event, timeoutInMS: number): Promise<APIResult>; | ||
} | ||
export {}; |
@@ -11,3 +11,3 @@ "use strict"; | ||
} | ||
async report(token, event) { | ||
async report(token, event, timeoutInMS) { | ||
if (event.type === "detected_attack") { | ||
@@ -25,5 +25,5 @@ const currentTime = Date.now(); | ||
} | ||
return await this.api.report(token, event); | ||
return await this.api.report(token, event, timeoutInMS); | ||
} | ||
} | ||
exports.APIRateLimitedClientSide = APIRateLimitedClientSide; |
@@ -9,3 +9,3 @@ import { API, APIResult } from "./API"; | ||
constructor(api: API); | ||
report(token: Token, event: Event): Promise<APIResult>; | ||
report(token: Token, event: Event, timeoutInMS: number): Promise<APIResult>; | ||
} |
@@ -10,3 +10,3 @@ "use strict"; | ||
} | ||
async report(token, event) { | ||
async report(token, event, timeoutInMS) { | ||
if (typeof this.rateLimitedAt === "number" && | ||
@@ -16,3 +16,3 @@ Date.now() - this.rateLimitedAt < this.stopSendingForInMilliseconds) { | ||
} | ||
const result = await this.api.report(token, event); | ||
const result = await this.api.report(token, event, timeoutInMS); | ||
if (!result.success && result.error === "rate_limited") { | ||
@@ -19,0 +19,0 @@ this.rateLimitedAt = Date.now(); |
@@ -5,3 +5,3 @@ import { API, APIResult } from "./API"; | ||
export declare class APIThatThrows implements API { | ||
report(token: Token, event: Event): Promise<APIResult>; | ||
report(token: Token, event: Event, timeoutInMS: number): Promise<APIResult>; | ||
} |
@@ -5,3 +5,3 @@ "use strict"; | ||
class APIThatThrows { | ||
async report(token, event) { | ||
async report(token, event, timeoutInMS) { | ||
throw new Error("Failed to report event"); | ||
@@ -8,0 +8,0 @@ } |
@@ -8,3 +8,3 @@ import { API, APIResult } from "./API"; | ||
constructor(api: API); | ||
report(token: Token, event: Event): Promise<APIResult>; | ||
report(token: Token, event: Event, timeoutInMS: number): Promise<APIResult>; | ||
} |
@@ -9,7 +9,7 @@ "use strict"; | ||
} | ||
async report(token, event) { | ||
async report(token, event, timeoutInMS) { | ||
if (this.tokenIsInvalid) { | ||
return { success: false, error: "invalid_token" }; | ||
} | ||
const result = await this.api.report(token, event); | ||
const result = await this.api.report(token, event, timeoutInMS); | ||
if (!result.success && result.error === "invalid_token") { | ||
@@ -16,0 +16,0 @@ this.tokenIsInvalid = true; |
@@ -12,3 +12,2 @@ "use strict"; | ||
const ModifyingArgumentsInterceptor_1 = require("./hooks/ModifyingArgumentsInterceptor"); | ||
const Source_1 = require("./Source"); | ||
/** | ||
@@ -140,3 +139,3 @@ * Hooks allows you to register packages and then wrap specific methods on | ||
if (agent.shouldBlock()) { | ||
throw new Error(`Aikido runtime has blocked a ${(0, Attack_1.attackKindHumanName)(result.kind)}: ${result.operation}(...) originating from ${(0, Source_1.sourceHumanName)(result.source)}${result.pathToPayload}`); | ||
throw new Error(`Aikido runtime has blocked a ${(0, Attack_1.attackKindHumanName)(result.kind)}: ${result.operation}(...) originating from ${result.source}${result.pathToPayload}`); | ||
} | ||
@@ -143,0 +142,0 @@ } |
@@ -55,3 +55,4 @@ type SinkCompressedTimings = { | ||
}): void; | ||
forceCompress(): void; | ||
} | ||
export {}; |
@@ -119,3 +119,8 @@ "use strict"; | ||
} | ||
forceCompress() { | ||
for (const sink in this.stats) { | ||
this.compressPerfSamples(sink); | ||
} | ||
} | ||
} | ||
exports.InspectionStatistics = InspectionStatistics; |
export type Source = "query" | "body" | "headers" | "cookies"; | ||
/** | ||
* Returns the friendly name of a source type | ||
* @param source A source type (either "query", "body", "headers" or "cookies") | ||
* @returns A friendly name for each of these types | ||
*/ | ||
export declare function sourceHumanName(source: Source): string; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.sourceHumanName = void 0; | ||
/** | ||
* Returns the friendly name of a source type | ||
* @param source A source type (either "query", "body", "headers" or "cookies") | ||
* @returns A friendly name for each of these types | ||
*/ | ||
function sourceHumanName(source) { | ||
switch (source) { | ||
case "query": | ||
return "query parameters"; | ||
case "body": | ||
return "body"; | ||
case "headers": | ||
return "headers"; | ||
case "cookies": | ||
return "cookies"; | ||
} | ||
} | ||
exports.sourceHumanName = sourceHumanName; |
"use strict"; | ||
const AgentSingleton_1 = require("../agent/AgentSingleton"); | ||
const protect_1 = require("../agent/protect"); | ||
process.on("SIGTERM", () => { | ||
const agent = (0, AgentSingleton_1.getInstance)(); | ||
if (!agent) { | ||
return; | ||
} | ||
agent.flushStats(1000); | ||
}); | ||
module.exports = (0, protect_1.lambda)(); |
{ | ||
"name": "@aikidosec/runtime", | ||
"version": "1.5.10", | ||
"version": "1.5.11", | ||
"description": "Aikido runtime protects your application against NoSQL injections and more", | ||
@@ -5,0 +5,0 @@ "repository": "https://github.com/AikidoSec/runtime-node", |
@@ -14,2 +14,3 @@ # Aikido Runtime for Node.js | ||
* 🛡️ [Prototype pollution](./docs/prototype-pollution.md) | ||
* 🛡 [Path traversal attacks](https://owasp.org/www-community/attacks/Path_Traversal) | ||
* 🔥 More to come (See roadmap below) | ||
@@ -20,3 +21,2 @@ | ||
* [ ] Monitor outbound requests | ||
* [ ] Protect against path traversal attacks | ||
* [ ] Protect against SSRF attacks | ||
@@ -23,0 +23,0 @@ |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createLambdaWrapper = void 0; | ||
const AgentSingleton_1 = require("../agent/AgentSingleton"); | ||
const Context_1 = require("../agent/Context"); | ||
@@ -63,3 +64,3 @@ const isPlainObject_1 = require("../helpers/isPlainObject"); | ||
} | ||
function isProxyEvent(event) { | ||
function isGatewayEvent(event) { | ||
return (0, isPlainObject_1.isPlainObject)(event) && "httpMethod" in event && "headers" in event; | ||
@@ -70,13 +71,20 @@ } | ||
} | ||
// eslint-disable-next-line max-lines-per-function | ||
function createLambdaWrapper(handler) { | ||
const asyncHandler = convertToAsyncFunction(handler); | ||
const agent = (0, AgentSingleton_1.getInstance)(); | ||
return async (event, context) => { | ||
var _a, _b, _c, _d; | ||
let agentContext = undefined; | ||
if (isSQSEvent(event)) { | ||
const body = event.Records.map((record) => tryParseAsJSON(record.body)).filter((body) => body); | ||
return (0, Context_1.runWithContext)({ | ||
agentContext = { | ||
url: undefined, | ||
method: undefined, | ||
remoteAddress: undefined, | ||
body: body, | ||
body: { | ||
Records: body.map((record) => ({ | ||
body: record, | ||
})), | ||
}, | ||
headers: {}, | ||
@@ -86,8 +94,6 @@ query: {}, | ||
source: "lambda/sqs", | ||
}, async () => { | ||
return await asyncHandler(event, context); | ||
}); | ||
}; | ||
} | ||
if (isProxyEvent(event)) { | ||
return (0, Context_1.runWithContext)({ | ||
else if (isGatewayEvent(event)) { | ||
agentContext = { | ||
url: undefined, | ||
@@ -101,9 +107,22 @@ method: event.httpMethod, | ||
source: "lambda/gateway", | ||
}, async () => { | ||
return await asyncHandler(event, context); | ||
}; | ||
} | ||
if (!agentContext) { | ||
// We don't know what the type of the event is | ||
// We can't provide any context for the underlying sinks | ||
// So we just run the handler without any context | ||
return await asyncHandler(event, context); | ||
} | ||
const result = await (0, Context_1.runWithContext)(agentContext, async () => { | ||
return await asyncHandler(event, context); | ||
}); | ||
if (agent) { | ||
agent.getInspectionStatistics().onRequest({ | ||
blocked: agent.shouldBlock(), | ||
attackDetected: !!agentContext.attackDetected, | ||
}); | ||
} | ||
return await asyncHandler(event, context); | ||
return result; | ||
}; | ||
} | ||
exports.createLambdaWrapper = createLambdaWrapper; |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
174313
3773
3