@contrast/agent 4.22.0 → 4.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/assess/models/call-context.js +13 -6
- package/lib/assess/propagators/JSON/stringify.js +3 -1
- package/lib/assess/sinks/dynamo.js +0 -1
- package/lib/core/arch-components/mongodb.js +140 -66
- package/lib/core/async-storage/index.js +1 -1
- package/lib/core/express/index.js +75 -29
- package/lib/protect/sinks/mongodb.js +77 -65
- package/lib/reporter/translations/to-protobuf/dtm/finding.js +11 -7
- package/package.json +2 -2
|
@@ -103,18 +103,25 @@ module.exports = class CallContext {
|
|
|
103
103
|
return !!(str && typeof str === 'object' && str[PROXY_TARGET]);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
static getDisplayRange(arg) {
|
|
106
|
+
static getDisplayRange(arg, orgArg = arg, iteration = 0) {
|
|
107
107
|
if (tracker.getData(arg)) {
|
|
108
108
|
return new TagRange(0, arg.length - 1, 'untrusted');
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
if (arg && typeof arg === 'object') {
|
|
112
112
|
for (const key in arg) {
|
|
113
|
+
if (arg[key] && typeof arg[key] === 'object' && iteration < 5) {
|
|
114
|
+
const nestedDisplayRange = CallContext.getDisplayRange(arg[key], orgArg, iteration += 1);
|
|
115
|
+
if (!_.isEmpty(nestedDisplayRange)) {
|
|
116
|
+
return nestedDisplayRange;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
113
119
|
const trackedData = tracker.getData(arg[key]);
|
|
114
|
-
if (trackedData) {
|
|
120
|
+
if (trackedData && trackedData.tagRanges.length > 0) {
|
|
115
121
|
const { start, stop } = trackedData.tagRanges[0];
|
|
122
|
+
const offset = Array.isArray(orgArg) ? 2 : 0;
|
|
116
123
|
const taintedString = arg[key].substring(start, stop + 1);
|
|
117
|
-
const taintRangeStart = CallContext.valueString(
|
|
124
|
+
const taintRangeStart = CallContext.valueString(orgArg).indexOf(taintedString) + offset;
|
|
118
125
|
if (taintRangeStart === -1) {
|
|
119
126
|
// If tracked string is not in the abbreviated stringified obj, disable highlighting
|
|
120
127
|
return new TagRange(0, 0, 'disable-highlighting');
|
|
@@ -178,9 +185,9 @@ module.exports = class CallContext {
|
|
|
178
185
|
return value.toString();
|
|
179
186
|
}
|
|
180
187
|
|
|
181
|
-
const
|
|
188
|
+
const type = _.get(value, 'constructor.name', 'null');
|
|
182
189
|
|
|
183
|
-
if (
|
|
190
|
+
if ((type === 'Object' || type === 'Array') && value) {
|
|
184
191
|
// make string representation uniform with no new lines and consistent spaces
|
|
185
192
|
let str = util
|
|
186
193
|
.inspect(value)
|
|
@@ -192,7 +199,7 @@ module.exports = class CallContext {
|
|
|
192
199
|
|
|
193
200
|
return str;
|
|
194
201
|
}
|
|
195
|
-
return
|
|
202
|
+
return type;
|
|
196
203
|
}
|
|
197
204
|
};
|
|
198
205
|
|
|
@@ -331,7 +331,9 @@ module.exports.handle = function() {
|
|
|
331
331
|
// always exist, even if an empty array, for production code.
|
|
332
332
|
if (metadata.sourceEvents && !metadata.sourceEvents.length) {
|
|
333
333
|
const { sourceEvents, braned } = data.metadata;
|
|
334
|
-
|
|
334
|
+
// If it is a source membrane (could be deserialization membrane), then
|
|
335
|
+
// we need to get a source event.
|
|
336
|
+
if (braned && braned.membrane && braned.membrane.makeReqSourceEvent) {
|
|
335
337
|
sourceEvents.push(
|
|
336
338
|
braned.membrane.makeReqSourceEvent(data.result.length)
|
|
337
339
|
);
|
|
@@ -19,36 +19,74 @@ const { PATCH_TYPES } = require('../../constants');
|
|
|
19
19
|
const ModuleHook = require('../../hooks/require');
|
|
20
20
|
const patcher = require('../../hooks/patcher');
|
|
21
21
|
const logger = require('../logger')('contrast:arch-component');
|
|
22
|
-
const semver = require('semver');
|
|
23
22
|
|
|
23
|
+
// Architecture component for versions <3.0.0
|
|
24
24
|
ModuleHook.resolve(
|
|
25
25
|
{
|
|
26
26
|
name: 'mongodb',
|
|
27
27
|
file: 'lib/mongo_client.js',
|
|
28
|
-
version: '
|
|
28
|
+
version: '<3.0.0'
|
|
29
29
|
},
|
|
30
|
-
(MongoClient
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
30
|
+
(MongoClient) => {
|
|
31
|
+
patcher.patch(MongoClient, 'connect', {
|
|
32
|
+
name: 'MongoClient.connect.arch_component',
|
|
33
|
+
patchType: PATCH_TYPES.ARCH_COMPONENT,
|
|
34
|
+
alwaysRun: true,
|
|
35
|
+
pre(ctx) {
|
|
36
|
+
// check if typeof callback == 'function'
|
|
37
|
+
// if yes:
|
|
38
|
+
// - MongoClient.connect executes a cb function, which has access to the connection status
|
|
39
|
+
// if not:
|
|
40
|
+
// - MongoClient.connect should return a promise and we can check it's result in another hook, just as before
|
|
41
|
+
const callbackIndex = ctx.args[2] ? 2 : 1;
|
|
42
|
+
if (ctx.args[callbackIndex] instanceof Function || typeof ctx.args[callbackIndex] === 'function') {
|
|
43
|
+
ctx.args[callbackIndex] = patcher.patch(ctx.args[callbackIndex], {
|
|
44
|
+
name: 'MongoClient.connect.callback.arch_component',
|
|
45
|
+
patchType: PATCH_TYPES.ARCH_COMPONENT,
|
|
46
|
+
alwaysRun: true,
|
|
47
|
+
pre(ctx) {
|
|
48
|
+
const [, db] = ctx.args;
|
|
49
|
+
if (db && db.s.topology && db.s.topology.s) {
|
|
50
|
+
try {
|
|
51
|
+
const server = db.s.topology.s.server.s;
|
|
52
|
+
if (server.pool && server.pool.state == 'connected') {
|
|
53
|
+
const connections = server.pool.availableConnections;
|
|
54
|
+
for (const c of connections) {
|
|
55
|
+
agentEmitter.emit('architectureComponent', {
|
|
56
|
+
vendor: 'MongoDB',
|
|
57
|
+
url: `mongodb://${c.host}:${c.port}`,
|
|
58
|
+
remoteHost: '',
|
|
59
|
+
remotePort: c.port
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} catch (err) {
|
|
64
|
+
logger.warn('unable to report MongoDB architecture component, err: %o', err);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
post(ctx) {
|
|
72
|
+
if (!ctx.result || !ctx.result.then) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
// it never gets here if callbacks are used, because the result won't be then-able
|
|
76
|
+
ctx.result.then(db => {
|
|
77
|
+
if (db && db.s && db.s.topology && db.s.topology.s) {
|
|
43
78
|
try {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
79
|
+
const server = db.s.topology.s.server.s;
|
|
80
|
+
if (server.pool && server.pool.state == 'connected') {
|
|
81
|
+
const connections = server.pool.availableConnections;
|
|
82
|
+
for (const c of connections) {
|
|
83
|
+
agentEmitter.emit('architectureComponent', {
|
|
84
|
+
vendor: 'MongoDB',
|
|
85
|
+
url: `mongodb://${c.host}:${c.port}`,
|
|
86
|
+
remoteHost: '',
|
|
87
|
+
remotePort: c.port
|
|
88
|
+
});
|
|
89
|
+
}
|
|
52
90
|
}
|
|
53
91
|
} catch (err) {
|
|
54
92
|
logger.warn(
|
|
@@ -56,60 +94,96 @@ ModuleHook.resolve(
|
|
|
56
94
|
err,
|
|
57
95
|
);
|
|
58
96
|
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// Architecture component for versions >=3.3.0 <4.0.0
|
|
105
|
+
ModuleHook.resolve(
|
|
106
|
+
{
|
|
107
|
+
name: 'mongodb',
|
|
108
|
+
file: 'lib/mongo_client.js',
|
|
109
|
+
version: '>=3.0.0 <4.0.0'
|
|
110
|
+
},
|
|
111
|
+
(MongoClient, { version }) => {
|
|
112
|
+
patcher.patch(MongoClient.prototype, 'connect', {
|
|
113
|
+
name: 'MongoClient.connect.arch_component',
|
|
114
|
+
patchType: PATCH_TYPES.ARCH_COMPONENT,
|
|
115
|
+
alwaysRun: true,
|
|
116
|
+
post(ctx) {
|
|
117
|
+
if (!ctx.result || !ctx.result.then) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// We should report only when connection is successful
|
|
122
|
+
ctx.result.then(function(client) {
|
|
123
|
+
try {
|
|
124
|
+
const { servers = [] } = ctx.obj.s && ctx.obj.s.options;
|
|
125
|
+
for (const server of servers) {
|
|
126
|
+
agentEmitter.emit('architectureComponent', {
|
|
127
|
+
vendor: 'MongoDB',
|
|
128
|
+
url: `mongodb://${server.host}:${server.port}`,
|
|
129
|
+
remoteHost: '',
|
|
130
|
+
remotePort: server.port,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
} catch (err) {
|
|
134
|
+
logger.warn(
|
|
135
|
+
'unable to report MongoDB architecture component, err: %o',
|
|
136
|
+
err,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
},
|
|
141
|
+
});
|
|
63
142
|
},
|
|
64
143
|
);
|
|
65
144
|
|
|
66
|
-
|
|
67
|
-
* It's not limited in the require hook to >=4.0.0 because
|
|
68
|
-
* that would result in confusing logs for the customer that
|
|
69
|
-
* we don't support older versions (which is not true) */
|
|
145
|
+
// Architecture component for versions >=4.0.0
|
|
70
146
|
ModuleHook.resolve(
|
|
71
147
|
{
|
|
72
148
|
name: 'mongodb',
|
|
73
|
-
version: '>=
|
|
149
|
+
version: '>=4.0.0'
|
|
74
150
|
},
|
|
75
151
|
(MongoDB, { version }) => {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
152
|
+
patcher.patch(MongoDB.MongoClient.prototype, 'connect', {
|
|
153
|
+
name: 'MongoClient.connect.arch_component',
|
|
154
|
+
patchType: PATCH_TYPES.ARCH_COMPONENT,
|
|
155
|
+
alwaysRun: true,
|
|
156
|
+
post(ctx) {
|
|
157
|
+
if (!ctx.result || !ctx.result.then) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
85
160
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
161
|
+
// We should report only when connection is successful
|
|
162
|
+
ctx.result.then(function(client) {
|
|
163
|
+
if (client && client.topology && client.topology.s) {
|
|
164
|
+
try {
|
|
165
|
+
const { servers } = client.topology.s;
|
|
166
|
+
for (const [, server] of servers) {
|
|
167
|
+
if (server.s && server.s.state === 'connected') {
|
|
168
|
+
const { srvServiceName } = server.s.options;
|
|
169
|
+
const { address } = server.s.description;
|
|
170
|
+
agentEmitter.emit('architectureComponent', {
|
|
171
|
+
vendor: 'MongoDB',
|
|
172
|
+
url: `${srvServiceName}://${address}`,
|
|
173
|
+
remoteHost: '',
|
|
174
|
+
remotePort: address.split(':').pop()
|
|
175
|
+
});
|
|
102
176
|
}
|
|
103
|
-
} catch (err) {
|
|
104
|
-
logger.warn(
|
|
105
|
-
'unable to report MongoDB architecture component, err: %o',
|
|
106
|
-
err,
|
|
107
|
-
);
|
|
108
177
|
}
|
|
178
|
+
} catch (err) {
|
|
179
|
+
logger.warn(
|
|
180
|
+
'unable to report MongoDB architecture component, err: %o',
|
|
181
|
+
err,
|
|
182
|
+
);
|
|
109
183
|
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
},
|
|
187
|
+
});
|
|
114
188
|
},
|
|
115
189
|
);
|
|
@@ -59,6 +59,7 @@ class ExpressFramework {
|
|
|
59
59
|
// Express middleware error handler.
|
|
60
60
|
this.errorHandler = ExpressFramework.ContrastErrorHandler.bind(this);
|
|
61
61
|
moduleHook.resolve({ name: 'express' }, this.hookExpress.bind(this));
|
|
62
|
+
moduleHook.resolve({ name: 'body-parser' }, this.hookBodyParser.bind(this));
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
/**
|
|
@@ -227,6 +228,80 @@ class ExpressFramework {
|
|
|
227
228
|
agentEmitter.on(HTTP_EVENTS.SERVER_LISTEN, this.onServerListen.bind(this));
|
|
228
229
|
}
|
|
229
230
|
|
|
231
|
+
hookBodyParser(bodyParser) {
|
|
232
|
+
const instrumentation = this;
|
|
233
|
+
const origBodyParser = bodyParser;
|
|
234
|
+
|
|
235
|
+
const { json: origJson, raw: origRaw, text: origText, urlencoded: origUrlencoded } = bodyParser;
|
|
236
|
+
const fnArr = [
|
|
237
|
+
{
|
|
238
|
+
key: 'json',
|
|
239
|
+
original: origJson,
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
key: 'raw',
|
|
243
|
+
original: origRaw,
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
key: 'text',
|
|
247
|
+
original: origText,
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
key: 'urlencoded',
|
|
251
|
+
original: origUrlencoded,
|
|
252
|
+
}
|
|
253
|
+
];
|
|
254
|
+
|
|
255
|
+
bodyParser = function bodyParser(...args) {
|
|
256
|
+
const parser = origBodyParser(...args);
|
|
257
|
+
const hookedParser = function(req, res, next) {
|
|
258
|
+
parser(req, res, instrumentation.contrastNext(req, res, next, 'bodyParser'));
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
Object.defineProperty(hookedParser, 'name', {
|
|
262
|
+
value: 'bodyParser'
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
return hookedParser;
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
fnArr.forEach((fn) => {
|
|
269
|
+
const fnName = `bodyParser.${fn.key}`;
|
|
270
|
+
function contrastHooked(...args) {
|
|
271
|
+
const parser = fn.original(...args);
|
|
272
|
+
const hookedParser = function (req, res, next) {
|
|
273
|
+
parser(req, res, instrumentation.contrastNext(req, res, next, fnName));
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
Object.defineProperty(hookedParser, 'name', {
|
|
277
|
+
value: `${fn.key}Parser`
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
return hookedParser;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
Object.defineProperty(bodyParser, fn.key, {
|
|
284
|
+
configurable: true,
|
|
285
|
+
enumerable: true,
|
|
286
|
+
get: () => contrastHooked,
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
return bodyParser;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
contrastNext(req, res, origNext, fnName) {
|
|
294
|
+
return function next() {
|
|
295
|
+
if (fnName == 'bodyParser.json') {
|
|
296
|
+
agentEmitter.emit(EVENTS.BODY_PARSED, req, res, INPUT_TYPES.JSON_VALUE);
|
|
297
|
+
} else {
|
|
298
|
+
agentEmitter.emit(EVENTS.BODY_PARSED, req, res, INPUT_TYPES.BODY);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
origNext();
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
230
305
|
onServerListen(args, server) {
|
|
231
306
|
if (!this.serversSeen.has(server)) {
|
|
232
307
|
logger.debug('ignore server.listen: non-express handler');
|
|
@@ -316,35 +391,6 @@ class ExpressFramework {
|
|
|
316
391
|
next();
|
|
317
392
|
}, 'multerMiddleware');
|
|
318
393
|
|
|
319
|
-
// ... body-parser ...............................................
|
|
320
|
-
this.useAfter(function ContrastRawBodyParsed(req, res, next) {
|
|
321
|
-
agentEmitter.emit(EVENTS.BODY_PARSED, req, res, INPUT_TYPES.BODY);
|
|
322
|
-
next();
|
|
323
|
-
}, 'rawParser');
|
|
324
|
-
|
|
325
|
-
// ... bodyParser in Sails Framework ............................
|
|
326
|
-
this.useAfter(function ContrastBodyParsed(req, res, next) {
|
|
327
|
-
if (req._sails && req.body) {
|
|
328
|
-
agentEmitter.emit(EVENTS.BODY_PARSED, req, res, INPUT_TYPES.BODY);
|
|
329
|
-
}
|
|
330
|
-
next();
|
|
331
|
-
}, '_parseHTTPBody');
|
|
332
|
-
|
|
333
|
-
this.useAfter(function ContrastTextBodyParsed(req, res, next) {
|
|
334
|
-
agentEmitter.emit(EVENTS.BODY_PARSED, req, res, INPUT_TYPES.BODY);
|
|
335
|
-
next();
|
|
336
|
-
}, 'textParser');
|
|
337
|
-
|
|
338
|
-
this.useAfter(function ContrastBodyParsed(req, res, next) {
|
|
339
|
-
agentEmitter.emit(EVENTS.BODY_PARSED, req, res, INPUT_TYPES.BODY);
|
|
340
|
-
next();
|
|
341
|
-
}, 'urlencodedParser');
|
|
342
|
-
|
|
343
|
-
this.useAfter(function ContrastJSONParsed(req, res, next) {
|
|
344
|
-
agentEmitter.emit(EVENTS.BODY_PARSED, req, res, INPUT_TYPES.JSON_VALUE);
|
|
345
|
-
next();
|
|
346
|
-
}, 'jsonParser');
|
|
347
|
-
|
|
348
394
|
// ... cookie-parser .............................................
|
|
349
395
|
this.useAfter(function ContrastCookiesParsed(req, res, next) {
|
|
350
396
|
agentEmitter.emit(
|
|
@@ -16,13 +16,11 @@ Copyright: 2022 Contrast Security, Inc
|
|
|
16
16
|
|
|
17
17
|
const _ = require('lodash');
|
|
18
18
|
const semver = require('semver');
|
|
19
|
-
const constants = require('../../constants');
|
|
20
|
-
const BaseSensor = require('../../hooks/frameworks/base');
|
|
21
19
|
const patcher = require('../../hooks/patcher');
|
|
22
|
-
const
|
|
20
|
+
const BaseSensor = require('../../hooks/frameworks/base');
|
|
21
|
+
const { PATCH_TYPES, SINK_TYPES } = require('../../constants');
|
|
23
22
|
const { emitSinkEvent } = require('../../hooks/frameworks/common');
|
|
24
23
|
|
|
25
|
-
const { SINK_TYPES } = constants;
|
|
26
24
|
const ID = 'mongodb';
|
|
27
25
|
|
|
28
26
|
function getCursorQueryData(args, version) {
|
|
@@ -61,84 +59,98 @@ class MongoDBSensor extends BaseSensor {
|
|
|
61
59
|
}
|
|
62
60
|
|
|
63
61
|
install({ ModuleHook }) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
62
|
+
const v4MethodsWithFilter = [
|
|
63
|
+
'updateOne',
|
|
64
|
+
'replaceOne',
|
|
65
|
+
'updateMany',
|
|
66
|
+
'deleteOne',
|
|
67
|
+
'deleteMany',
|
|
68
|
+
'findOneAndDelete',
|
|
69
|
+
'findOneAndReplace',
|
|
70
|
+
'findOneAndUpdate',
|
|
71
|
+
'countDocuments',
|
|
72
|
+
'count',
|
|
73
|
+
'distinct',
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
ModuleHook.resolve({ name: ID, version: '<4.0.0' }, (mongodb, { version }) => {
|
|
77
|
+
['command', 'cursor'].forEach(method => {
|
|
78
|
+
patcher.patch(mongodb.CoreServer.prototype, method, {
|
|
79
|
+
alwaysRun: true,
|
|
80
|
+
name: `mongodb.CoreServer.prototype.${method}`,
|
|
81
|
+
patchType: PATCH_TYPES.PROTECT_SINK,
|
|
82
|
+
pre: (wrapCtx) => {
|
|
83
|
+
emitSinkEvent(
|
|
84
|
+
getCursorQueryData(wrapCtx.args, version),
|
|
85
|
+
SINK_TYPES.NOSQL_QUERY,
|
|
86
|
+
ID
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
91
90
|
});
|
|
92
91
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
emitSinkEvent(eData, SINK_TYPES.NOSQL_QUERY, ID);
|
|
92
|
+
['remove', 'update'].forEach(method => {
|
|
93
|
+
patcher.patch(mongodb.CoreServer.prototype, method, {
|
|
94
|
+
alwaysRun: true,
|
|
95
|
+
name: 'mongodb.CoreServer.prototype.remove',
|
|
96
|
+
patchType: PATCH_TYPES.PROTECT_SINK,
|
|
97
|
+
pre: (wrapCtx) => {
|
|
98
|
+
const ops = Array.isArray(wrapCtx.args[1]) ? wrapCtx.args[1] : [wrapCtx.args[1]];
|
|
99
|
+
|
|
100
|
+
for (const op of ops) {
|
|
101
|
+
const eData = getOpQueryData(op);
|
|
102
|
+
if (eData) {
|
|
103
|
+
emitSinkEvent(eData, SINK_TYPES.NOSQL_QUERY, ID);
|
|
104
|
+
}
|
|
107
105
|
}
|
|
108
106
|
}
|
|
109
|
-
}
|
|
107
|
+
});
|
|
110
108
|
});
|
|
111
109
|
|
|
112
|
-
|
|
113
|
-
patcher.patch(mongodb.CoreServer.prototype, 'update', {
|
|
110
|
+
patcher.patch(mongodb.Db.prototype, 'eval', {
|
|
114
111
|
alwaysRun: true,
|
|
115
|
-
name: 'mongodb.
|
|
112
|
+
name: 'mongodb.Db.prototype.eval',
|
|
116
113
|
patchType: PATCH_TYPES.PROTECT_SINK,
|
|
117
|
-
pre: (
|
|
118
|
-
|
|
119
|
-
? data.args[1]
|
|
120
|
-
: [data.args[1]];
|
|
121
|
-
|
|
122
|
-
for (const op of ops) {
|
|
123
|
-
const eData = getOpQueryData(op);
|
|
124
|
-
if (eData) {
|
|
125
|
-
emitSinkEvent(eData, SINK_TYPES.NOSQL_QUERY, ID);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
114
|
+
pre: (wrapCtx) => {
|
|
115
|
+
emitSinkEvent(wrapCtx.args[0], SINK_TYPES.NOSQL_QUERY, ID);
|
|
128
116
|
}
|
|
129
117
|
});
|
|
118
|
+
});
|
|
130
119
|
|
|
131
|
-
|
|
132
|
-
|
|
120
|
+
ModuleHook.resolve({ name: ID, version: '>=4.0.0' }, (mongodb) => {
|
|
121
|
+
v4MethodsWithFilter.forEach((method) => {
|
|
122
|
+
patcher.patch(mongodb.Collection.prototype, method, {
|
|
123
|
+
alwaysRun: true,
|
|
124
|
+
name: `mongodb.Collection.prototype.${method}`,
|
|
125
|
+
patchType: PATCH_TYPES.PROTECT_SINK,
|
|
126
|
+
pre: (wrapCtx) => {
|
|
127
|
+
const value = typeof wrapCtx.args[0] == 'function' ? null : wrapCtx.args[0];
|
|
128
|
+
emitSinkEvent(value, SINK_TYPES.NOSQL_QUERY, ID);
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
patcher.patch(mongodb.Db.prototype, 'command', {
|
|
133
134
|
alwaysRun: true,
|
|
134
|
-
name: 'mongodb.Db.prototype',
|
|
135
|
+
name: 'mongodb.Db.prototype.command',
|
|
135
136
|
patchType: PATCH_TYPES.PROTECT_SINK,
|
|
136
|
-
pre: (
|
|
137
|
-
|
|
137
|
+
pre: (wrapCtx) => {
|
|
138
|
+
const value = wrapCtx.args[0] && wrapCtx.args[0].filter;
|
|
139
|
+
emitSinkEvent(value, SINK_TYPES.NOSQL_QUERY, ID);
|
|
138
140
|
}
|
|
139
141
|
});
|
|
140
142
|
});
|
|
141
143
|
|
|
144
|
+
ModuleHook.resolve({ name: ID, file: 'lib/cursor/find_cursor', version: '>=4.0.0' }, (cursor) => patcher.patch(cursor, 'FindCursor', {
|
|
145
|
+
alwaysRun: true,
|
|
146
|
+
name: 'mongodb.FindCursor',
|
|
147
|
+
patchType: PATCH_TYPES.PROTECT_SINK,
|
|
148
|
+
pre: (wrapCtx) => {
|
|
149
|
+
const value = wrapCtx.args[2];
|
|
150
|
+
emitSinkEvent(value, SINK_TYPES.NOSQL_QUERY, ID);
|
|
151
|
+
}
|
|
152
|
+
}));
|
|
153
|
+
|
|
142
154
|
return this;
|
|
143
155
|
}
|
|
144
156
|
}
|
|
@@ -29,15 +29,19 @@ module.exports = function Finding(details = {}) {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
const routes = RoutesListWithObservations(details.routes);
|
|
32
|
+
const properties = objToMap(details.properties).map(
|
|
33
|
+
// ensure string values
|
|
34
|
+
([key, value]) => [key, String(value)]
|
|
35
|
+
);
|
|
32
36
|
|
|
33
37
|
return new dtm.Finding({
|
|
34
|
-
0: String(details.hash || ''), //
|
|
35
|
-
2: details.ruleId, //
|
|
36
|
-
5:
|
|
37
|
-
6: mapToModelArray(TraceEvent, details.events), //
|
|
38
|
-
7: preflight, //
|
|
39
|
-
8: details.tags, //
|
|
40
|
-
9: details.version, //
|
|
38
|
+
0: String(details.hash || ''), // 1 hash_code
|
|
39
|
+
2: details.ruleId, // 3 rule_id
|
|
40
|
+
5: properties, // 6 properties
|
|
41
|
+
6: mapToModelArray(TraceEvent, details.events), // 7 events
|
|
42
|
+
7: preflight, // 8 preflight
|
|
43
|
+
8: details.tags, // 9 tags
|
|
44
|
+
9: details.version, // 10 version
|
|
41
45
|
10: routes // 11 routes
|
|
42
46
|
});
|
|
43
47
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/agent",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.23.1",
|
|
4
4
|
"description": "Node.js security instrumentation by Contrast Security",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"security",
|
|
@@ -163,7 +163,7 @@
|
|
|
163
163
|
"mock-fs": "^5.1.2",
|
|
164
164
|
"mongodb": "file:test/mock/mongodb",
|
|
165
165
|
"mongodb-npm": "npm:mongodb@^3.6.5",
|
|
166
|
-
"mongoose": "^6.
|
|
166
|
+
"mongoose": "^6.4.6",
|
|
167
167
|
"mustache": "^3.0.1",
|
|
168
168
|
"mysql": "file:test/mock/mysql",
|
|
169
169
|
"mysql2": "file:test/mock/mysql2",
|