Skip to content

Commit c4158f4

Browse files
committed
Add kind to sink stats
To know what kind of operations the sink does
1 parent a193ec3 commit c4158f4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+803
-321
lines changed

library/agent/InspectionStatistics.test.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ t.test("it resets stats", async () => {
1616
blocked: false,
1717
durationInMs: 0.1,
1818
attackDetected: false,
19+
kind: "nosql_op",
1920
});
2021

2122
t.same(stats.getStats(), {
2223
sinks: {
2324
mongodb: {
25+
kind: "nosql_op",
2426
attacksDetected: {
2527
total: 0,
2628
blocked: 0,
@@ -89,11 +91,13 @@ t.test("it keeps track of amount of calls", async () => {
8991
blocked: false,
9092
durationInMs: 0.1,
9193
attackDetected: false,
94+
kind: "nosql_op",
9295
});
9396

9497
t.same(stats.getStats(), {
9598
sinks: {
9699
mongodb: {
100+
kind: "nosql_op",
97101
attacksDetected: {
98102
total: 0,
99103
blocked: 0,
@@ -121,11 +125,13 @@ t.test("it keeps track of amount of calls", async () => {
121125
blocked: false,
122126
durationInMs: 0.1,
123127
attackDetected: false,
128+
kind: "nosql_op",
124129
});
125130

126131
t.same(stats.getStats(), {
127132
sinks: {
128133
mongodb: {
134+
kind: "nosql_op",
129135
attacksDetected: {
130136
total: 0,
131137
blocked: 0,
@@ -147,11 +153,12 @@ t.test("it keeps track of amount of calls", async () => {
147153
},
148154
});
149155

150-
stats.interceptorThrewError("mongodb");
156+
stats.interceptorThrewError("mongodb", "nosql_op");
151157

152158
t.same(stats.getStats(), {
153159
sinks: {
154160
mongodb: {
161+
kind: "nosql_op",
155162
attacksDetected: {
156163
total: 0,
157164
blocked: 0,
@@ -179,11 +186,13 @@ t.test("it keeps track of amount of calls", async () => {
179186
blocked: false,
180187
durationInMs: 0.1,
181188
attackDetected: true,
189+
kind: "nosql_op",
182190
});
183191

184192
t.same(stats.getStats(), {
185193
sinks: {
186194
mongodb: {
195+
kind: "nosql_op",
187196
attacksDetected: {
188197
total: 1,
189198
blocked: 0,
@@ -211,11 +220,13 @@ t.test("it keeps track of amount of calls", async () => {
211220
blocked: true,
212221
durationInMs: 0.3,
213222
attackDetected: true,
223+
kind: "nosql_op",
214224
});
215225

216226
t.same(stats.getStats(), {
217227
sinks: {
218228
mongodb: {
229+
kind: "nosql_op",
219230
attacksDetected: {
220231
total: 2,
221232
blocked: 1,
@@ -248,13 +259,15 @@ t.test("it keeps track of amount of calls", async () => {
248259
blocked: false,
249260
durationInMs: i * 0.1,
250261
attackDetected: false,
262+
kind: "nosql_op",
251263
});
252264
}
253265

254266
t.same(stats.hasCompressedStats(), true);
255267
t.same(stats.getStats(), {
256268
sinks: {
257269
mongodb: {
270+
kind: "nosql_op",
258271
attacksDetected: {
259272
total: 2,
260273
blocked: 1,
@@ -302,6 +315,7 @@ t.test("it keeps track of amount of calls", async () => {
302315
blocked: false,
303316
durationInMs: i * 0.1,
304317
attackDetected: false,
318+
kind: "nosql_op",
305319
});
306320
}
307321

@@ -431,6 +445,7 @@ t.test("it force compresses stats", async () => {
431445
blocked: false,
432446
durationInMs: 0.1,
433447
attackDetected: false,
448+
kind: "nosql_op",
434449
});
435450

436451
t.same(stats.hasCompressedStats(), false);

library/agent/InspectionStatistics.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { percentiles } from "../helpers/percentiles";
2+
import { MonitoredSinkStatsKind } from "./api/Event";
23

34
type SinkCompressedTimings = {
45
averageInMS: number;
@@ -7,6 +8,7 @@ type SinkCompressedTimings = {
78
};
89

910
type SinkStats = {
11+
kind: MonitoredSinkStatsKind | undefined;
1012
withoutContext: number;
1113
total: number;
1214
// array where we accumulate durations for each sink-request (e.g. mysql.query)
@@ -87,6 +89,7 @@ export class InspectionStatistics {
8789
for (const sink in this.stats) {
8890
const sinkStats = this.stats[sink];
8991
sinks[sink] = {
92+
kind: sinkStats.kind,
9093
total: sinkStats.total,
9194
attacksDetected: {
9295
total: sinkStats.attacksDetected.total,
@@ -105,10 +108,14 @@ export class InspectionStatistics {
105108
};
106109
}
107110

108-
private ensureSinkStats(sink: string) {
111+
private ensureSinkStats(
112+
sink: string,
113+
kind: MonitoredSinkStatsKind | undefined
114+
) {
109115
if (!this.stats[sink]) {
110116
this.stats[sink] = {
111117
withoutContext: 0,
118+
kind: kind,
112119
total: 0,
113120
durations: [],
114121
compressedTimings: [],
@@ -119,6 +126,11 @@ export class InspectionStatistics {
119126
},
120127
};
121128
}
129+
130+
if (kind && !this.stats[sink].kind) {
131+
// if we don't have a kind yet, set it
132+
this.stats[sink].kind = kind;
133+
}
122134
}
123135

124136
private compressPerfSamples(sink: string) {
@@ -163,8 +175,11 @@ export class InspectionStatistics {
163175
this.stats[sink].durations = [];
164176
}
165177

166-
interceptorThrewError(sink: string) {
167-
this.ensureSinkStats(sink);
178+
interceptorThrewError(
179+
sink: string,
180+
kind: MonitoredSinkStatsKind | undefined
181+
) {
182+
this.ensureSinkStats(sink, kind);
168183
this.stats[sink].total += 1;
169184
this.stats[sink].interceptorThrewError += 1;
170185
}
@@ -186,18 +201,20 @@ export class InspectionStatistics {
186201

187202
onInspectedCall({
188203
sink,
204+
kind,
189205
blocked,
190206
attackDetected,
191207
durationInMs,
192208
withoutContext,
193209
}: {
194210
sink: string;
211+
kind: MonitoredSinkStatsKind | undefined;
195212
durationInMs: number;
196213
attackDetected: boolean;
197214
blocked: boolean;
198215
withoutContext: boolean;
199216
}) {
200-
this.ensureSinkStats(sink);
217+
this.ensureSinkStats(sink, kind);
201218

202219
this.stats[sink].total += 1;
203220

library/agent/api/Event.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,17 @@ export type DetectedAttack = {
6464
time: number;
6565
};
6666

67+
export type MonitoredSinkStatsKind =
68+
| "sql_op"
69+
| "nosql_op"
70+
| "outgoing_http_op"
71+
| "fs_op"
72+
| "path_op"
73+
| "exec_op"
74+
| "unserialize_op"
75+
| "graphql_op"
76+
| "eval_op";
77+
6778
type MonitoredSinkStats = {
6879
attacksDetected: {
6980
total: number;
@@ -72,6 +83,7 @@ type MonitoredSinkStats = {
7283
interceptorThrewError: number;
7384
withoutContext: number;
7485
total: number;
86+
kind: MonitoredSinkStatsKind | undefined;
7587
compressedTimings: {
7688
averageInMS: number;
7789
percentiles: Record<string, number>;

library/agent/applyHooks.test.ts

Lines changed: 52 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -35,26 +35,38 @@ t.test(
3535
const hooks = new Hooks();
3636

3737
let modifyCalled = false;
38-
hooks.addGlobal("fetch", {
39-
modifyArgs: (args) => {
40-
modifyCalled = true;
41-
return args;
38+
hooks.addGlobal(
39+
"fetch",
40+
{
41+
modifyArgs: (args) => {
42+
modifyCalled = true;
43+
return args;
44+
},
4245
},
43-
});
46+
"outgoing_http_op"
47+
);
4448

4549
let inspectCalled = false;
46-
hooks.addGlobal("atob", {
47-
inspectArgs: (args) => {
48-
inspectCalled = true;
50+
hooks.addGlobal(
51+
"atob",
52+
{
53+
inspectArgs: (args) => {
54+
inspectCalled = true;
55+
},
4956
},
50-
});
57+
"outgoing_http_op"
58+
);
5159

5260
// Unknown global
53-
hooks.addGlobal("unknown", {
54-
inspectArgs: (args) => {
55-
return;
61+
hooks.addGlobal(
62+
"unknown",
63+
{
64+
inspectArgs: (args) => {
65+
return;
66+
},
5667
},
57-
});
68+
"outgoing_http_op"
69+
);
5870

5971
// Without name
6072
// @ts-expect-error Test with invalid arguments
@@ -90,11 +102,17 @@ t.test("it ignores route if force protection off is on", async (t) => {
90102

91103
const hooks = new Hooks();
92104
hooks.addBuiltinModule("dns/promises").onRequire((exports, pkgInfo) => {
93-
wrapExport(exports, "lookup", pkgInfo, {
94-
inspectArgs: (args, agent) => {
95-
inspectionCalls.push({ args });
105+
wrapExport(
106+
exports,
107+
"lookup",
108+
pkgInfo,
109+
{
110+
inspectArgs: (args, agent) => {
111+
inspectionCalls.push({ args });
112+
},
96113
},
97-
});
114+
"outgoing_http_op"
115+
);
98116
});
99117

100118
applyHooks(hooks);
@@ -156,18 +174,24 @@ t.test("it ignores route if force protection off is on", async (t) => {
156174
t.test("it does not report attack if IP is allowed", async (t) => {
157175
const hooks = new Hooks();
158176
hooks.addBuiltinModule("os").onRequire((exports, pkgInfo) => {
159-
wrapExport(exports, "hostname", pkgInfo, {
160-
inspectArgs: (args, agent) => {
161-
return {
162-
operation: "os.hostname",
163-
source: "body",
164-
pathsToPayload: ["path"],
165-
payload: "payload",
166-
metadata: {},
167-
kind: "path_traversal",
168-
};
177+
wrapExport(
178+
exports,
179+
"hostname",
180+
pkgInfo,
181+
{
182+
inspectArgs: (args, agent) => {
183+
return {
184+
operation: "os.hostname",
185+
source: "body",
186+
pathsToPayload: ["path"],
187+
payload: "payload",
188+
metadata: {},
189+
kind: "path_traversal",
190+
};
191+
},
169192
},
170-
});
193+
"path_op"
194+
);
171195
});
172196

173197
applyHooks(hooks);

library/agent/applyHooks.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ export function applyHooks(hooks: Hooks) {
3232
name: name,
3333
type: "global",
3434
},
35-
g.getInterceptors()
35+
g.getInterceptors(),
36+
g.getKind()
3637
);
3738
});
3839
}

library/agent/hooks/Global.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import { MonitoredSinkStatsKind } from "../api/Event";
12
import { InterceptorObject } from "./wrapExport";
23

34
export class Global {
45
constructor(
56
private readonly name: string,
6-
private readonly interceptors: InterceptorObject
7+
private readonly interceptors: InterceptorObject,
8+
private readonly kind: MonitoredSinkStatsKind
79
) {
810
if (!this.name) {
911
throw new Error("Name is required");
@@ -20,4 +22,8 @@ export class Global {
2022
getInterceptors() {
2123
return this.interceptors;
2224
}
25+
26+
getKind() {
27+
return this.kind;
28+
}
2329
}

library/agent/hooks/Hooks.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { MonitoredSinkStatsKind } from "../api/Event";
12
import { BuiltinModule } from "./BuiltinModule";
23
import { Global } from "./Global";
34
import { Package } from "./Package";
@@ -15,8 +16,12 @@ export class Hooks {
1516
return pkg;
1617
}
1718

18-
addGlobal(name: string, interceptors: InterceptorObject) {
19-
const global = new Global(name, interceptors);
19+
addGlobal(
20+
name: string,
21+
interceptors: InterceptorObject,
22+
kind: MonitoredSinkStatsKind
23+
) {
24+
const global = new Global(name, interceptors, kind);
2025
this.globals.push(global);
2126
}
2227

0 commit comments

Comments
 (0)