Skip to content
17 changes: 17 additions & 0 deletions library/agent/Agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { wrapInstalledPackages } from "./wrapInstalledPackages";
import { Wrapper } from "./Wrapper";
import { isAikidoCI } from "../helpers/isAikidoCI";
import { AttackLogger } from "./AttackLogger";
import { Packages } from "./Packages";

type WrappedPackage = { version: string | null; supported: boolean };

Expand All @@ -38,6 +39,7 @@ export class Agent {
private preventedPrototypePollution = false;
private incompatiblePackages: Record<string, string> = {};
private wrappedPackages: Record<string, WrappedPackage> = {};
private packages = new Packages();
private timeoutInMS = 30 * 1000;
private hostnames = new Hostnames(200);
private users = new Users(1000);
Expand Down Expand Up @@ -296,11 +298,13 @@ export class Agent {
const routes = this.routes.asArray();
const outgoingDomains = this.hostnames.asArray();
const users = this.users.asArray();
const packages = this.packages.asArray();
const endedAt = Date.now();
this.statistics.reset();
this.routes.clear();
this.hostnames.clear();
this.users.clear();
this.packages.clear();
const response = await this.api.report(
this.token,
{
Expand All @@ -313,6 +317,7 @@ export class Agent {
endedAt: endedAt,
requests: stats.requests,
},
packages,
hostnames: outgoingDomains,
routes: routes,
users: users,
Expand Down Expand Up @@ -492,11 +497,23 @@ export class Agent {
this.logger.log(`Failed to wrap module ${module}: ${error.message}`);
}

onPackageRequired(name: string, version: string) {
this.packages.addPackage({
name,
version,
});
}

getPackages() {
return this.packages;
}

onPackageWrapped(name: string, details: WrappedPackage) {
if (this.wrappedPackages[name]) {
// Already reported as wrapped
return;
}

this.wrappedPackages[name] = details;

if (details.version) {
Expand Down
34 changes: 34 additions & 0 deletions library/agent/Packages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
type PackageInfo = {
name: string;
version: string;
requiredAt: number;
};

export class Packages {
private packages: Map<string, PackageInfo[]> = new Map();

addPackage(pkg: { name: string; version: string }) {
const versions = this.packages.get(pkg.name) || [];
const existingVersion = versions.find((v) => v.version === pkg.version);

if (existingVersion) {
return;
}

versions.push({
name: pkg.name,
version: pkg.version,
requiredAt: Date.now(),
});

this.packages.set(pkg.name, versions);
}

asArray() {
return Array.from(this.packages.values()).flat();
}

clear() {
this.packages.clear();
}
}
10 changes: 9 additions & 1 deletion library/agent/api/Event.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Kind } from "../Attack";
import { Source } from "../Source";

type Version = string;
type PackageName = string;

export type AgentInfo = {
dryMode: boolean;
hostname: string;
version: string;
library: string;
packages: Record<string, string>;
packages: Record<PackageName, Version>;
ipAddress: string;
preventedPrototypePollution: boolean;
incompatiblePackages: {
Expand Down Expand Up @@ -94,6 +97,11 @@ type Heartbeat = {
};
};
};
packages: {
name: string;
version: string;
requiredAt: number;
}[];
hostnames: { hostname: string; port: number | undefined; hits: number }[];
routes: {
path: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ function generateHeartbeatEvent(): Event {
hostnames: [],
routes: [],
users: [],
packages: [],
};
}

Expand Down
26 changes: 13 additions & 13 deletions library/agent/hooks/wrapRequire.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,11 +188,6 @@ function patchPackage(this: mod, id: string, originalExports: unknown) {
.map((pkg) => pkg.getVersions())
.flat();

// We don't want to patch this package because we do not have any hooks for it
if (!versionedPackages.length) {
return originalExports;
}

// Read the package.json of the required package
const packageJson = originalRequire(
`${pathInfo.base}/package.json`
Expand All @@ -206,19 +201,24 @@ function patchPackage(this: mod, id: string, originalExports: unknown) {
);
}

const agent = getInstance();
agent?.onPackageRequired(moduleName, installedPkgVersion);

// We don't want to patch this package because we do not have any hooks for it
if (!versionedPackages.length) {
return originalExports;
}

// Check if the installed package version is supported (get all matching versioned packages)
const matchingVersionedPackages = versionedPackages.filter((pkg) =>
satisfiesVersion(pkg.getRange(), installedPkgVersion)
);

const agent = getInstance();
if (agent) {
// Report to the agent that the package was wrapped or not if it's version is not supported
agent.onPackageWrapped(moduleName, {
version: installedPkgVersion,
supported: !!matchingVersionedPackages.length,
});
}
// Report to the agent that the package was wrapped or not if it's version is not supported
agent?.onPackageWrapped(moduleName, {
version: installedPkgVersion,
supported: !!matchingVersionedPackages.length,
});

if (!matchingVersionedPackages.length) {
// We don't want to patch this package version
Expand Down
Loading