@contrast/agent 4.13.1 → 4.15.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/bin/VERSION +1 -1
- package/bin/linux/contrast-service +0 -0
- package/bin/mac/contrast-service +0 -0
- package/bin/windows/contrast-service.exe +0 -0
- package/lib/assess/policy/propagators.json +13 -4
- package/lib/assess/policy/rules.json +42 -0
- package/lib/assess/policy/signatures.json +18 -0
- package/lib/assess/propagators/joi/any.js +1 -1
- package/lib/assess/propagators/joi/object.js +1 -1
- package/lib/assess/propagators/joi/string-base.js +16 -3
- package/lib/assess/propagators/mongoose/map.js +1 -1
- package/lib/assess/propagators/mongoose/mixed.js +1 -1
- package/lib/assess/propagators/mongoose/string.js +1 -1
- package/lib/assess/propagators/validator/init-hooks.js +2 -1
- package/lib/assess/propagators/validator/validator-methods.js +2 -1
- package/lib/core/async-storage/hooks/mysql.js +57 -6
- package/lib/core/config/options.js +6 -6
- package/lib/libraries.js +1 -1
- package/lib/protect/service.js +13 -21
- package/lib/reporter/models/app-update/library.js +60 -6
- package/package.json +3 -3
package/bin/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.28.
|
|
1
|
+
2.28.19
|
|
Binary file
|
package/bin/mac/contrast-service
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"source": "P",
|
|
14
14
|
"target": "R",
|
|
15
15
|
"command": {
|
|
16
|
-
|
|
16
|
+
"type": "all"
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"url.domainToUnicode": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"source": "P",
|
|
22
22
|
"target": "R",
|
|
23
23
|
"command": {
|
|
24
|
-
|
|
24
|
+
"type": "all"
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
27
|
"joi": {
|
|
@@ -65,6 +65,15 @@
|
|
|
65
65
|
"type": "all"
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
|
+
"mysql2/lib/connection.Connection.escape": {
|
|
69
|
+
"enabled": true,
|
|
70
|
+
"source": "P",
|
|
71
|
+
"target": "R",
|
|
72
|
+
"tags": ["sql-encoded"],
|
|
73
|
+
"command": {
|
|
74
|
+
"type": "all"
|
|
75
|
+
}
|
|
76
|
+
},
|
|
68
77
|
"mustache.escape": {
|
|
69
78
|
"enabled": true,
|
|
70
79
|
"provider": "./propagators/mustache/escape.js"
|
|
@@ -347,8 +356,8 @@
|
|
|
347
356
|
},
|
|
348
357
|
"process.__add": {
|
|
349
358
|
"enabled": true,
|
|
350
|
-
"source":"P",
|
|
351
|
-
"target":"R",
|
|
359
|
+
"source": "P",
|
|
360
|
+
"target": "R",
|
|
352
361
|
"command": {
|
|
353
362
|
"type": "append"
|
|
354
363
|
}
|
|
@@ -388,6 +388,48 @@
|
|
|
388
388
|
]
|
|
389
389
|
}
|
|
390
390
|
},
|
|
391
|
+
"mysql2/lib/connection.Connection.query": {
|
|
392
|
+
"type": "dataflow",
|
|
393
|
+
"enabled": true,
|
|
394
|
+
"conditions": {
|
|
395
|
+
"mode": "or",
|
|
396
|
+
"args": [
|
|
397
|
+
{
|
|
398
|
+
"index": 0,
|
|
399
|
+
"requiredTags": ["untrusted"],
|
|
400
|
+
"disallowedTags": ["sql-encoded", "limited-chars"]
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
"index": 0,
|
|
404
|
+
"depth": 1,
|
|
405
|
+
"exclusiveKeys": ["sql"],
|
|
406
|
+
"requiredTags": ["untrusted"],
|
|
407
|
+
"disallowedTags": ["sql-encoded", "limited-chars"]
|
|
408
|
+
}
|
|
409
|
+
]
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
"mysql2/lib/connection.Connection.execute": {
|
|
413
|
+
"type": "dataflow",
|
|
414
|
+
"enabled": true,
|
|
415
|
+
"conditions": {
|
|
416
|
+
"mode": "or",
|
|
417
|
+
"args": [
|
|
418
|
+
{
|
|
419
|
+
"index": 0,
|
|
420
|
+
"requiredTags": ["untrusted"],
|
|
421
|
+
"disallowedTags": ["sql-encoded", "limited-chars"]
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
"index": 0,
|
|
425
|
+
"depth": 1,
|
|
426
|
+
"exclusiveKeys": ["sql"],
|
|
427
|
+
"requiredTags": ["untrusted"],
|
|
428
|
+
"disallowedTags": ["sql-encoded", "limited-chars"]
|
|
429
|
+
}
|
|
430
|
+
]
|
|
431
|
+
}
|
|
432
|
+
},
|
|
391
433
|
"pg.Connection.prototype.query": {
|
|
392
434
|
"type": "dataflow",
|
|
393
435
|
"enabled": true,
|
|
@@ -171,6 +171,24 @@
|
|
|
171
171
|
"methodName": "prototype.query",
|
|
172
172
|
"isModule": true
|
|
173
173
|
},
|
|
174
|
+
"mysql2/lib/connection.Connection.escape": {
|
|
175
|
+
"moduleName": "mysql2",
|
|
176
|
+
"fileName": "lib/connection.js",
|
|
177
|
+
"methodName": "prototype.escape",
|
|
178
|
+
"isModule": true
|
|
179
|
+
},
|
|
180
|
+
"mysql2/lib/connection.Connection.query": {
|
|
181
|
+
"moduleName": "mysql2",
|
|
182
|
+
"fileName": "lib/connection.js",
|
|
183
|
+
"methodName": "prototype.query",
|
|
184
|
+
"isModule": true
|
|
185
|
+
},
|
|
186
|
+
"mysql2/lib/connection.Connection.execute": {
|
|
187
|
+
"moduleName": "mysql2",
|
|
188
|
+
"fileName": "lib/connection.js",
|
|
189
|
+
"methodName": "prototype.execute",
|
|
190
|
+
"isModule": true
|
|
191
|
+
},
|
|
174
192
|
"sequelize.prototype.query": {
|
|
175
193
|
"moduleName": "sequelize",
|
|
176
194
|
"version": ">=5.0.0",
|
|
@@ -32,7 +32,7 @@ requireHook.resolve(
|
|
|
32
32
|
patchType: ASSESS_PROPAGATOR,
|
|
33
33
|
alwaysRun: true,
|
|
34
34
|
post(data) {
|
|
35
|
-
if (!agent.config.
|
|
35
|
+
if (!agent.config.assess.trust_custom_validators) return;
|
|
36
36
|
|
|
37
37
|
if (data.result && typeof data.result === 'string') {
|
|
38
38
|
tracker.untrack(data.result);
|
|
@@ -49,7 +49,7 @@ requireHook.resolve(
|
|
|
49
49
|
patchType: ASSESS_PROPAGATOR,
|
|
50
50
|
alwaysRun: true,
|
|
51
51
|
post(data) {
|
|
52
|
-
if (!agent.config.
|
|
52
|
+
if (!agent.config.assess.trust_custom_validators) return;
|
|
53
53
|
|
|
54
54
|
data.result.then((result) =>
|
|
55
55
|
traverseObjectAndUntrack(data.obj, data.args[0], result),
|
|
@@ -26,6 +26,12 @@ const TagRange = require('../../models/tag-range');
|
|
|
26
26
|
const tagRangeUtil = require('../../models/tag-range/util');
|
|
27
27
|
const agent = require('../../../agent');
|
|
28
28
|
|
|
29
|
+
const areThereRules = (obj) =>
|
|
30
|
+
obj &&
|
|
31
|
+
obj.schema &&
|
|
32
|
+
Array.isArray(obj.schema._rules) &&
|
|
33
|
+
obj.schema._rules.length > 0;
|
|
34
|
+
|
|
29
35
|
/**
|
|
30
36
|
* Patch joi.string.validate so that it tags input with string-type-checked if validated
|
|
31
37
|
* @param {Object} string
|
|
@@ -38,11 +44,18 @@ function instrumentJoiString(string) {
|
|
|
38
44
|
patchType: ASSESS_PROPAGATOR,
|
|
39
45
|
post(data) {
|
|
40
46
|
const trackingData = tracker.getData(data.args[0]);
|
|
47
|
+
if (
|
|
48
|
+
areThereRules(data.args[1]) &&
|
|
49
|
+
data.args[1].schema._rules.find((rule) => rule.name === 'pattern') &&
|
|
50
|
+
!agent.config.assess.trust_custom_validators
|
|
51
|
+
)
|
|
52
|
+
return;
|
|
53
|
+
|
|
41
54
|
if (data.result === undefined && trackingData) {
|
|
42
55
|
const { event } = trackingData;
|
|
43
56
|
trackingData.tagRanges = tagRangeUtil.add(
|
|
44
57
|
trackingData.tagRanges,
|
|
45
|
-
new TagRange(0, data.args[0].length - 1, 'string-type-checked')
|
|
58
|
+
new TagRange(0, data.args[0].length - 1, 'string-type-checked')
|
|
46
59
|
);
|
|
47
60
|
trackingData.event = new PropagationEvent({
|
|
48
61
|
context: new CallContext(data),
|
|
@@ -63,7 +76,7 @@ function instrumentJoiString(string) {
|
|
|
63
76
|
post(data) {
|
|
64
77
|
if (
|
|
65
78
|
!data.obj.$_terms.externals.length ||
|
|
66
|
-
!agent.config.
|
|
79
|
+
!agent.config.assess.trust_custom_validators
|
|
67
80
|
)
|
|
68
81
|
return;
|
|
69
82
|
|
|
@@ -77,5 +90,5 @@ function instrumentJoiString(string) {
|
|
|
77
90
|
|
|
78
91
|
requireHook.resolve(
|
|
79
92
|
{ name: 'joi', file: 'lib/types/string.js', version: '>=17.0.0' },
|
|
80
|
-
instrumentJoiString
|
|
93
|
+
instrumentJoiString
|
|
81
94
|
);
|
|
@@ -22,6 +22,7 @@ const moduleHook = require('../../../hooks/require');
|
|
|
22
22
|
const TagRange = require('../../models/tag-range');
|
|
23
23
|
const tagRangeUtil = require('../../models/tag-range/util');
|
|
24
24
|
const { PropagationEvent, Signature, CallContext } = require('../../models');
|
|
25
|
+
const agent = require('../../../agent');
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* this override propagator instruments each of the classes of methods
|
|
@@ -88,7 +89,7 @@ module.exports.handle = function() {
|
|
|
88
89
|
{ name: 'validator', file: `lib/${validator}` },
|
|
89
90
|
(index, meta) => {
|
|
90
91
|
function post(data) {
|
|
91
|
-
if (data.result) {
|
|
92
|
+
if (data.result && (validator !== 'matches' || (validator === 'matches' && agent.config.assess.trust_custom_validators))) {
|
|
92
93
|
const trackingData = tracker.getData(data.args[0]);
|
|
93
94
|
if (trackingData) {
|
|
94
95
|
tagRangeUtil.addInPlace(
|
|
@@ -72,7 +72,8 @@ module.exports = {
|
|
|
72
72
|
isSemVer: 'limited-chars',
|
|
73
73
|
isTaxID: 'limited-chars',
|
|
74
74
|
isUUID: 'alphanum-space-hyphen',
|
|
75
|
-
isVAT: 'alphanum-space-hyphen'
|
|
75
|
+
isVAT: 'alphanum-space-hyphen',
|
|
76
|
+
matches: 'string-type-checked'
|
|
76
77
|
},
|
|
77
78
|
untrackers: [
|
|
78
79
|
// these methods have the tag 'trusted' which the node-agent doesn't support.
|
|
@@ -16,13 +16,15 @@ Copyright: 2022 Contrast Security, Inc
|
|
|
16
16
|
|
|
17
17
|
const logger = require('../../logger')('contrast:async-storage:hooks');
|
|
18
18
|
const { AsyncStorage } = require('../index');
|
|
19
|
+
const { Scopes } = require('../scopes');
|
|
19
20
|
const { ASYNC_CONTEXT } = require('../../../constants').PATCH_TYPES;
|
|
20
21
|
const requireHook = require('../../../hooks/require');
|
|
21
22
|
const patcher = require('../../../hooks/patcher');
|
|
23
|
+
const { bindFnArgAtIndex } = require('./utils');
|
|
22
24
|
|
|
23
25
|
module.exports = function init() {
|
|
24
26
|
// done only to stub these fns for tests
|
|
25
|
-
const { patchSequence, patchPool } = module.exports;
|
|
27
|
+
const { patchSequence, patchPool, patchQuery } = module.exports;
|
|
26
28
|
|
|
27
29
|
// this callback _must return_ the patched function to set export
|
|
28
30
|
requireHook.resolve(
|
|
@@ -36,12 +38,20 @@ module.exports = function init() {
|
|
|
36
38
|
requireHook.resolve({ name: 'mysql', file: 'lib/Pool.js' }, (Pool) =>
|
|
37
39
|
patchPool(Pool)
|
|
38
40
|
);
|
|
41
|
+
|
|
42
|
+
requireHook.resolve(
|
|
43
|
+
{ name: 'mysql2', file: 'lib/commands/query.js' },
|
|
44
|
+
(Query) => patchQuery(Query)
|
|
45
|
+
);
|
|
39
46
|
};
|
|
40
47
|
|
|
41
48
|
module.exports.patchSequence = patchSequence;
|
|
42
49
|
module.exports.sequencePostHook = sequencePostHook;
|
|
43
50
|
module.exports.patchPool = patchPool;
|
|
44
51
|
module.exports.poolPreHook = poolPreHook;
|
|
52
|
+
module.exports.patchQuery = patchQuery;
|
|
53
|
+
module.exports.queryPreHook = queryPreHook;
|
|
54
|
+
module.exports.runInAllowAllScope = runInAllowAllScope;
|
|
45
55
|
|
|
46
56
|
/**
|
|
47
57
|
* Patches the Sequence constructor which the protocol classes inherit.
|
|
@@ -62,12 +72,15 @@ function patchSequence(sequenceCtor) {
|
|
|
62
72
|
* Typically in a constructor the data.result would be the instance. But mysql
|
|
63
73
|
* has the subclasses e.g. Query, do Sequence.call(this, cb). In this case the
|
|
64
74
|
* data.obj is the instance.
|
|
65
|
-
* @param {object} data.obj
|
|
75
|
+
* @param {object} data.obj sequence instance
|
|
66
76
|
*/
|
|
67
|
-
function sequencePostHook({ obj }) {
|
|
77
|
+
function sequencePostHook({ obj, funcKey: identifier }) {
|
|
78
|
+
// done only to stub this fn for tests
|
|
79
|
+
const { runInAllowAllScope } = module.exports;
|
|
68
80
|
try {
|
|
69
81
|
if (obj && obj._callback && typeof obj._callback === 'function') {
|
|
70
|
-
|
|
82
|
+
const cb = obj._callback;
|
|
83
|
+
obj._callback = AsyncStorage.bind(runInAllowAllScope(cb, identifier));
|
|
71
84
|
}
|
|
72
85
|
AsyncStorage.getNamespace().bindEmitter(obj);
|
|
73
86
|
} catch (err) {
|
|
@@ -75,6 +88,13 @@ function sequencePostHook({ obj }) {
|
|
|
75
88
|
}
|
|
76
89
|
}
|
|
77
90
|
|
|
91
|
+
// Created only for the purpose of testing
|
|
92
|
+
function runInAllowAllScope (cb, identifier) {
|
|
93
|
+
return function (...args) {
|
|
94
|
+
return Scopes.runInAllowAllScope(() => cb.call(this, ...args), identifier);
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
78
98
|
/**
|
|
79
99
|
* Patches Pool.prototype.getConnection.
|
|
80
100
|
* @param {function} poolCtor Pool constructor fn
|
|
@@ -92,12 +112,43 @@ function patchPool(poolCtor) {
|
|
|
92
112
|
* Binds callback (when present) to cls.
|
|
93
113
|
* @param {object} data.args getConnection arguments
|
|
94
114
|
*/
|
|
95
|
-
function poolPreHook({ args }) {
|
|
115
|
+
function poolPreHook({ args, funcKey: identifier }) {
|
|
96
116
|
try {
|
|
97
117
|
if (args.length && typeof args[0] === 'function') {
|
|
98
|
-
args
|
|
118
|
+
bindFnArgAtIndex({ args, idx: 0, identifier });
|
|
99
119
|
}
|
|
100
120
|
} catch (err) {
|
|
101
121
|
logger.warn('Unable to patch Pool.prototype.getConnection: %o', err);
|
|
102
122
|
}
|
|
103
123
|
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Patches the Query constructor.
|
|
127
|
+
* This _must return_ the patched value to set the export in require hook.
|
|
128
|
+
* @param {function} queryCtor Query constructor fn
|
|
129
|
+
* @returns {function}
|
|
130
|
+
*/
|
|
131
|
+
function patchQuery(queryCtor) {
|
|
132
|
+
return patcher.patch(queryCtor, {
|
|
133
|
+
name: 'mysql2.lib/commands/query.js.Query',
|
|
134
|
+
patchType: ASYNC_CONTEXT,
|
|
135
|
+
alwaysRun: true,
|
|
136
|
+
pre: queryPreHook
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Binds callback (when present) to the context the constructor is called in..
|
|
142
|
+
* @param {object} data the argument for the preHook
|
|
143
|
+
* @param {object} data.args the arguments passed to the Query constructor
|
|
144
|
+
* @param {object} data.funcKey Contrast funcKey identifier for a hooked Query function
|
|
145
|
+
*/
|
|
146
|
+
function queryPreHook({ args, funcKey: identifier }) {
|
|
147
|
+
try {
|
|
148
|
+
if (args.length && args[1] && typeof args[1] === 'function') {
|
|
149
|
+
bindFnArgAtIndex({ args, idx: 1, identifier });
|
|
150
|
+
}
|
|
151
|
+
} catch (err) {
|
|
152
|
+
logger.warn('Unable to patch Query constructor: %o', err);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -472,12 +472,6 @@ const agent = [
|
|
|
472
472
|
fn: parseNum,
|
|
473
473
|
desc: 'set limit for stack trace size (larger limits will improve accuracy but increase memory usage)',
|
|
474
474
|
},
|
|
475
|
-
{
|
|
476
|
-
name: 'agent.trust_custom_validators',
|
|
477
|
-
arg: '<trust-custom-validators>',
|
|
478
|
-
default: false,
|
|
479
|
-
desc: `trust incoming strings when they pass custom validators (Mongoose, Joi)`,
|
|
480
|
-
},
|
|
481
475
|
{
|
|
482
476
|
name: 'agent.traverse_and_track',
|
|
483
477
|
arg: '<traverse-and-track>',
|
|
@@ -714,6 +708,12 @@ const assess = [
|
|
|
714
708
|
fn: castBoolean,
|
|
715
709
|
desc: 'if false, disable assess for this agent. A restart is required to re-enable',
|
|
716
710
|
},
|
|
711
|
+
{
|
|
712
|
+
name: 'assess.trust_custom_validators',
|
|
713
|
+
arg: '<trust-custom-validators>',
|
|
714
|
+
default: false,
|
|
715
|
+
desc: 'trust incoming strings when they pass custom validators (Mongoose, Joi)',
|
|
716
|
+
},
|
|
717
717
|
{
|
|
718
718
|
name: 'assess.enable_preflight',
|
|
719
719
|
arg: '[false]',
|
package/lib/libraries.js
CHANGED
|
@@ -124,7 +124,7 @@ const getLibInfo = async (agent, eluEnabled) =>
|
|
|
124
124
|
|
|
125
125
|
if (!nodeModsPath) {
|
|
126
126
|
logger.error(
|
|
127
|
-
|
|
127
|
+
'unable to read installed dependencies because a node_modules directory could not be detected given a package.json located at %s - use the agent.node.app_root configuration variable if installed in non-standard location',
|
|
128
128
|
agent.appInfo.path
|
|
129
129
|
);
|
|
130
130
|
return AppUpdate.libraries;
|
package/lib/protect/service.js
CHANGED
|
@@ -37,6 +37,8 @@ const UserInputKit = require('../reporter/models/utils/user-input-kit');
|
|
|
37
37
|
const UserInputFactory = require('../reporter/models/utils/user-input-factory');
|
|
38
38
|
const blockRequest = require('../util/block-request');
|
|
39
39
|
|
|
40
|
+
const evalOptions = { preferWorthWatching: true };
|
|
41
|
+
|
|
40
42
|
class ProtectService {
|
|
41
43
|
/**
|
|
42
44
|
* Configures the service to use the provided agent.
|
|
@@ -196,8 +198,6 @@ class ProtectService {
|
|
|
196
198
|
}
|
|
197
199
|
|
|
198
200
|
const arg = {
|
|
199
|
-
rules,
|
|
200
|
-
preferWorthWatching: true,
|
|
201
201
|
// header names must be lowercase. should this be done in agent-lib?
|
|
202
202
|
headers: req.rawHeaders.map((h, ix) => (ix & 1 ? h : h.toLowerCase()))
|
|
203
203
|
};
|
|
@@ -207,7 +207,7 @@ class ProtectService {
|
|
|
207
207
|
arg.queries = req.url.slice(questionMark + 1);
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
const findings = this.agentLib.scoreRequestConnect(arg);
|
|
210
|
+
const findings = this.agentLib.scoreRequestConnect(rules, arg, evalOptions);
|
|
211
211
|
|
|
212
212
|
return findings;
|
|
213
213
|
}
|
|
@@ -218,14 +218,12 @@ class ProtectService {
|
|
|
218
218
|
return {};
|
|
219
219
|
}
|
|
220
220
|
// also, if content-type has multipart...
|
|
221
|
-
const options = { preferWorthWatching: true };
|
|
222
|
-
|
|
223
221
|
const bodyBuffer = Buffer.concat(chunks);
|
|
224
222
|
|
|
225
223
|
const findings = this.agentLib.scoreRequestUnknownBody(
|
|
226
224
|
rules,
|
|
227
225
|
bodyBuffer,
|
|
228
|
-
|
|
226
|
+
evalOptions
|
|
229
227
|
);
|
|
230
228
|
|
|
231
229
|
// store body buffer on findings for nosqli sink.
|
|
@@ -814,9 +812,9 @@ class ProtectService {
|
|
|
814
812
|
// for each key, check out the value. the key is set in the code so
|
|
815
813
|
// is not vulnerable.
|
|
816
814
|
for (const key in params) {
|
|
817
|
-
// items from scoreAtom()
|
|
815
|
+
// items from scoreAtom() return only [{ruleId, score}, ...] because the key
|
|
818
816
|
// and inputType are already known and there is no path.
|
|
819
|
-
const items = this.agentLib.scoreAtom(params[key], type
|
|
817
|
+
const items = this.agentLib.scoreAtom(libRules, params[key], type);
|
|
820
818
|
if (!items) {
|
|
821
819
|
continue;
|
|
822
820
|
}
|
|
@@ -866,7 +864,7 @@ class ProtectService {
|
|
|
866
864
|
const filenames = Object.keys(event.data);
|
|
867
865
|
|
|
868
866
|
for (const filename of filenames) {
|
|
869
|
-
const items = this.agentLib.scoreAtom(filename, type
|
|
867
|
+
const items = this.agentLib.scoreAtom(libRules, filename, type);
|
|
870
868
|
if (!items) {
|
|
871
869
|
continue;
|
|
872
870
|
}
|
|
@@ -900,13 +898,9 @@ class ProtectService {
|
|
|
900
898
|
queries.unshift(...q); return queries;
|
|
901
899
|
}, []);
|
|
902
900
|
|
|
903
|
-
const arg = {
|
|
904
|
-
rules: rulesMask,
|
|
905
|
-
preferWorthWatching: true,
|
|
906
|
-
queries,
|
|
907
|
-
};
|
|
901
|
+
const arg = { queries };
|
|
908
902
|
|
|
909
|
-
const findings = this.agentLib.scoreRequestConnect(arg);
|
|
903
|
+
const findings = this.agentLib.scoreRequestConnect(rulesMask, arg, evalOptions);
|
|
910
904
|
|
|
911
905
|
this.handleAgentLibAnalysis({
|
|
912
906
|
asyncStorageContext: event._ctxt,
|
|
@@ -922,11 +916,9 @@ class ProtectService {
|
|
|
922
916
|
acc.unshift(key, value);
|
|
923
917
|
return acc;
|
|
924
918
|
}, []);
|
|
925
|
-
const
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
cookies
|
|
929
|
-
});
|
|
919
|
+
const rulesMask = this.getRulesMask(rules);
|
|
920
|
+
const arg = { cookies };
|
|
921
|
+
const findings = this.agentLib.scoreRequestConnect(rulesMask, arg, evalOptions);
|
|
930
922
|
this.handleAgentLibAnalysis({
|
|
931
923
|
asyncStorageContext: event._ctxt,
|
|
932
924
|
appContext: {},
|
|
@@ -1172,7 +1164,7 @@ class ProtectService {
|
|
|
1172
1164
|
const { _type, _value: input } = finding.sample.input;
|
|
1173
1165
|
const type = this.agentLib.InputType[_type];
|
|
1174
1166
|
|
|
1175
|
-
const alFinding = this.agentLib.scoreAtom(input, type
|
|
1167
|
+
const alFinding = this.agentLib.scoreAtom(agentLibBit, input, type);
|
|
1176
1168
|
if (!alFinding) {
|
|
1177
1169
|
return false;
|
|
1178
1170
|
}
|
|
@@ -13,9 +13,10 @@ Copyright: 2022 Contrast Security, Inc
|
|
|
13
13
|
way not consistent with the End User License Agreement.
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
|
-
const readdir = require('recursive-readdir');
|
|
17
16
|
const LibraryManifest = require('./library-manifest');
|
|
18
17
|
const logger = require('../../../core/logger')('contrast:libraries');
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const pathModule = require('path');
|
|
19
20
|
|
|
20
21
|
module.exports = class Library {
|
|
21
22
|
/**
|
|
@@ -53,7 +54,7 @@ module.exports = class Library {
|
|
|
53
54
|
manifest: this.manifest.toSerializable(),
|
|
54
55
|
usedClassCount: 0,
|
|
55
56
|
classCount: this.fileCount,
|
|
56
|
-
tags: this.tags
|
|
57
|
+
tags: this.tags,
|
|
57
58
|
};
|
|
58
59
|
}
|
|
59
60
|
|
|
@@ -88,15 +89,68 @@ module.exports = class Library {
|
|
|
88
89
|
);
|
|
89
90
|
}
|
|
90
91
|
|
|
92
|
+
readdir(path, callback) {
|
|
93
|
+
if (!callback) {
|
|
94
|
+
return new Promise((resolve, reject) => {
|
|
95
|
+
this.readdir(path, (err, data) => {
|
|
96
|
+
if (err) {
|
|
97
|
+
reject(err);
|
|
98
|
+
} else {
|
|
99
|
+
resolve(data);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let list = [];
|
|
106
|
+
|
|
107
|
+
fs.readdir(path, (err, files) => {
|
|
108
|
+
if (err) {
|
|
109
|
+
return callback(err);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let pending = files.length;
|
|
113
|
+
if (!pending) {
|
|
114
|
+
return callback(null, list);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
files.forEach((file) => {
|
|
118
|
+
const filePath = pathModule.join(path, file);
|
|
119
|
+
fs.stat(filePath, (_err, stats) => {
|
|
120
|
+
if (_err) {
|
|
121
|
+
return callback(_err);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (stats.isDirectory() && !filePath.endsWith('/node_modules')) {
|
|
125
|
+
this.readdir(filePath, (__err, res) => {
|
|
126
|
+
if (__err) {
|
|
127
|
+
return callback(__err);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
list = list.concat(res);
|
|
131
|
+
pending -= 1;
|
|
132
|
+
if (!pending) {
|
|
133
|
+
return callback(null, list);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
} else {
|
|
137
|
+
list.push(filePath);
|
|
138
|
+
pending -= 1;
|
|
139
|
+
if (!pending) {
|
|
140
|
+
return callback(null, list);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
91
148
|
/**
|
|
92
149
|
* Counts all the valid files in a module directory
|
|
93
150
|
*/
|
|
94
151
|
getComposition() {
|
|
95
152
|
// ignore nested node_modules
|
|
96
|
-
return readdir(this._path
|
|
97
|
-
`${this._path}/node_modules/*`,
|
|
98
|
-
`${this._path}/*/node_modules/*`
|
|
99
|
-
])
|
|
153
|
+
return this.readdir(this._path)
|
|
100
154
|
.then((files) => {
|
|
101
155
|
this.fileCount = files.filter((file) =>
|
|
102
156
|
Library.applicableFile(file)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/agent",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.15.1",
|
|
4
4
|
"description": "Node.js security instrumentation by Contrast Security",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"security",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"@babel/template": "^7.10.4",
|
|
77
77
|
"@babel/traverse": "^7.12.1",
|
|
78
78
|
"@babel/types": "^7.12.1",
|
|
79
|
-
"@contrast/agent-lib": "^
|
|
79
|
+
"@contrast/agent-lib": "^3.0.0",
|
|
80
80
|
"@contrast/distringuish-prebuilt": "^2.2.0",
|
|
81
81
|
"@contrast/flat": "^4.1.1",
|
|
82
82
|
"@contrast/fn-inspect": "^2.4.4",
|
|
@@ -107,7 +107,6 @@
|
|
|
107
107
|
"parent-package-json": "^2.0.1",
|
|
108
108
|
"parseurl": "^1.3.3",
|
|
109
109
|
"prom-client": "^12.0.0",
|
|
110
|
-
"recursive-readdir": "^2.2.2",
|
|
111
110
|
"semver": "^7.3.2",
|
|
112
111
|
"uuid": "^8.3.2",
|
|
113
112
|
"winston": "^3.1.0",
|
|
@@ -166,6 +165,7 @@
|
|
|
166
165
|
"mongoose": "^6.1.1",
|
|
167
166
|
"mustache": "^3.0.1",
|
|
168
167
|
"mysql": "file:test/mock/mysql",
|
|
168
|
+
"mysql2": "file:test/mock/mysql2",
|
|
169
169
|
"nock": "^12.0.3",
|
|
170
170
|
"node-fetch": "^2.6.7",
|
|
171
171
|
"node-serialize": "file:test/mock/node-serialize",
|