@contrast/assess 1.77.0 → 1.79.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 (35) hide show
  1. package/lib/dataflow/propagation/index.js +2 -0
  2. package/lib/dataflow/propagation/install/JSON/stringify.js +14 -141
  3. package/lib/dataflow/propagation/install/JSON/tag-ranges.js +174 -0
  4. package/lib/dataflow/propagation/install/jsonwebtoken.js +1 -1
  5. package/lib/dataflow/propagation/install/sanitize-html.js +1 -1
  6. package/lib/dataflow/propagation/install/string/concat.js +23 -2
  7. package/lib/dataflow/propagation/install/url/url.js +56 -22
  8. package/lib/dataflow/propagation/install/util-format.js +43 -3
  9. package/lib/dataflow/propagation/install/zod/matrix.js +149 -0
  10. package/lib/dataflow/propagation/install/zod/process-result-v3.js +228 -0
  11. package/lib/dataflow/propagation/install/zod/process-result-v4.js +191 -0
  12. package/lib/dataflow/propagation/install/zod/utils.js +227 -0
  13. package/lib/dataflow/propagation/install/zod/v3.js +117 -0
  14. package/lib/dataflow/propagation/install/zod/v4.js +97 -0
  15. package/lib/dataflow/sinks/install/child-process.js +1 -1
  16. package/lib/dataflow/sinks/install/eval.js +1 -1
  17. package/lib/dataflow/sinks/install/fs.js +1 -1
  18. package/lib/dataflow/sinks/install/function.js +1 -1
  19. package/lib/dataflow/sinks/install/libxmljs.js +1 -1
  20. package/lib/dataflow/sinks/install/mssql.js +1 -1
  21. package/lib/dataflow/sinks/install/mysql.js +1 -1
  22. package/lib/dataflow/sinks/install/node-serialize.js +1 -1
  23. package/lib/dataflow/sinks/install/sequelize.js +1 -1
  24. package/lib/dataflow/sinks/install/sqlite3.js +1 -1
  25. package/lib/dataflow/sinks/install/vm.js +1 -1
  26. package/lib/dataflow/sources/handler.js +1 -1
  27. package/lib/dataflow/sources/install/@sap.js +14 -2
  28. package/lib/dataflow/sources/install/fastify-websocket.js +1 -1
  29. package/lib/dataflow/sources/install/socket.io.js +1 -1
  30. package/lib/event-factory.js +1 -1
  31. package/lib/get-source-context.js +1 -1
  32. package/lib/index.js +1 -1
  33. package/lib/make-source-context.js +1 -1
  34. package/lib/policy.js +1 -1
  35. package/package.json +16 -16
@@ -0,0 +1,227 @@
1
+ /*
2
+ * Copyright: 2026 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: { CUSTOM_VALIDATED },
20
+ isNonEmptyObject,
21
+ traverseValues,
22
+ } = require('@contrast/common');
23
+ const { createFullLengthCopyTags } = require('../../../tag-utils');
24
+
25
+ module.exports = function(core) {
26
+ const {
27
+ assess: {
28
+ getPropagatorContext,
29
+ inspect,
30
+ eventFactory: { createPropagationEvent },
31
+ dataflow: { tracker },
32
+ },
33
+ config,
34
+ } = core;
35
+
36
+ /**
37
+ * Merges the given tags onto an already-tracked string's existing tracking
38
+ * info, in place. Used for checks that validate a string without changing
39
+ * its identity (format checks, length checks, refine/superRefine).
40
+ */
41
+ function applyTags(value, tags, schema, methodName) {
42
+ if (!tags.size || typeof value !== 'string') return;
43
+
44
+ const strInfo = tracker.getData(value);
45
+ if (!strInfo) return;
46
+
47
+ const event = createPropagationEvent({
48
+ name: `zod.${methodName}`,
49
+ moduleName: 'zod',
50
+ methodName,
51
+ history: [{ ...strInfo }],
52
+ object: { tracked: false, value: 'zod.schema' },
53
+ args: [
54
+ { tracked: true, value: strInfo.value },
55
+ { tracked: false, value: inspect(schema) },
56
+ ],
57
+ result: { tracked: false, value: undefined },
58
+ source: 'P0',
59
+ tags: Object.assign(
60
+ { ...strInfo.tags },
61
+ ...[...tags].map((tag) => ({ [tag]: [0, strInfo.value.length - 1] }))
62
+ ),
63
+ target: 'P0',
64
+ });
65
+
66
+ if (event) Object.assign(strInfo, event);
67
+ }
68
+
69
+ /**
70
+ * Untracks a value that has become a member of a fixed allow-list
71
+ * (enum/literal) and is no longer meaningfully "tainted" freeform input.
72
+ */
73
+ function untrackValue(value) {
74
+ if (typeof value !== 'string' && typeof value !== 'object') return;
75
+ if (!tracker.getData(value)) return;
76
+ tracker.untrack(value);
77
+ }
78
+
79
+ /**
80
+ * Generic retrack for any '.check === overwrite' check (trim/toLowerCase/
81
+ * toUpperCase/normalize/slugify) - the check mutates the value in place
82
+ * inside zod, producing a new string object. Carries the input's existing
83
+ * tags forward onto the new string, generalizing joi's isoDate-coercion
84
+ * retrack (string-schema.js's reTrackCoercedValue) to any overwrite check.
85
+ * Returns the (possibly externalized) result string to use going forward.
86
+ */
87
+ function retrackOverwrite(input, result, schema) {
88
+ if (typeof input !== 'string' || typeof result !== 'string' || input === result) return result;
89
+
90
+ const inputInfo = tracker.getData(input);
91
+ if (!inputInfo) return result;
92
+
93
+ const tags = createFullLengthCopyTags(inputInfo.tags, result.length);
94
+ if (!tags) return result;
95
+
96
+ const event = createPropagationEvent({
97
+ name: 'zod.string.overwrite',
98
+ moduleName: 'zod',
99
+ methodName: 'string.overwrite',
100
+ history: [{ ...inputInfo }],
101
+ object: { tracked: false, value: 'zod.string' },
102
+ args: [
103
+ { tracked: true, value: inputInfo.value },
104
+ { tracked: false, value: inspect(schema) },
105
+ ],
106
+ result: { tracked: false, value: result },
107
+ source: 'P0',
108
+ tags,
109
+ target: 'R',
110
+ });
111
+
112
+ if (!event) return result;
113
+
114
+ const { extern } = tracker.track(result, event);
115
+ return extern || result;
116
+ }
117
+
118
+ /**
119
+ * Tags data derived from arbitrary user code (refine/superRefine/transform)
120
+ * as CUSTOM_VALIDATED, gated behind the same operator opt-in flag used by
121
+ * every other instrumented validator library in this codebase (joi,
122
+ * validator, mongoose) - trusting an arbitrary function to have performed
123
+ * meaningful validation/sanitization is exactly the judgment call this flag
124
+ * exists for.
125
+ *
126
+ * `retrack: true` (transform - the value's identity changes) tracks a fresh
127
+ * value derived from `sourceValue`'s tags. `retrack: false` (refine/
128
+ * superRefine - identity unchanged) merges the tag onto the existing value
129
+ * in place.
130
+ */
131
+ function tagCustomValidated({ sourceValue, targetValue, schema, methodName, retrack }) {
132
+ if (!config.assess.trust_custom_validators) return targetValue;
133
+
134
+ const sourceInfo = tracker.getData(sourceValue);
135
+ if (!sourceInfo) return targetValue;
136
+
137
+ if (!retrack) {
138
+ applyTags(targetValue, new Set([CUSTOM_VALIDATED]), schema, methodName);
139
+ return targetValue;
140
+ }
141
+
142
+ if (typeof targetValue !== 'string') return targetValue;
143
+
144
+ const event = createPropagationEvent({
145
+ addedTags: [CUSTOM_VALIDATED],
146
+ name: `zod.${methodName}`,
147
+ moduleName: 'zod',
148
+ methodName,
149
+ history: [{ ...sourceInfo }],
150
+ object: { tracked: false, value: `zod.${methodName}` },
151
+ args: [
152
+ { tracked: true, value: sourceInfo.value },
153
+ { tracked: false, value: inspect(schema) },
154
+ ],
155
+ result: { tracked: true, value: targetValue },
156
+ source: 'P0',
157
+ tags: { [CUSTOM_VALIDATED]: [0, targetValue.length - 1] },
158
+ target: 'R',
159
+ });
160
+
161
+ if (!event) return targetValue;
162
+
163
+ const { extern } = tracker.track(targetValue, event);
164
+ return extern || targetValue;
165
+ }
166
+
167
+ /**
168
+ * Handles a transform-shaped boundary - v4's `.transform()`/`.pipe()`
169
+ * (def.type === 'pipe') and v3's `.transform()` (ZodEffects, effect.type
170
+ * 'transform') and explicit `.pipe()` (ZodPipeline) all reduce to the same
171
+ * shape once each version's process-result module has already resolved
172
+ * `sourceSchema` (v4's `def.in`, v3's `def.schema`/`def.in`) - see each
173
+ * version's own walker for that resolution. Per design, format/type tags
174
+ * from the source schema's checks are deliberately NOT carried forward
175
+ * across the boundary - an arbitrary transform function can invalidate the
176
+ * security guarantee those tags imply (e.g. LIMITED_CHARS after `.email()`
177
+ * no longer holds once a transform can do anything to the string). Only
178
+ * CUSTOM_VALIDATED is applied to the output, gated by
179
+ * trust_custom_validators, treating the output like any other
180
+ * arbitrary-user-code result (refine/superRefine).
181
+ */
182
+ function handleTransformLike(sourceSchema, input, result) {
183
+ if (typeof result !== 'string' && !isNonEmptyObject(result)) return result;
184
+ if (typeof input !== 'string' && !isNonEmptyObject(input)) return result;
185
+
186
+ return tagCustomValidated({
187
+ sourceValue: input,
188
+ targetValue: result,
189
+ schema: sourceSchema,
190
+ methodName: 'transform',
191
+ retrack: input !== result,
192
+ });
193
+ }
194
+
195
+ /**
196
+ * True if `value` is, or contains, any currently-tracked string. Checked
197
+ * against BOTH the pre-parse input and the post-parse result before
198
+ * deciding whether the (potentially expensive) schema walk is worth
199
+ * running: a retracked value (trim/toLowerCase/transform) appears
200
+ * untracked in `result` alone, since it's a brand new string object, so
201
+ * checking `result` by itself would miss every retracking case.
202
+ */
203
+ function hasAnyTrackedValue(value) {
204
+ if (typeof value === 'string') return !!tracker.getData(value);
205
+ if (!isNonEmptyObject(value)) return false;
206
+
207
+ let found = false;
208
+ traverseValues(value, (_path, _type, v) => {
209
+ if (tracker.getData(v)) {
210
+ found = true;
211
+ return 1;
212
+ }
213
+ });
214
+ return found;
215
+ }
216
+
217
+ return {
218
+ applyTags,
219
+ untrackValue,
220
+ retrackOverwrite,
221
+ tagCustomValidated,
222
+ handleTransformLike,
223
+ hasAnyTrackedValue,
224
+ getPropagatorContext,
225
+ tracker,
226
+ };
227
+ };
@@ -0,0 +1,117 @@
1
+ /*
2
+ * Copyright: 2026 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 { patchType } = require('../../common');
19
+
20
+ // zod v3's classic API assigns `.parse`/`.safeParse`/`.parseAsync`/
21
+ // `.safeParseAsync` as bound-per-instance COPIES of shared `ZodType.prototype`
22
+ // methods (`this.safeParse = this.safeParse.bind(this)` in the constructor),
23
+ // unlike v4's independent per-instance closures - so the hook target here is
24
+ // the prototype itself, patched before any schema is constructed (dep-hooks
25
+ // patches at module-load time, before app code can call `z.string()`).
26
+ //
27
+ // Only `safeParse`/`safeParseAsync` are patched, NOT `parse`/`parseAsync`.
28
+ // In v3's own source, `parse()` is `{ const result = this.safeParse(data,
29
+ // params); if (result.success) return result.data; throw result.error; }`,
30
+ // and `parseAsync()` wraps `safeParseAsync()` the same way - they are thin
31
+ // wrappers, not independent implementations. Patching all 4 (mirroring v4)
32
+ // would fire this hook TWICE per `.parse()` call - once via the outer patch,
33
+ // once via the inner `safeParse` call `parse()` makes internally - causing
34
+ // duplicate `tracker.track()` calls and duplicate propagation-graph nodes on
35
+ // every retracking check (trim/toLowerCase/transform). Patching only the two
36
+ // primitives avoids this: `parse`/`parseAsync` pick up tagging for free since
37
+ // they read `result.data` off the now-mutated safeParse/safeParseAsync return
38
+ // value. The `spa` alias (`safeParseAsync` alias, bound from the same
39
+ // prototype method) is covered for free too.
40
+ //
41
+ // Known gap: `ZodType.prototype['~validate']` (the Standard Schema v1 entry
42
+ // point used by tRPC v11, @hookform/resolvers, and newer
43
+ // fastify-type-provider-zod) calls `_parseSync` directly, bypassing
44
+ // `safeParse` entirely - uninstrumented here. v4 has the identical gap via
45
+ // its own `~standard.validate`. See NODE-4022 for a follow-up covering both.
46
+ const PARSE_METHODS = ['safeParse', 'safeParseAsync'];
47
+
48
+ module.exports = function(core) {
49
+ const {
50
+ depHooks,
51
+ patcher,
52
+ logger,
53
+ assess: { getPropagatorContext },
54
+ } = core;
55
+ const { handleParseSuccess } = require('./process-result-v3')(core);
56
+
57
+ function patchZodType(types) {
58
+ const proto = types?.ZodType?.prototype;
59
+ if (!proto) return;
60
+
61
+ PARSE_METHODS.forEach((method) => {
62
+ const isAsync = method.endsWith('Async');
63
+
64
+ patcher.patch(proto, method, {
65
+ name: `zod.${method}`,
66
+ patchType,
67
+ usePerf: isAsync ? 'tbd' : 'sync',
68
+ post(data) {
69
+ try {
70
+ if (!getPropagatorContext()) return;
71
+
72
+ // patching a prototype method means the schema is the receiver
73
+ // (`data.obj`), not `data.args[0]` like v4's shared-module hook -
74
+ // `data.args` here is just `[input, params]`.
75
+ const schema = data.obj;
76
+ const [input] = data.args;
77
+
78
+ // both patched methods always return the uniform
79
+ // `{success, data|error}` shape, so - unlike v4 - there's no
80
+ // isSafe/isAsync result-shape branching needed here.
81
+ if (isAsync) {
82
+ data.result = data.result.then((outcome) => {
83
+ if (outcome?.success) {
84
+ outcome.data = handleParseSuccess(schema, input, outcome.data);
85
+ }
86
+ return outcome;
87
+ });
88
+ } else if (data.result?.success) {
89
+ data.result.data = handleParseSuccess(schema, input, data.result.data);
90
+ }
91
+ } catch (err) {
92
+ logger.error({ err }, 'zod v3 parse propagation hook failed');
93
+ }
94
+ },
95
+ });
96
+ });
97
+ }
98
+
99
+ const zodV3Instrumentation = core.assess.dataflow.propagation.zodV3Instrumentation = {
100
+ install() {
101
+ // stable, dominant 3.x line - unchanged from early 3.x through 3.24.4.
102
+ depHooks.resolve({ name: 'zod', file: 'lib/types.js', version: '>=3.0.0 <3.25.0' }, patchZodType);
103
+
104
+ // covers both the tail of the 3.25.x prerelease line AND, more
105
+ // importantly, any app on zod v4 using the `zod/v3` compat subpath
106
+ // (the standard migration path off v3) - byte-equivalent ZodType
107
+ // shape, confirmed against the installed zod package's own
108
+ // `v3/types.cjs`. Deliberately excludes zod@3.25.0-3.25.75, a short
109
+ // prerelease window that used two other transitional file layouts
110
+ // (`dist/commonjs/types.js`, then `dist/cjs/v3/types.js`) - not worth
111
+ // two more registrations for versions nobody pins to.
112
+ depHooks.resolve({ name: 'zod', file: 'v3/types.cjs', version: '>=3.25.76 <5' }, patchZodType);
113
+ },
114
+ };
115
+
116
+ return zodV3Instrumentation;
117
+ };
@@ -0,0 +1,97 @@
1
+ /*
2
+ * Copyright: 2026 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 { patchType } = require('../../common');
19
+
20
+ // zod v4's classic API (`import { z } from 'zod'`) assigns `.parse`/
21
+ // `.safeParse`/`.parseAsync`/`.safeParseAsync` as PER-INSTANCE closures in
22
+ // each schema's constructor (a deliberate zod performance optimization -
23
+ // there is no shared prototype method to patch). Every one of those closures
24
+ // calls through to the shared `v4/classic/parse.cjs` module's own exports
25
+ // (e.g. `inst.parse = (data, params) => parse.parse(inst, data, params, ...)`)
26
+ // as a live property lookup on every call, so patching that module's exports
27
+ // object is the single centralized, correct hook point.
28
+ const PARSE_METHODS = ['parse', 'safeParse', 'parseAsync', 'safeParseAsync'];
29
+
30
+ module.exports = function(core) {
31
+ const {
32
+ depHooks,
33
+ patcher,
34
+ logger,
35
+ assess: { getPropagatorContext },
36
+ } = core;
37
+ const { handleParseSuccess } = require('./process-result-v4')(core);
38
+
39
+ const zodInstrumentation = core.assess.dataflow.propagation.zodInstrumentation = {
40
+ install() {
41
+ depHooks.resolve(
42
+ { name: 'zod', file: 'v4/classic/parse.cjs', version: '>=4 <5' },
43
+ (parseExports) => {
44
+ PARSE_METHODS.forEach((method) => {
45
+ const isSafe = method.startsWith('safe');
46
+ const isAsync = method.endsWith('Async');
47
+
48
+ patcher.patch(parseExports, method, {
49
+ name: `zod.${method}`,
50
+ patchType,
51
+ usePerf: isAsync ? 'tbd' : 'sync',
52
+ post(data) {
53
+ try {
54
+ if (!getPropagatorContext()) return;
55
+
56
+ const [schema, input] = data.args;
57
+
58
+ // handleParseSuccess may retrack the value (trim/toLowerCase/
59
+ // transform produce a NEW string) - its return value must
60
+ // replace `data.result` (sync) or be spliced into a
61
+ // replacement promise (async) so the original caller of
62
+ // `.parse()`/`.parseAsync()` actually receives the tracked
63
+ // value, not zod's untracked one. `post` runs before the
64
+ // patcher returns `data.result` to the caller, so mutating
65
+ // it here (sync case) takes effect; for async, a promise's
66
+ // resolution value can't be swapped after the fact, so we
67
+ // replace `data.result` itself with a chained promise.
68
+ if (isAsync) {
69
+ data.result = data.result.then((outcome) => {
70
+ if (isSafe) {
71
+ if (outcome.success) {
72
+ outcome.data = handleParseSuccess(schema, input, outcome.data);
73
+ }
74
+ return outcome;
75
+ }
76
+ return handleParseSuccess(schema, input, outcome);
77
+ });
78
+ } else if (isSafe) {
79
+ if (data.result.success) {
80
+ data.result.data = handleParseSuccess(schema, input, data.result.data);
81
+ }
82
+ } else {
83
+ data.result = handleParseSuccess(schema, input, data.result);
84
+ }
85
+ } catch (err) {
86
+ logger.error({ err }, 'zod parse propagation hook failed');
87
+ }
88
+ },
89
+ });
90
+ });
91
+ }
92
+ );
93
+ },
94
+ };
95
+
96
+ return zodInstrumentation;
97
+ };
@@ -173,7 +173,7 @@ module.exports = function(core) {
173
173
  tags: trackedArgs[vulnerableArgIdx].tags,
174
174
  source: 'P1',
175
175
  stacktraceOpts: {
176
- contructorOpt: hooked,
176
+ constructorOpt: hooked,
177
177
  },
178
178
  });
179
179
 
@@ -118,7 +118,7 @@ module.exports = function (core) {
118
118
  tags: strInfo.tags,
119
119
  source: 'P0',
120
120
  stacktraceOpts: {
121
- contructorOpt: orig
121
+ constructorOpt: orig
122
122
  },
123
123
  });
124
124
 
@@ -100,7 +100,7 @@ module.exports = function(core) {
100
100
  tags: strInfo.tags,
101
101
  source: `P${i}`,
102
102
  stacktraceOpts: {
103
- contructorOpt: data.hooked,
103
+ constructorOpt: data.hooked,
104
104
  },
105
105
  });
106
106
 
@@ -130,7 +130,7 @@ module.exports = function (core) {
130
130
  tags: strInfo.tags,
131
131
  source: `P${origArgs.length - 1}`,
132
132
  stacktraceOpts: {
133
- contructorOpt: hooked,
133
+ constructorOpt: hooked,
134
134
  },
135
135
  });
136
136
 
@@ -102,7 +102,7 @@ module.exports = function(core) {
102
102
  tags: strInfo.tags,
103
103
  source: 'P0',
104
104
  stacktraceOpts: {
105
- contructorOpt: data.hooked,
105
+ constructorOpt: data.hooked,
106
106
  },
107
107
  });
108
108
 
@@ -91,7 +91,7 @@ module.exports = function (core) {
91
91
  tags: strInfo.tags,
92
92
  source: 'P0',
93
93
  stacktraceOpts: {
94
- contructorOpt: data.hooked,
94
+ constructorOpt: data.hooked,
95
95
  },
96
96
  });
97
97
 
@@ -108,7 +108,7 @@ module.exports = function(core) {
108
108
  tags: strInfo.tags,
109
109
  source: 'P0',
110
110
  stacktraceOpts: {
111
- contructorOpt: data.hooked,
111
+ constructorOpt: data.hooked,
112
112
  },
113
113
  });
114
114
 
@@ -81,7 +81,7 @@ module.exports = function(core) {
81
81
  tags: strInfo.tags,
82
82
  source: 'P0',
83
83
  stacktraceOpts: {
84
- contructorOpt: data.hooked,
84
+ constructorOpt: data.hooked,
85
85
  },
86
86
  });
87
87
 
@@ -120,7 +120,7 @@ module.exports = function (core) {
120
120
  tags: queryInfo?.tags,
121
121
  source: 'P0',
122
122
  stacktraceOpts: {
123
- contructorOpt: hooked,
123
+ constructorOpt: hooked,
124
124
  },
125
125
  });
126
126
 
@@ -92,7 +92,7 @@ module.exports = function(core) {
92
92
  tags: strInfo.tags,
93
93
  source: 'P0',
94
94
  stacktraceOpts: {
95
- contructorOpt: data.hooked,
95
+ constructorOpt: data.hooked,
96
96
  },
97
97
  });
98
98
 
@@ -231,7 +231,7 @@ module.exports = function (core) {
231
231
  : vulnerableArg.strInfo.tags,
232
232
  source: `P${vulnerableArg.idx}`,
233
233
  stacktraceOpts: {
234
- contructorOpt: hooked,
234
+ constructorOpt: hooked,
235
235
  },
236
236
  });
237
237
 
@@ -25,7 +25,7 @@ const {
25
25
  StringPrototypeToLowerCase
26
26
  }
27
27
  } = require('@contrast/common');
28
- const { Core } = require('@contrast/core/lib/ioc/core');
28
+ const { Core } = require('@contrast/core');
29
29
 
30
30
  module.exports = Core.makeComponent({
31
31
  name: 'assess.dataflow.sources.handle',
@@ -15,7 +15,7 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const { kComponentName, ComponentBase } = require('@contrast/core/lib/ioc/core');
18
+ const { kComponentName, ComponentBase } = require('@contrast/core');
19
19
  const {
20
20
  InputType,
21
21
  traverseValues,
@@ -47,7 +47,6 @@ module.exports = class SAPInstrumentation extends ComponentBase {
47
47
  sourceContext.cdsDispatchHandled = true;
48
48
  const cdsReq = data.args[0];
49
49
 
50
- // this should also take care of cdsReq.req._data and cdsReq.data
51
50
  if (cdsReq?.req?.body) {
52
51
  assess.dataflow.sources.handle({
53
52
  context: 'req.body',
@@ -75,6 +74,19 @@ module.exports = class SAPInstrumentation extends ComponentBase {
75
74
  }
76
75
  }
77
76
 
77
+ if (cdsReq?.data) {
78
+ assess.dataflow.sources.handle({
79
+ context: 'req.data',
80
+ data: cdsReq.data,
81
+ name: data.name,
82
+ inputType: InputType.BODY,
83
+ sourceContext,
84
+ stacktraceOpts: {
85
+ constructorOpt: data.hooked,
86
+ },
87
+ });
88
+ }
89
+
78
90
  if (cdsReq?.params?.length) {
79
91
  assess.dataflow.sources.handle({
80
92
  context: 'req.params',
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const { InputType, set } = require('@contrast/common');
4
- const Core = require('@contrast/core/lib/ioc/core');
4
+ const Core = require('@contrast/core');
5
5
  const { patchType } = require('../common');
6
6
 
7
7
  const COMPONENT_NAME = 'assess.dataflow.sources.fastifyWebsocketInstrumentation';
@@ -15,7 +15,7 @@
15
15
  'use strict';
16
16
 
17
17
  const { InputType, set } = require('@contrast/common');
18
- const Core = require('@contrast/core/lib/ioc/core');
18
+ const Core = require('@contrast/core');
19
19
  const { patchType } = require('../common');
20
20
 
21
21
  const COMPONENT_NAME = 'assess.dataflow.sources.socketIoInstrumentation';
@@ -23,7 +23,7 @@ const {
23
23
  primordials: { StringPrototypeMatch }
24
24
  } = require('@contrast/common');
25
25
  const { ConfigSource } = require('@contrast/config');
26
- const { Core } = require('@contrast/core/lib/ioc/core');
26
+ const { Core } = require('@contrast/core');
27
27
 
28
28
  const ANNOTATION_REGEX = /^(A|O|R|P|P\d+)$/;
29
29
  const SOURCE_EVENT_MSG = 'Source event not created: %s';
@@ -15,7 +15,7 @@
15
15
  // @ts-check
16
16
  'use strict';
17
17
 
18
- const { Core } = require('@contrast/core/lib/ioc/core');
18
+ const { Core } = require('@contrast/core');
19
19
 
20
20
  /** @typedef {import('@contrast/assess').SourceContext} SourceContext */
21
21
 
package/lib/index.js CHANGED
@@ -42,7 +42,7 @@ module.exports = function assess(core) {
42
42
  }
43
43
 
44
44
  // configure rewriter
45
- core.rewriter.install('assess');
45
+ core.rewriter.modes.add('assess');
46
46
  // dispatch subcomponent installation
47
47
  callChildComponentMethodsSync(core.assess, 'install');
48
48
  },
@@ -15,7 +15,7 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const { Core } = require('@contrast/core/lib/ioc/core');
18
+ const { Core } = require('@contrast/core');
19
19
 
20
20
  /**
21
21
  * @param {{
package/lib/policy.js CHANGED
@@ -25,7 +25,7 @@ const {
25
25
  set,
26
26
  primordials: { ArrayPrototypeJoin, RegExpPrototypeTest }
27
27
  } = require('@contrast/common');
28
- const { Core } = require('@contrast/core/lib/ioc/core');
28
+ const { Core } = require('@contrast/core');
29
29
 
30
30
  const ASSESS_RULES = Object.values({
31
31
  ...Rule,