@contrast/assess 1.12.0 → 1.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.
Files changed (42) hide show
  1. package/LICENSE +12 -0
  2. package/lib/dataflow/propagation/index.js +2 -0
  3. package/lib/dataflow/propagation/install/buffer.js +6 -5
  4. package/lib/dataflow/propagation/install/contrast-methods/add.js +3 -0
  5. package/lib/dataflow/propagation/install/joi/any.js +46 -0
  6. package/lib/dataflow/propagation/install/joi/boolean.js +109 -0
  7. package/lib/dataflow/propagation/install/joi/expression.js +99 -0
  8. package/lib/dataflow/propagation/install/joi/index.js +172 -0
  9. package/lib/dataflow/propagation/install/joi/keys.js +140 -0
  10. package/lib/dataflow/propagation/install/joi/number.js +107 -0
  11. package/lib/dataflow/propagation/install/joi/object.js +46 -0
  12. package/lib/dataflow/propagation/install/joi/string-schema.js +233 -0
  13. package/lib/dataflow/propagation/install/joi/utils.js +111 -0
  14. package/lib/dataflow/propagation/install/joi/values.js +154 -0
  15. package/lib/dataflow/propagation/install/path/basename.js +1 -3
  16. package/lib/dataflow/propagation/install/path/join-and-resolve.js +1 -3
  17. package/lib/dataflow/propagation/install/path/normalize.js +1 -3
  18. package/lib/dataflow/propagation/install/pug/index.js +2 -2
  19. package/lib/dataflow/propagation/install/reg-exp-prototype-exec.js +7 -6
  20. package/lib/dataflow/propagation/install/send.js +60 -0
  21. package/lib/dataflow/propagation/install/sequelize.js +4 -4
  22. package/lib/dataflow/propagation/install/string/match-all.js +6 -5
  23. package/lib/dataflow/propagation/install/string/match.js +14 -11
  24. package/lib/dataflow/propagation/install/string/replace.js +6 -5
  25. package/lib/dataflow/propagation/install/string/slice.js +3 -0
  26. package/lib/dataflow/propagation/install/string/split.js +6 -5
  27. package/lib/dataflow/propagation/install/string/substring.js +1 -3
  28. package/lib/dataflow/propagation/install/string/trim.js +2 -0
  29. package/lib/dataflow/propagation/install/url/parse.js +3 -8
  30. package/lib/dataflow/propagation/install/url/searchParams.js +7 -7
  31. package/lib/dataflow/propagation/install/validator/hooks.js +4 -4
  32. package/lib/dataflow/sinks/index.js +3 -3
  33. package/lib/dataflow/sinks/install/eval.js +63 -67
  34. package/lib/dataflow/sinks/install/fs.js +2 -2
  35. package/lib/dataflow/sinks/install/function.js +87 -91
  36. package/lib/dataflow/sinks/install/vm.js +7 -7
  37. package/lib/dataflow/sources/install/body-parser1.js +2 -2
  38. package/lib/dataflow/sources/install/fastify/fastify.js +1 -1
  39. package/lib/dataflow/sources/install/http.js +11 -10
  40. package/lib/dataflow/tracker.js +1 -3
  41. package/lib/response-scanning/install/http.js +3 -2
  42. package/package.json +14 -11
@@ -0,0 +1,107 @@
1
+ /*
2
+ * Copyright: 2023 Contrast Security, Inc
3
+ * Contact: support@contrastsecurity.com
4
+ * License: Commercial
5
+
6
+ * NOTICE: This Software and the patented inventions embodied within may only be
7
+ * used as part of Contrast Security’s commercial offerings. Even though it is
8
+ * made available through public repositories, use of this Software is subject to
9
+ * the applicable End User Licensing Agreement found at
10
+ * https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
11
+ * between Contrast Security and the End User. The Software may not be reverse
12
+ * engineered, modified, repackaged, sold, redistributed or otherwise used in a
13
+ * way not consistent with the End User License Agreement.
14
+ */
15
+
16
+ 'use strict';
17
+
18
+ const {
19
+ DataflowTag: { LIMITED_CHARS },
20
+ inspect,
21
+ } = require('@contrast/common');
22
+ const { patchType } = require('../../common');
23
+
24
+ module.exports = function(core) {
25
+ const {
26
+ depHooks,
27
+ scopes: { sources, instrumentation },
28
+ patcher,
29
+ assess: {
30
+ eventFactory: { createPropagationEvent },
31
+ dataflow: { tracker },
32
+ },
33
+ } = core;
34
+
35
+ /**
36
+ * patch Joi's number coerce function so it tags the input w/ limited-chars if the coercion was successful
37
+ * @param {Object} the number export
38
+ */
39
+ function instrumentJoiNumber(number) {
40
+ const def = Object.getPrototypeOf(number)?._definition;
41
+ def &&
42
+ patcher.patch(def.coerce, 'method', {
43
+ name: 'joi.number.coerce',
44
+ patchType,
45
+ post(data) {
46
+ if (
47
+ !data.result?.value ||
48
+ data.result.errors ||
49
+ !sources.getStore()?.assess ||
50
+ instrumentation.isLocked()
51
+ )
52
+ return;
53
+
54
+ const argInfo = tracker.getData(data.args[0]);
55
+
56
+ if (!argInfo) return;
57
+
58
+ const event = createPropagationEvent({
59
+ name: 'Joi.number.coerce',
60
+ moduleName: 'joi',
61
+ methodName: 'number.coerce',
62
+ history: [{ ...argInfo }],
63
+ object: {
64
+ tracked: false,
65
+ value: 'Joi.number',
66
+ },
67
+ args: [
68
+ { tracked: true, value: argInfo.value },
69
+ {
70
+ tracked: false,
71
+ value: {
72
+ ...inspect(data.args[1]),
73
+ original: argInfo.value,
74
+ },
75
+ },
76
+ ],
77
+ result: {
78
+ tracked: false,
79
+ value: data.result,
80
+ },
81
+ source: 'P0',
82
+ tags: {
83
+ ...argInfo.tags,
84
+ [LIMITED_CHARS]: [0, argInfo.value.length - 1],
85
+ },
86
+ target: 'P0',
87
+ stacktraceOpts: {
88
+ prependFrames: [data.orig],
89
+ },
90
+ });
91
+
92
+ if (event) {
93
+ Object.assign(argInfo, event);
94
+ }
95
+ },
96
+ });
97
+ }
98
+
99
+ return (core.assess.dataflow.propagation.joiInstrumentation.numberCoerce = {
100
+ install() {
101
+ depHooks.resolve(
102
+ { name: 'joi', file: 'lib/types/number.js', version: '>=17.0.0' },
103
+ instrumentJoiNumber
104
+ );
105
+ },
106
+ });
107
+ };
@@ -0,0 +1,46 @@
1
+ /*
2
+ * Copyright: 2023 Contrast Security, Inc
3
+ * Contact: support@contrastsecurity.com
4
+ * License: Commercial
5
+
6
+ * NOTICE: This Software and the patented inventions embodied within may only be
7
+ * used as part of Contrast Security’s commercial offerings. Even though it is
8
+ * made available through public repositories, use of this Software is subject to
9
+ * the applicable End User Licensing Agreement found at
10
+ * https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
11
+ * between Contrast Security and the End User. The Software may not be reverse
12
+ * engineered, modified, repackaged, sold, redistributed or otherwise used in a
13
+ * way not consistent with the End User License Agreement.
14
+ */
15
+
16
+ 'use strict';
17
+
18
+ module.exports = function(core) {
19
+ const {
20
+ depHooks,
21
+ assess: {
22
+ dataflow: {
23
+ propagation: { joiInstrumentation },
24
+ },
25
+ },
26
+ } = core;
27
+
28
+ joiInstrumentation.object = {
29
+ install() {
30
+ depHooks.resolve(
31
+ { name: 'joi', file: 'lib/types/object', version: '>=17.0.0' },
32
+ (exp) => {
33
+ const objectTypePrototype = Object.getPrototypeOf(exp);
34
+ const def = objectTypePrototype?._definition;
35
+
36
+ objectTypePrototype &&
37
+ joiInstrumentation.patchValidateAsync(
38
+ objectTypePrototype,
39
+ 'object.external'
40
+ );
41
+ def && joiInstrumentation.patchCustomValidate(def, 'object');
42
+ }
43
+ );
44
+ },
45
+ };
46
+ };
@@ -0,0 +1,233 @@
1
+ /*
2
+ * Copyright: 2023 Contrast Security, Inc
3
+ * Contact: support@contrastsecurity.com
4
+ * License: Commercial
5
+
6
+ * NOTICE: This Software and the patented inventions embodied within may only be
7
+ * used as part of Contrast Security’s commercial offerings. Even though it is
8
+ * made available through public repositories, use of this Software is subject to
9
+ * the applicable End User Licensing Agreement found at
10
+ * https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
11
+ * between Contrast Security and the End User. The Software may not be reverse
12
+ * engineered, modified, repackaged, sold, redistributed or otherwise used in a
13
+ * way not consistent with the End User License Agreement.
14
+ */
15
+
16
+ 'use strict';
17
+
18
+ const {
19
+ DataflowTag: { ALPHANUM_SPACE_HYPHEN, LIMITED_CHARS, STRING_TYPE_CHECKED },
20
+ inspect,
21
+ } = require('@contrast/common');
22
+ const { handleReferences } = require('./utils');
23
+ const { createFullLengthCopyTags } = require('../../../tag-utils');
24
+ const { patchType } = require('../../common');
25
+ const VALIDATORS = {
26
+ base64: ALPHANUM_SPACE_HYPHEN,
27
+ guid: ALPHANUM_SPACE_HYPHEN,
28
+ alphanum: ALPHANUM_SPACE_HYPHEN,
29
+ hex: ALPHANUM_SPACE_HYPHEN,
30
+ isoDate: ALPHANUM_SPACE_HYPHEN,
31
+ isoDuration: ALPHANUM_SPACE_HYPHEN,
32
+ token: ALPHANUM_SPACE_HYPHEN,
33
+ creditCard: LIMITED_CHARS,
34
+ ip: LIMITED_CHARS,
35
+ hostname: ALPHANUM_SPACE_HYPHEN,
36
+ domain: ALPHANUM_SPACE_HYPHEN,
37
+ };
38
+
39
+ module.exports = function(core) {
40
+ const {
41
+ depHooks,
42
+ scopes: { sources, instrumentation },
43
+ patcher,
44
+ assess: {
45
+ eventFactory: { createPropagationEvent },
46
+ dataflow: {
47
+ tracker, propagation: {
48
+ joiInstrumentation
49
+ }
50
+ },
51
+
52
+ },
53
+ } = core;
54
+
55
+ function definePropagation(methodName, tagName, inspectedSchema, origFn) {
56
+ function propagationFn(strInfo) {
57
+ const event = createPropagationEvent({
58
+ addedTags: [tagName],
59
+ name: `Joi.string.${methodName}`,
60
+ moduleName: 'joi',
61
+ methodName: `string.${methodName}`,
62
+ history: [{ ...strInfo }],
63
+ object: {
64
+ tracked: false,
65
+ value: 'Joi.string',
66
+ },
67
+ args: [
68
+ { tracked: true, value: strInfo.value },
69
+ { tracked: false, value: inspectedSchema },
70
+ ],
71
+ result: {
72
+ tracked: false,
73
+ value: undefined,
74
+ },
75
+ source: 'P0',
76
+ tags: {
77
+ ...strInfo.tags,
78
+ [tagName]: [0, strInfo.value.length - 1],
79
+ },
80
+ target: 'P0',
81
+ stacktraceOpts: {
82
+ prependFrames: [origFn],
83
+ },
84
+ });
85
+
86
+ if (event) {
87
+ Object.assign(strInfo, event);
88
+ }
89
+ }
90
+
91
+ Object.defineProperty(propagationFn, 'name', {
92
+ value: tagName,
93
+ writable: false,
94
+ });
95
+
96
+ return propagationFn;
97
+ }
98
+
99
+ function patchValidator(
100
+ validatorObj,
101
+ patchName,
102
+ validatorName,
103
+ tagName,
104
+ ) {
105
+ patcher.patch(validatorObj, 'validate', {
106
+ name: patchName,
107
+ patchType,
108
+ post(data) {
109
+ const [input, schema] = data.args;
110
+
111
+ if (
112
+ !input ||
113
+ (validatorName !== 'validate' && typeof data.result !== 'string') ||
114
+ (validatorName === 'validate' && data.result) ||
115
+ !sources.getStore()?.assess ||
116
+ instrumentation.isLocked()
117
+ )
118
+ return;
119
+
120
+ const inspectedSchema = inspect(schema);
121
+ const validation = definePropagation(
122
+ validatorName,
123
+ tagName,
124
+ inspectedSchema,
125
+ data.orig
126
+ );
127
+ const strInfo = tracker.getData(input);
128
+
129
+ handleReferences(tracker, schema, validation);
130
+
131
+ if (strInfo) {
132
+ validation(strInfo);
133
+ }
134
+ },
135
+ });
136
+ }
137
+
138
+ function reTrackCoercedValue(coerce) {
139
+ patcher.patch(coerce, 'method', {
140
+ name: 'joi.string._definition.coerce',
141
+ patchType,
142
+ post(data) {
143
+ const { args, result } = data;
144
+
145
+ if (
146
+ !args[0] ||
147
+ // currently, we are losing track of coerced isoDate only
148
+ !args[1].schema.$_getRule('isoDate') ||
149
+ !sources.getStore()?.assess ||
150
+ instrumentation.isLocked()
151
+ )
152
+ return;
153
+
154
+ const argInfo = tracker.getData(args[0]);
155
+
156
+ if (!argInfo) {
157
+ return;
158
+ }
159
+
160
+ const event = createPropagationEvent({
161
+ name: 'Joi.string.isoDate.coerce',
162
+ moduleName: 'joi',
163
+ methodName: 'string.isDate.coerce',
164
+ history: [argInfo],
165
+ object: {
166
+ tracked: false,
167
+ value: 'Joi.string',
168
+ },
169
+ args: [
170
+ { tracked: true, value: argInfo.value },
171
+ { tracked: false, value: inspect(args[1]) },
172
+ ],
173
+ result: {
174
+ tracked: false,
175
+ value: result,
176
+ },
177
+ source: 'P0',
178
+ tags: createFullLengthCopyTags(argInfo.tags, result.value.length),
179
+ target: 'R',
180
+ stacktraceOpts: {
181
+ prependFrames: [data.orig],
182
+ },
183
+ });
184
+
185
+ if (!event) return;
186
+
187
+ const { extern } = tracker.track(result.value, event);
188
+
189
+ if (extern) {
190
+ data.result.value = extern;
191
+ }
192
+ },
193
+ });
194
+ }
195
+
196
+ return (joiInstrumentation.stringSchema = {
197
+ install() {
198
+ depHooks.resolve(
199
+ { name: 'joi', file: 'lib/types/string.js', version: '>=17.0.0' },
200
+ (stringType) => {
201
+ const stringTypePrototype = Object.getPrototypeOf(stringType);
202
+ const definition = stringTypePrototype?._definition;
203
+ const rules = definition?.rules || {};
204
+ const coerce = definition?.coerce;
205
+
206
+ stringTypePrototype && joiInstrumentation.patchValidateAsync(stringTypePrototype, 'string.external');
207
+ definition && joiInstrumentation.patchCustomValidate(definition, 'string');
208
+ patchValidator(
209
+ definition,
210
+ 'joi.string.validate',
211
+ 'validate',
212
+ STRING_TYPE_CHECKED
213
+ );
214
+
215
+ for (const rule in VALIDATORS) {
216
+ if (rules[rule]) {
217
+ patchValidator(
218
+ rules[rule],
219
+ 'joi.string._definition.rules',
220
+ rule,
221
+ VALIDATORS[rule],
222
+ );
223
+ }
224
+ }
225
+
226
+ if (coerce && rules['isoDate']) {
227
+ reTrackCoercedValue(coerce);
228
+ }
229
+ }
230
+ );
231
+ },
232
+ });
233
+ };
@@ -0,0 +1,111 @@
1
+ /*
2
+ * Copyright: 2023 Contrast Security, Inc
3
+ * Contact: support@contrastsecurity.com
4
+ * License: Commercial
5
+
6
+ * NOTICE: This Software and the patented inventions embodied within may only be
7
+ * used as part of Contrast Security’s commercial offerings. Even though it is
8
+ * made available through public repositories, use of this Software is subject to
9
+ * the applicable End User Licensing Agreement found at
10
+ * https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
11
+ * between Contrast Security and the End User. The Software may not be reverse
12
+ * engineered, modified, repackaged, sold, redistributed or otherwise used in a
13
+ * way not consistent with the End User License Agreement.
14
+ */
15
+
16
+ 'use strict';
17
+
18
+ const { split, DataflowTag: { CUSTOM_VALIDATED }, join } = require('@contrast/common');
19
+
20
+ function getRefInstancesTrackingData(tracker, obj, refInstancesPaths) {
21
+ return refInstancesPaths
22
+ .map((referenceInstance) => {
23
+ const value = split(referenceInstance, '.').reduce(
24
+ (acc, v) => acc[v] || acc,
25
+ obj
26
+ );
27
+ return tracker.getData(value);
28
+ })
29
+ .filter(Boolean);
30
+ }
31
+
32
+ function tagCustomValidatedString(createPropagationEvent, strInfo, metadata) {
33
+ const { inspectedSecondArg, origFn, methodName, target } = metadata;
34
+
35
+ if (!strInfo) return;
36
+
37
+ const event = createPropagationEvent({
38
+ addedTags: [CUSTOM_VALIDATED],
39
+ name: `Joi.${methodName}`,
40
+ moduleName: 'joi',
41
+ methodName,
42
+ history: [{ ...strInfo }],
43
+ object: {
44
+ tracked: false,
45
+ value: `Joi.${methodName}`,
46
+ },
47
+ args: [
48
+ { tracked: true, value: strInfo.value },
49
+ inspectedSecondArg && { tracked: false, value: inspectedSecondArg },
50
+ ].filter(Boolean),
51
+ result: {
52
+ tracked: target === 'R',
53
+ value: target === 'R' ? strInfo.value : undefined
54
+ },
55
+ source: 'P0',
56
+ tags: {
57
+ ...strInfo.tags,
58
+ [CUSTOM_VALIDATED]: [0, strInfo.value.length - 1],
59
+ },
60
+ target,
61
+ stacktraceOpts: {
62
+ prependFrames: [origFn],
63
+ },
64
+ });
65
+
66
+ if (event) {
67
+ Object.assign(strInfo, event);
68
+ }
69
+ }
70
+
71
+ function handleReferences(tracker, schema, validationFn) {
72
+ const contrastData = schema?.schema?.__CONTRAST__;
73
+ let refTargetPath = contrastData && join(schema.state.path, '.');
74
+ const inReferenceTargetPath = contrastData && join(schema.state.path.slice(0, -1), '.');
75
+ if (contrastData?.inReferenceTargets.has(inReferenceTargetPath)) {
76
+ refTargetPath = join(
77
+ schema.state.path.slice(0, -1),
78
+ '.'
79
+ );
80
+ }
81
+ const references = contrastData?.refTargets[refTargetPath];
82
+
83
+ if (references?.length) {
84
+ getRefInstancesTrackingData(
85
+ tracker,
86
+ schema.state.ancestors[schema.state.ancestors.length - 1],
87
+ references
88
+ ).forEach((refMetadata) => {
89
+ let { eventualValidations } = refMetadata;
90
+ if (!eventualValidations) {
91
+ eventualValidations = { [refTargetPath]: [validationFn] };
92
+ } else if (!eventualValidations[refTargetPath]) {
93
+ eventualValidations[refTargetPath] = [validationFn];
94
+ } else if (
95
+ !eventualValidations[refTargetPath].find(
96
+ (v) => v.name === CUSTOM_VALIDATED
97
+ )
98
+ ) {
99
+ eventualValidations[refTargetPath].push(validationFn);
100
+ }
101
+
102
+ refMetadata.eventualValidations = eventualValidations;
103
+ });
104
+ }
105
+ }
106
+
107
+ module.exports = {
108
+ getRefInstancesTrackingData,
109
+ tagCustomValidatedString,
110
+ handleReferences
111
+ };
@@ -0,0 +1,154 @@
1
+ /*
2
+ * Copyright: 2023 Contrast Security, Inc
3
+ * Contact: support@contrastsecurity.com
4
+ * License: Commercial
5
+
6
+ * NOTICE: This Software and the patented inventions embodied within may only be
7
+ * used as part of Contrast Security’s commercial offerings. Even though it is
8
+ * made available through public repositories, use of this Software is subject to
9
+ * the applicable End User Licensing Agreement found at
10
+ * https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
11
+ * between Contrast Security and the End User. The Software may not be reverse
12
+ * engineered, modified, repackaged, sold, redistributed or otherwise used in a
13
+ * way not consistent with the End User License Agreement.
14
+ */
15
+
16
+ 'use strict';
17
+
18
+ const {
19
+ isNonEmptyObject, isString, inspect, traverseValues, join
20
+ } = require('@contrast/common');
21
+ const { createMergedTags } = require('../../../tag-utils');
22
+ const { patchType } = require('../../common');
23
+
24
+ module.exports = function(core) {
25
+ const {
26
+ depHooks,
27
+ scopes: { sources, instrumentation },
28
+ patcher,
29
+ assess: {
30
+ eventFactory: { createPropagationEvent },
31
+ dataflow: { tracker },
32
+ },
33
+ } = core;
34
+
35
+ function instrumentJoiValues(values) {
36
+ patcher.patch(values.prototype, 'get', {
37
+ name: 'joi.values',
38
+ patchType,
39
+ post(data) {
40
+ const {
41
+ args: [value, state, prefs],
42
+ result,
43
+ } = data;
44
+
45
+ if (
46
+ !value ||
47
+ !result ||
48
+ !sources.getStore()?.assess ||
49
+ instrumentation.isLocked()
50
+ ) return;
51
+
52
+ const metadata = {
53
+ state: inspect(state),
54
+ prefs: inspect(prefs),
55
+ orig: data.orig
56
+ };
57
+
58
+
59
+ if (result.ref) {
60
+ const targetAbsolutePath = join(result.ref.absolute(state), '.');
61
+
62
+ if (isString(value)) {
63
+ validateStringReferenceValue(value, result.value, result.ref, targetAbsolutePath, metadata);
64
+ } else if (isNonEmptyObject(value)) {
65
+ traverseValues(value, (path, _type, v) => {
66
+ validateStringReferenceValue(v, result.value, result.ref, join([...targetAbsolutePath, ...path], '.'), metadata, path);
67
+ });
68
+ }
69
+ } else if (data.obj?._values.has(data.result?.value)) {
70
+ if (isString(value)) {
71
+ // Should we untrack the argument too?
72
+ tracker.untrack(result.value);
73
+ } else if (isNonEmptyObject(result.value)) {
74
+ traverseValues(value, (_path, _type, v) => {
75
+ tracker.untrack(v);
76
+ });
77
+ }
78
+ }
79
+ },
80
+ });
81
+ }
82
+
83
+ function validateStringReferenceValue(value, resValue, ref, targetAbsolutePath, metadata, path = []) {
84
+ const strInfo = ref && tracker.getData(value);
85
+ const resStringValue = path.reduce((acc, val) => acc[val] || acc, resValue);
86
+
87
+ if (strInfo) {
88
+ const validations = strInfo.eventualValidations?.[targetAbsolutePath];
89
+ const mappedValueInfo = ref.map && tracker.getData(resStringValue);
90
+ const adjustedValueInfo = ref.adjust && tracker.getData(resStringValue);
91
+
92
+ if (validations?.length && !ref.map && !ref.adjust) {
93
+ validations.forEach(validation => validation(strInfo));
94
+
95
+ delete strInfo.eventualValidations[targetAbsolutePath];
96
+ !Object.keys(strInfo.eventualValidations).length && delete strInfo.eventualValidations;
97
+ }
98
+
99
+ if (mappedValueInfo || adjustedValueInfo) {
100
+ const resultInfo = mappedValueInfo || adjustedValueInfo;
101
+ const mergedTags = createMergedTags(strInfo.tags, resultInfo.tags);
102
+ const addedTags = [
103
+ Object.keys(resultInfo.tags).filter((tag) => !(Object.keys(strInfo.tags).find(t => t === tag))),
104
+ Object.keys(strInfo.tags).filter((tag) => !(Object.keys(resultInfo.tags).find(t => t === tag)))
105
+ ];
106
+
107
+ [strInfo, resultInfo].forEach((info, idx) => {
108
+ const event = createPropagationEvent({
109
+ addedTags: [addedTags[idx]],
110
+ name: 'Joi.values.get',
111
+ moduleName: 'joi',
112
+ methodName: 'values.get',
113
+ history: [{ ...info }],
114
+ object: {
115
+ tracked: false,
116
+ value: 'Joi.string',
117
+ },
118
+ args: [
119
+ { tracked: true, value: info.value },
120
+ { tracked: false, value: metadata.state },
121
+ { tracked: false, value: metadata.prefs },
122
+ ],
123
+ result: {
124
+ tracked: false,
125
+ value: inspect({ value: resultInfo.value, ref }),
126
+ },
127
+ source: 'P0',
128
+ tags: mergedTags,
129
+ target: 'A',
130
+ stacktraceOpts: {
131
+ prependFrames: [metadata.orig],
132
+ },
133
+ });
134
+
135
+ if (event) {
136
+ Object.assign(info, event);
137
+ }
138
+ });
139
+ } else {
140
+ (ref.map || ref.adjust) && tracker.untrack(value);
141
+ }
142
+ }
143
+ }
144
+
145
+ return (core.assess.dataflow.propagation.joiInstrumentation.values = {
146
+ install() {
147
+ depHooks.resolve(
148
+ { name: 'joi', file: 'lib/values.js', version: '>=17.0.0' },
149
+ instrumentJoiValues
150
+ );
151
+ },
152
+ });
153
+ };
154
+
@@ -102,9 +102,7 @@ module.exports = function(core) {
102
102
  },
103
103
  });
104
104
 
105
- if (!event) {
106
- return;
107
- }
105
+ if (!event) return;
108
106
 
109
107
  const { extern } = tracker.track(result, event);
110
108
 
@@ -118,9 +118,7 @@ module.exports = function(core) {
118
118
  },
119
119
  });
120
120
 
121
- if (!event) {
122
- return;
123
- }
121
+ if (!event) return;
124
122
 
125
123
  const { extern } = tracker.track(result, event);
126
124
 
@@ -101,9 +101,7 @@ module.exports = function(core) {
101
101
  },
102
102
  });
103
103
 
104
- if (!event) {
105
- return;
106
- }
104
+ if (!event) return;
107
105
 
108
106
  const { extern } = tracker.track(result, event);
109
107
 
@@ -16,7 +16,7 @@
16
16
 
17
17
  const { patchType } = require('../../common');
18
18
 
19
- module.exports = function(core) {
19
+ module.exports = function (core) {
20
20
  const store = { lock: true, name: 'assess:propagators:pug-compile' };
21
21
  const {
22
22
  scopes: { sources, instrumentation },
@@ -28,7 +28,7 @@ module.exports = function(core) {
28
28
  const pugInstrumentation = {
29
29
  install() {
30
30
  depHooks.resolve(
31
- { name: 'pug', file: 'lib/index.js' },
31
+ { name: 'pug' },
32
32
  (pug) => patcher.patch(pug, 'compile', {
33
33
  name: 'pug.compile',
34
34
  patchType,