@contrast/agent 4.12.2 → 4.14.0
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/bootstrap.js +2 -3
- package/esm.mjs +9 -35
- package/lib/assess/membrane/debraner.js +0 -2
- package/lib/assess/membrane/index.js +1 -3
- package/lib/assess/models/tag-range/util.js +1 -2
- 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/policy/util.js +3 -2
- package/lib/assess/propagators/JSON/stringify.js +6 -11
- package/lib/assess/propagators/ajv/conditionals.js +0 -3
- package/lib/assess/propagators/ajv/json-schema-type-evaluators.js +5 -4
- package/lib/assess/propagators/ajv/refs.js +1 -2
- package/lib/assess/propagators/ajv/schema-context.js +2 -3
- 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/path/common.js +38 -29
- package/lib/assess/propagators/path/resolve.js +1 -0
- package/lib/assess/propagators/sequelize/utils.js +1 -2
- package/lib/assess/propagators/v8/init-hooks.js +0 -1
- package/lib/assess/sinks/dynamo.js +65 -30
- package/lib/assess/static/hardcoded.js +3 -3
- package/lib/assess/static/read-findings-from-cache.js +40 -0
- package/lib/assess/technologies/index.js +12 -13
- package/lib/cli-rewriter/index.js +65 -6
- package/lib/core/async-storage/hooks/mysql.js +57 -6
- package/lib/core/config/options.js +12 -6
- package/lib/core/config/util.js +15 -33
- package/lib/core/exclusions/input.js +6 -1
- package/lib/core/express/index.js +2 -4
- package/lib/core/logger/debug-logger.js +2 -2
- package/lib/core/stacktrace.js +2 -1
- package/lib/hooks/http.js +81 -81
- package/lib/hooks/require.js +1 -0
- package/lib/instrumentation.js +17 -0
- package/lib/protect/analysis/aho-corasick.js +1 -1
- package/lib/protect/errors/handler-async-errors.js +66 -0
- package/lib/protect/input-analysis.js +7 -13
- package/lib/protect/listeners.js +27 -23
- package/lib/protect/rules/base-scanner/index.js +2 -2
- package/lib/protect/rules/bot-blocker/bot-blocker-rule.js +4 -2
- package/lib/protect/rules/cmd-injection/cmdinjection-rule.js +57 -2
- package/lib/protect/rules/cmd-injection-semantic-chained-commands/cmd-injection-semantic-chained-commands-rule.js +31 -2
- package/lib/protect/rules/cmd-injection-semantic-dangerous-paths/cmd-injection-semantic-dangerous-paths-rule.js +32 -2
- package/lib/protect/rules/index.js +42 -21
- package/lib/protect/rules/ip-denylist/ip-denylist-rule.js +2 -2
- package/lib/protect/rules/nosqli/nosql-injection-rule.js +104 -39
- package/lib/protect/rules/path-traversal/path-traversal-rule.js +3 -0
- package/lib/protect/rules/rule-factory.js +6 -7
- package/lib/protect/rules/signatures/signature.js +3 -0
- package/lib/protect/rules/sqli/sql-injection-rule.js +98 -5
- package/lib/protect/rules/sqli/sql-scanner/labels.json +0 -3
- package/lib/protect/rules/xss/reflected-xss-rule.js +3 -3
- package/lib/protect/sample-aggregator.js +65 -57
- package/lib/protect/service.js +709 -104
- package/lib/reporter/models/app-activity/sample.js +6 -0
- package/lib/reporter/speedracer/unknown-connection-state.js +20 -32
- package/lib/reporter/translations/to-protobuf/settings/assess-features.js +4 -6
- package/lib/reporter/ts-reporter.js +1 -1
- package/lib/util/get-file-type.js +43 -0
- package/package.json +11 -11
- package/perf-logs.js +2 -5
package/bootstrap.js
CHANGED
|
@@ -47,9 +47,8 @@ Module.runMain = async function (...args) {
|
|
|
47
47
|
loader.logTime(appStartTime, 'application');
|
|
48
48
|
loader.logTime(startTime, 'agent & application');
|
|
49
49
|
} catch (err) {
|
|
50
|
-
// eslint-disable-next-line no-console
|
|
51
50
|
console.error(err);
|
|
52
|
-
// eslint-disable-
|
|
53
|
-
|
|
51
|
+
process.exit(-1); // eslint-disable-line no-process-exit
|
|
52
|
+
|
|
54
53
|
}
|
|
55
54
|
};
|
package/esm.mjs
CHANGED
|
@@ -24,45 +24,14 @@ if (enabled) {
|
|
|
24
24
|
await loader.resetArgs(process.argv[0], process.argv[1]);
|
|
25
25
|
const { readFile } = require('fs').promises;
|
|
26
26
|
|
|
27
|
-
const path = require('path');
|
|
28
27
|
const agent = require(`./lib/agent.js`);
|
|
29
28
|
const logger = require(`./lib/core/logger/index.js`)('contrast:esm-loaders');
|
|
30
29
|
const rewriter = require(`./lib/core/rewrite/index.js`)(agent);
|
|
31
30
|
const helpers = require(`./lib/hooks/module/helpers.js`);
|
|
32
|
-
const
|
|
31
|
+
const getType = require(`./lib/util/get-file-type.js`);
|
|
33
32
|
|
|
34
33
|
const loadedFromCache = new Set();
|
|
35
34
|
|
|
36
|
-
function getType(url) {
|
|
37
|
-
const {protocol, pathname} = new URL(url);
|
|
38
|
-
|
|
39
|
-
let parentType = 'commonjs';
|
|
40
|
-
try {
|
|
41
|
-
parentType = parent(pathname).parse().type;
|
|
42
|
-
} catch (err) {
|
|
43
|
-
// Node assumes `commonjs ` if there's no `type` set in package.json
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (protocol === 'node:') {
|
|
47
|
-
return 'builtin';
|
|
48
|
-
}
|
|
49
|
-
if (protocol === 'file:') {
|
|
50
|
-
const ext = path.extname(pathname);
|
|
51
|
-
if (
|
|
52
|
-
ext === '.mjs' ||
|
|
53
|
-
(ext === '.js' && parentType === 'module')
|
|
54
|
-
){
|
|
55
|
-
return 'module';
|
|
56
|
-
}
|
|
57
|
-
else if (
|
|
58
|
-
ext === '.cjs' ||
|
|
59
|
-
(ext === '.js' && parentType !== 'module')
|
|
60
|
-
){
|
|
61
|
-
return 'commonjs';
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return 'unknown';
|
|
65
|
-
}
|
|
66
35
|
/**
|
|
67
36
|
* The `getSource` hook is used to provide a custom method for retrieving source
|
|
68
37
|
* code. In our case, we check for previously rewritten ESM files in our cache
|
|
@@ -156,10 +125,15 @@ export async function load(url, context, defaultLoad) {
|
|
|
156
125
|
const filename = fileURLToPath(url);
|
|
157
126
|
|
|
158
127
|
try {
|
|
128
|
+
let result;
|
|
159
129
|
const cached = helpers.find(agent, filename);
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
130
|
+
if (cached) {
|
|
131
|
+
result = { code: cached };
|
|
132
|
+
} else {
|
|
133
|
+
const source = await readFile(filename, 'utf8');
|
|
134
|
+
result = rewriter.rewriteFile(source, filename, { sourceType: type === 'commonjs' ? 'script' : 'module' });
|
|
135
|
+
helpers.cacheWithSourceMap(agent, filename, result);
|
|
136
|
+
}
|
|
163
137
|
return { format: type, source: result.code };
|
|
164
138
|
} catch (err) {
|
|
165
139
|
logger.error(
|
|
@@ -152,12 +152,10 @@ class Membrane {
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
includes(object) {
|
|
155
|
-
// eslint-disable-next-line prettier/prettier
|
|
156
155
|
return this.members.has(object) || this.members.has(Membrane.unwrap(object));
|
|
157
156
|
}
|
|
158
157
|
|
|
159
158
|
getMapping(object) {
|
|
160
|
-
// eslint-disable-next-line prettier/prettier
|
|
161
159
|
return this.members.get(object) || this.members.get(Membrane.unwrap(object));
|
|
162
160
|
}
|
|
163
161
|
|
|
@@ -422,7 +420,7 @@ function copyMetadata(tar, prop, metadata) {
|
|
|
422
420
|
});
|
|
423
421
|
|
|
424
422
|
if (!nm.path) {
|
|
425
|
-
nm.path = prop ||
|
|
423
|
+
nm.path = prop || "''";
|
|
426
424
|
} else if (nm.isArray) {
|
|
427
425
|
nm.path += `[${prop}]`;
|
|
428
426
|
} else if (prop) {
|
|
@@ -158,7 +158,7 @@ function trim(list, start, stop) {
|
|
|
158
158
|
return trimmedList;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
|
|
161
|
+
// eslint-disable-next-line complexity
|
|
162
162
|
function trimInPlace(list, start, stop) {
|
|
163
163
|
if (start < 0) {
|
|
164
164
|
logger.debug(
|
|
@@ -205,7 +205,6 @@ function trimInPlace(list, start, stop) {
|
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
|
-
/* eslint-enable complexity */
|
|
209
208
|
|
|
210
209
|
/**
|
|
211
210
|
* Returns a filtered lists of TagRanges by type
|
|
@@ -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",
|
|
@@ -12,6 +12,8 @@ Copyright: 2022 Contrast Security, Inc
|
|
|
12
12
|
engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
13
|
way not consistent with the End User License Agreement.
|
|
14
14
|
*/
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
15
17
|
/**
|
|
16
18
|
* Contains helper functions used to manage the policy
|
|
17
19
|
* Management of hooked functions that are defined in assess policies(e.g. deadzone.json, propagators.json, rules.json)
|
|
@@ -184,7 +186,6 @@ utils.patch = function(obj, path, props, hookOptions) {
|
|
|
184
186
|
// this triggers, it /should/ be patched anyways.
|
|
185
187
|
const refs = new WeakSet();
|
|
186
188
|
|
|
187
|
-
/* eslint-disable complexity */
|
|
188
189
|
/**
|
|
189
190
|
* Recursively patch all exports on objects and classes
|
|
190
191
|
* note cyclomatic check disabled because there's no place to break
|
|
@@ -195,6 +196,7 @@ const refs = new WeakSet();
|
|
|
195
196
|
* @param {number} depth current recursion depth
|
|
196
197
|
* @returns {Object|function} returns original reference but patched
|
|
197
198
|
*/
|
|
199
|
+
// eslint-disable-next-line complexity
|
|
198
200
|
utils.patchRecursive = function(obj, hookOptions, depth) {
|
|
199
201
|
let protoNeedsPatch = false;
|
|
200
202
|
let propertyNames; // scoped value so that this doesn't need to be called twice.
|
|
@@ -242,7 +244,6 @@ utils.patchRecursive = function(obj, hookOptions, depth) {
|
|
|
242
244
|
|
|
243
245
|
return obj;
|
|
244
246
|
};
|
|
245
|
-
/* eslint-enable complexity */
|
|
246
247
|
|
|
247
248
|
/**
|
|
248
249
|
* Patches either global module or a resolved
|
|
@@ -44,14 +44,12 @@ const patcher = require('../../../hooks/patcher');
|
|
|
44
44
|
const { PATCH_TYPES } = require('../../../constants');
|
|
45
45
|
|
|
46
46
|
function makeCanary() {
|
|
47
|
-
/* eslint-disable prettier/prettier */
|
|
48
47
|
return crypto
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
/* eslint-enable prettier/prettier */
|
|
48
|
+
.randomBytes(12)
|
|
49
|
+
.toString('base64')
|
|
50
|
+
// map regex special chars to other chars
|
|
51
|
+
.replace(/\+/g, '%')
|
|
52
|
+
.replace(/\//g, '#');
|
|
55
53
|
}
|
|
56
54
|
|
|
57
55
|
// read canary as if the word was "marker". it's just a string inserted into the values
|
|
@@ -233,8 +231,7 @@ module.exports.handle = function() {
|
|
|
233
231
|
if (isString(value)) {
|
|
234
232
|
// make skeletal version of valProperties with only the tagRanges prop.
|
|
235
233
|
data.metadata[id] =
|
|
236
|
-
|
|
237
|
-
{ tagRanges: [new TagRange(0, value.length - 1, '')] };
|
|
234
|
+
{ tagRanges: [new TagRange(0, value.length - 1, '')] };
|
|
238
235
|
value = `${canary}${id}~${value}`;
|
|
239
236
|
id++;
|
|
240
237
|
}
|
|
@@ -333,7 +330,6 @@ module.exports.handle = function() {
|
|
|
333
330
|
// this check is required only for tests, i believe. sourceEvents should
|
|
334
331
|
// always exist, even if an empty array, for production code.
|
|
335
332
|
if (metadata.sourceEvents && !metadata.sourceEvents.length) {
|
|
336
|
-
// eslint-disable-next-line prettier/prettier
|
|
337
333
|
const { sourceEvents, braned } = data.metadata;
|
|
338
334
|
if (braned) {
|
|
339
335
|
sourceEvents.push(
|
|
@@ -381,7 +377,6 @@ module.exports.handle = function() {
|
|
|
381
377
|
// we only find our marker at the start of a string value.
|
|
382
378
|
canaryReplacementDiff += m.length - 1;
|
|
383
379
|
for (const tr of metadata[id].tagRanges) {
|
|
384
|
-
// eslint-disable-next-line prettier/prettier
|
|
385
380
|
tagRanges.push(new TagRange(tr.start + offset, tr.stop + offset, tr.tag));
|
|
386
381
|
}
|
|
387
382
|
|
|
@@ -31,7 +31,6 @@ function executeConditionals(schema) {
|
|
|
31
31
|
// if the conditional has a validator to check its schema, do it.
|
|
32
32
|
if (conditional.validator) {
|
|
33
33
|
if (!conditional.validator(schema[key])) {
|
|
34
|
-
// eslint-disable-next-line prettier/prettier
|
|
35
34
|
this.logger.trace(`conditional schema ${key} not valid`);
|
|
36
35
|
continue;
|
|
37
36
|
}
|
|
@@ -44,7 +43,6 @@ function executeConditionals(schema) {
|
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
if ('if' in schema) {
|
|
47
|
-
// eslint-disable-next-line prettier/prettier
|
|
48
46
|
valid = handleIfThenElse.call(this, schema.if, schema.then, schema.else) && valid;
|
|
49
47
|
}
|
|
50
48
|
|
|
@@ -158,7 +156,6 @@ const conditionalDispatch = new Map([
|
|
|
158
156
|
['allOf', { method: handleAllOf, validator: nonEmptyArray }]
|
|
159
157
|
]);
|
|
160
158
|
|
|
161
|
-
/* eslint-enable prettier/prettier */
|
|
162
159
|
const conditionals = [...conditionalDispatch.keys()];
|
|
163
160
|
|
|
164
161
|
module.exports = {
|
|
@@ -32,12 +32,11 @@ function t_boolean(schema, ctx) {
|
|
|
32
32
|
return typeof ctx.data === 'boolean';
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
/* eslint-disable complexity */
|
|
36
35
|
//
|
|
37
36
|
// type: number, integer
|
|
38
37
|
//
|
|
38
|
+
// eslint-disable-next-line complexity
|
|
39
39
|
function t_number(schema, ctx) {
|
|
40
|
-
/* eslint-disable prettier/prettier */
|
|
41
40
|
|
|
42
41
|
// ajv's number evaluation order
|
|
43
42
|
// integer (if type: integer)
|
|
@@ -83,7 +82,6 @@ function t_number(schema, ctx) {
|
|
|
83
82
|
if ('multipleOf' in schema && (ctx.data / schema.multipleOf) % 1 !== 0) {
|
|
84
83
|
valid = false;
|
|
85
84
|
}
|
|
86
|
-
/* eslint-enable prettier/prettier */
|
|
87
85
|
|
|
88
86
|
return valid;
|
|
89
87
|
}
|
|
@@ -91,6 +89,7 @@ function t_number(schema, ctx) {
|
|
|
91
89
|
//
|
|
92
90
|
// type: string
|
|
93
91
|
//
|
|
92
|
+
// eslint-disable-next-line complexity
|
|
94
93
|
function t_string(schema, ctx) {
|
|
95
94
|
// ajv's string evaluation order
|
|
96
95
|
// maxLength
|
|
@@ -134,6 +133,7 @@ function t_string(schema, ctx) {
|
|
|
134
133
|
//
|
|
135
134
|
// type: array
|
|
136
135
|
//
|
|
136
|
+
// eslint-disable-next-line complexity
|
|
137
137
|
function t_array(schema, ctx) {
|
|
138
138
|
if (!Array.isArray(ctx.data)) {
|
|
139
139
|
return false;
|
|
@@ -248,6 +248,7 @@ function elementsAreUnique(data) {
|
|
|
248
248
|
//
|
|
249
249
|
// type: object
|
|
250
250
|
//
|
|
251
|
+
// eslint-disable-next-line complexity
|
|
251
252
|
function t_object(schema, ctx) {
|
|
252
253
|
if (typeof ctx.data !== 'object') {
|
|
253
254
|
return false;
|
|
@@ -431,6 +432,7 @@ function* matching(patternProps, prop, ctx) {
|
|
|
431
432
|
//
|
|
432
433
|
// type: object helper
|
|
433
434
|
//
|
|
435
|
+
// eslint-disable-next-line complexity
|
|
434
436
|
function evaluateObjectDependencies(schema, ctx) {
|
|
435
437
|
const deps = schema.dependencies;
|
|
436
438
|
let valid = true;
|
|
@@ -455,7 +457,6 @@ function evaluateObjectDependencies(schema, ctx) {
|
|
|
455
457
|
}
|
|
456
458
|
// it's an array! if key is present in this.data then all the properties
|
|
457
459
|
// enumerated in the array (they're strings) must be present in this.data.
|
|
458
|
-
// eslint-disable-next-line prettier/prettier
|
|
459
460
|
if (key in ctx.data && !dep.every((requiredProp) => requiredProp in ctx.data)) {
|
|
460
461
|
// allErrors: true - check ajv's handling
|
|
461
462
|
valid = false;
|
|
@@ -40,7 +40,7 @@ const { objectWalk, objectOnlyWalk } = require('./object-walk');
|
|
|
40
40
|
// when userSchema sees '#/$defs/street' it tries to dereference it as a *local* reference
|
|
41
41
|
// because it has lost the addressSchema context.
|
|
42
42
|
//
|
|
43
|
-
|
|
43
|
+
// eslint-disable-next-line complexity
|
|
44
44
|
function evalInRefSchema(ref) {
|
|
45
45
|
const ctx = this;
|
|
46
46
|
const ix = ref.indexOf('#');
|
|
@@ -194,7 +194,6 @@ function findNoHashRef(ref, ctx) {
|
|
|
194
194
|
// yes, really.
|
|
195
195
|
|
|
196
196
|
// try simple path replacement first.
|
|
197
|
-
// eslint-disable-next-line node/no-deprecated-api
|
|
198
197
|
const urlParts = url.parse(ref);
|
|
199
198
|
if (schemaUrl && !urlParts.host && urlParts.path === ref) {
|
|
200
199
|
const newRef = `${schemaUrl.origin}/${ref}`;
|
|
@@ -19,8 +19,6 @@ const { evalInRefSchema } = require('./refs');
|
|
|
19
19
|
const fastDeepEqual = require('fast-deep-equal');
|
|
20
20
|
const { objectWalk } = require('./object-walk');
|
|
21
21
|
|
|
22
|
-
/* eslint-disable complexity */
|
|
23
|
-
|
|
24
22
|
class SchemaContext {
|
|
25
23
|
constructor(shadow, schema, object, key, options) {
|
|
26
24
|
const schemaName = schema.$id || '';
|
|
@@ -107,6 +105,7 @@ class SchemaContext {
|
|
|
107
105
|
* @param {object} ctx context for the evaluation, as defined above in evaluate().
|
|
108
106
|
* @returns {boolean} validation result
|
|
109
107
|
*/
|
|
108
|
+
// eslint-disable-next-line complexity
|
|
110
109
|
evaluate(schema) {
|
|
111
110
|
this.inferredType = false;
|
|
112
111
|
// don't do any validation of strings if the schema is boolean. while
|
|
@@ -165,6 +164,7 @@ class SchemaContext {
|
|
|
165
164
|
return false;
|
|
166
165
|
}
|
|
167
166
|
|
|
167
|
+
// eslint-disable-next-line complexity
|
|
168
168
|
typeSpecificEval(schema) {
|
|
169
169
|
const { type } = this;
|
|
170
170
|
|
|
@@ -374,7 +374,6 @@ class SchemaContext {
|
|
|
374
374
|
|
|
375
375
|
const t = require('./json-schema-type-evaluators');
|
|
376
376
|
|
|
377
|
-
/* eslint-disable prettier/prettier */
|
|
378
377
|
// map each JSON Schema type to the method name for it
|
|
379
378
|
SchemaContext.typeDispatch = new Map([
|
|
380
379
|
['null', { method: t.null, enumerator: 'scalarEnum' }],
|
|
@@ -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
|
);
|