@aikidosec/runtime
Advanced tools
Comparing version 1.5.17 to 1.5.18
@@ -40,3 +40,3 @@ import { ReportingAPI } from "./api/ReportingAPI"; | ||
*/ | ||
onStart(): void; | ||
onStart(): Promise<void>; | ||
onErrorThrownByInterceptor({ error, module, method, }: { | ||
@@ -73,2 +73,3 @@ error: Error; | ||
private startHeartbeats; | ||
private startPollingForConfigChanges; | ||
private getAgentInfo; | ||
@@ -75,0 +76,0 @@ start(wrappers: Wrapper[]): void; |
@@ -11,2 +11,3 @@ "use strict"; | ||
const limitLengthMetadata_1 = require("../helpers/limitLengthMetadata"); | ||
const pollForChanges_1 = require("./realtime/pollForChanges"); | ||
const Hostnames_1 = require("./Hostnames"); | ||
@@ -35,3 +36,3 @@ const InspectionStatistics_1 = require("./InspectionStatistics"); | ||
this.hostnames = new Hostnames_1.Hostnames(200); | ||
this.serviceConfig = new ServiceConfig_1.ServiceConfig([]); | ||
this.serviceConfig = new ServiceConfig_1.ServiceConfig([], Date.now()); | ||
this.routes = new Routes_1.Routes(200); | ||
@@ -72,14 +73,10 @@ this.statistics = new InspectionStatistics_1.InspectionStatistics({ | ||
*/ | ||
onStart() { | ||
async onStart() { | ||
if (this.token) { | ||
this.api | ||
.report(this.token, { | ||
const result = await this.api.report(this.token, { | ||
type: "started", | ||
time: Date.now(), | ||
agent: this.getAgentInfo(), | ||
}, this.timeoutInMS) | ||
.then((result) => this.updateServiceConfig(result)) | ||
.catch((error) => { | ||
this.logger.log("Failed to report started event"); | ||
}); | ||
}, this.timeoutInMS); | ||
this.updateServiceConfig(result); | ||
} | ||
@@ -143,3 +140,5 @@ } | ||
if (response.endpoints) { | ||
this.serviceConfig = new ServiceConfig_1.ServiceConfig(response.endpoints); | ||
this.serviceConfig = new ServiceConfig_1.ServiceConfig(response.endpoints, typeof response.configUpdatedAt === "number" | ||
? response.configUpdatedAt | ||
: Date.now()); | ||
} | ||
@@ -179,6 +178,10 @@ const minimumHeartbeatIntervalMS = 2 * 60 * 1000; | ||
startHeartbeats() { | ||
/* c8 ignore next 3 */ | ||
if (this.serverless) { | ||
throw new Error("Heartbeats in serverless mode are not supported"); | ||
this.logger.log("Running in serverless environment, not starting heartbeats"); | ||
return; | ||
} | ||
if (!this.token) { | ||
this.logger.log("No token provided, not starting heartbeats"); | ||
return; | ||
} | ||
/* c8 ignore next 3 */ | ||
@@ -201,2 +204,13 @@ if (this.interval) { | ||
} | ||
startPollingForConfigChanges() { | ||
(0, pollForChanges_1.pollForChanges)({ | ||
token: this.token, | ||
serverless: this.serverless, | ||
logger: this.logger, | ||
lastUpdatedAt: this.serviceConfig.getLastUpdatedAt(), | ||
onConfigUpdate: (config) => { | ||
this.updateServiceConfig({ success: true, ...config }); | ||
}, | ||
}); | ||
} | ||
getAgentInfo() { | ||
@@ -259,6 +273,12 @@ return { | ||
} | ||
this.onStart(); | ||
if (!this.serverless) { | ||
// Send startup event and wait for config | ||
// Then start heartbeats and polling for config changes | ||
this.onStart() | ||
.then(() => { | ||
this.startHeartbeats(); | ||
} | ||
this.startPollingForConfigChanges(); | ||
}) | ||
.catch(() => { | ||
this.logger.log("Failed to start agent"); | ||
}); | ||
} | ||
@@ -265,0 +285,0 @@ onFailedToWrapMethod(module, name) { |
@@ -13,2 +13,3 @@ import { Event } from "./Event"; | ||
heartbeatIntervalInMS?: number; | ||
configUpdatedAt?: number; | ||
} | { | ||
@@ -15,0 +16,0 @@ success: false; |
@@ -8,4 +8,3 @@ import { ReportingAPI, ReportingAPIResponse } from "./ReportingAPI"; | ||
private toAPIResponse; | ||
private fetch; | ||
report(token: Token, event: Event, timeoutInMS: number): Promise<ReportingAPIResponse>; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ReportingAPINodeHTTP = void 0; | ||
const http_1 = require("http"); | ||
const https_1 = require("https"); | ||
const fetch_1 = require("../../helpers/fetch"); | ||
class ReportingAPINodeHTTP { | ||
@@ -10,7 +9,7 @@ constructor(reportingUrl) { | ||
} | ||
toAPIResponse(response, data) { | ||
if (response.statusCode === 429) { | ||
toAPIResponse(statusCode, data) { | ||
if (statusCode === 429) { | ||
return { success: false, error: "rate_limited" }; | ||
} | ||
if (response.statusCode === 401) { | ||
if (statusCode === 401) { | ||
return { success: false, error: "invalid_token" }; | ||
@@ -25,34 +24,7 @@ } | ||
} | ||
async fetch(url, { signal, method, body, headers, }) { | ||
/* c8 ignore next */ | ||
const request = url.startsWith("https://") ? https_1.request : http_1.request; | ||
return new Promise((resolve, reject) => { | ||
const req = request(url, { | ||
method, | ||
headers, | ||
signal, | ||
}, (response) => { | ||
let data = ""; | ||
response.on("data", (chunk) => { | ||
data += chunk; | ||
}); | ||
response.on("end", () => { | ||
// We don't throw errors unless the request times out, is aborted or fails for low level reasons | ||
// Error objects are annoying to work with | ||
// That's why we use `resolve` instead of `reject` | ||
resolve(this.toAPIResponse(response, data)); | ||
}); | ||
}); | ||
req.on("error", (error) => { | ||
reject(error); | ||
}); | ||
req.write(body); | ||
req.end(); | ||
}); | ||
} | ||
async report(token, event, timeoutInMS) { | ||
const abort = new AbortController(); | ||
return await Promise.race([ | ||
this.fetch(this.reportingUrl.toString(), { | ||
signal: abort.signal, | ||
let response; | ||
try { | ||
response = await (0, fetch_1.fetch)({ | ||
url: new URL(`${this.reportingUrl.toString()}api/runtime/events`), | ||
method: "POST", | ||
@@ -64,10 +36,14 @@ headers: { | ||
body: JSON.stringify(event), | ||
}), | ||
new Promise((resolve) => setTimeout(() => { | ||
abort.abort(); | ||
resolve({ success: false, error: "timeout" }); | ||
}, timeoutInMS)), | ||
]); | ||
timeoutInMS, | ||
}); | ||
} | ||
catch (error) { | ||
if (error.message.includes("timed out")) { | ||
return { success: false, error: "timeout" }; | ||
} | ||
throw error; | ||
} | ||
return this.toAPIResponse(response.statusCode, response.body); | ||
} | ||
} | ||
exports.ReportingAPINodeHTTP = ReportingAPINodeHTTP; |
@@ -6,2 +6,3 @@ "use strict"; | ||
const Fetch_1 = require("../sinks/Fetch"); | ||
const FileSystem_1 = require("../sinks/FileSystem"); | ||
const HTTPRequest_1 = require("../sinks/HTTPRequest"); | ||
@@ -14,4 +15,4 @@ const MongoDB_1 = require("../sinks/MongoDB"); | ||
const Undici_1 = require("../sinks/Undici"); | ||
const Express_1 = require("../sources/Express"); | ||
const FunctionsFramework_1 = require("../sources/FunctionsFramework"); | ||
const Express_1 = require("../sources/Express"); | ||
const Lambda_1 = require("../sources/Lambda"); | ||
@@ -22,9 +23,9 @@ const PubSub_1 = require("../sources/PubSub"); | ||
const ReportingAPINodeHTTP_1 = require("./api/ReportingAPINodeHTTP"); | ||
const ReportingAPIRateLimitedClientSide_1 = require("./api/ReportingAPIRateLimitedClientSide"); | ||
const ReportingAPIRateLimitedServerSide_1 = require("./api/ReportingAPIRateLimitedServerSide"); | ||
const ReportingAPIRateLimitedClientSide_1 = require("./api/ReportingAPIRateLimitedClientSide"); | ||
const ReportingAPIThatValidatesToken_1 = require("./api/ReportingAPIThatValidatesToken"); | ||
const Token_1 = require("./api/Token"); | ||
const getAPIURL_1 = require("./getAPIURL"); | ||
const LoggerConsole_1 = require("./logger/LoggerConsole"); | ||
const LoggerNoop_1 = require("./logger/LoggerNoop"); | ||
const FileSystem_1 = require("../sinks/FileSystem"); | ||
function isDebugging() { | ||
@@ -56,7 +57,3 @@ return (process.env.AIKIDO_DEBUG === "true" || process.env.AIKIDO_DEBUG === "1"); | ||
function getAPI() { | ||
let url = new URL("https://guard.aikido.dev/api/runtime/events"); | ||
if (process.env.AIKIDO_URL) { | ||
url = new URL(process.env.AIKIDO_URL); | ||
} | ||
return validatesToken(serverSideRateLimited(clientSideRateLimited(new ReportingAPINodeHTTP_1.ReportingAPINodeHTTP(url)))); | ||
return validatesToken(serverSideRateLimited(clientSideRateLimited(new ReportingAPINodeHTTP_1.ReportingAPINodeHTTP((0, getAPIURL_1.getAPIURL)())))); | ||
} | ||
@@ -63,0 +60,0 @@ function getTokenFromEnv() { |
import { Endpoint } from "./api/ReportingAPI"; | ||
export declare class ServiceConfig { | ||
private readonly lastUpdatedAt; | ||
private endpoints; | ||
constructor(endpoints: Endpoint[]); | ||
constructor(endpoints: Endpoint[], lastUpdatedAt: number); | ||
private getKey; | ||
shouldProtectEndpoint(method: string, route: string | RegExp): boolean; | ||
getLastUpdatedAt(): number; | ||
} |
@@ -5,3 +5,4 @@ "use strict"; | ||
class ServiceConfig { | ||
constructor(endpoints) { | ||
constructor(endpoints, lastUpdatedAt) { | ||
this.lastUpdatedAt = lastUpdatedAt; | ||
this.endpoints = new Map(); | ||
@@ -27,3 +28,6 @@ endpoints.forEach((rule) => { | ||
} | ||
getLastUpdatedAt() { | ||
return this.lastUpdatedAt; | ||
} | ||
} | ||
exports.ServiceConfig = ServiceConfig; |
{ | ||
"name": "@aikidosec/runtime", | ||
"version": "1.5.17", | ||
"version": "1.5.18", | ||
"description": "Aikido runtime protects your application against NoSQL injections and more", | ||
@@ -5,0 +5,0 @@ "repository": "https://github.com/AikidoSec/runtime-node", |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
215131
197
4877
15
4