@contrast/assess 1.10.0 → 1.11.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 (116) hide show
  1. package/lib/dataflow/event-factory.js +1 -1
  2. package/lib/dataflow/index.js +1 -1
  3. package/lib/dataflow/propagation/common.js +1 -1
  4. package/lib/dataflow/propagation/index.js +2 -1
  5. package/lib/dataflow/propagation/install/JSON/index.js +1 -1
  6. package/lib/dataflow/propagation/install/JSON/parse-fn.js +1 -1
  7. package/lib/dataflow/propagation/install/JSON/parse.js +1 -1
  8. package/lib/dataflow/propagation/install/JSON/stringify.js +1 -1
  9. package/lib/dataflow/propagation/install/array-prototype-join.js +1 -1
  10. package/lib/dataflow/propagation/install/buffer.js +1 -1
  11. package/lib/dataflow/propagation/install/contrast-methods/add.js +1 -1
  12. package/lib/dataflow/propagation/install/contrast-methods/index.js +1 -1
  13. package/lib/dataflow/propagation/install/contrast-methods/number.js +1 -1
  14. package/lib/dataflow/propagation/install/contrast-methods/string.js +1 -1
  15. package/lib/dataflow/propagation/install/contrast-methods/tag.js +1 -1
  16. package/lib/dataflow/propagation/install/decode-uri-component.js +1 -1
  17. package/lib/dataflow/propagation/install/ejs/escape-xml.js +1 -1
  18. package/lib/dataflow/propagation/install/ejs/index.js +1 -1
  19. package/lib/dataflow/propagation/install/encode-uri-component.js +1 -1
  20. package/lib/dataflow/propagation/install/escape-html.js +1 -1
  21. package/lib/dataflow/propagation/install/escape.js +1 -1
  22. package/lib/dataflow/propagation/install/handlebars-utils-escape-expression.js +1 -1
  23. package/lib/dataflow/propagation/install/isnumeric-0.js +1 -1
  24. package/lib/dataflow/propagation/install/mongoose/common.js +20 -0
  25. package/lib/dataflow/propagation/install/mongoose/index.js +5 -9
  26. package/lib/dataflow/propagation/install/mongoose/schema-map.js +149 -0
  27. package/lib/dataflow/propagation/install/mongoose/schema-mixed.js +162 -0
  28. package/lib/dataflow/propagation/install/mongoose/schema-string.js +91 -37
  29. package/lib/dataflow/propagation/install/mysql-connection-escape.js +1 -1
  30. package/lib/dataflow/propagation/install/parse-int.js +1 -1
  31. package/lib/dataflow/propagation/install/path/basename.js +1 -1
  32. package/lib/dataflow/propagation/install/path/common.js +1 -1
  33. package/lib/dataflow/propagation/install/path/index.js +1 -1
  34. package/lib/dataflow/propagation/install/path/join-and-resolve.js +1 -1
  35. package/lib/dataflow/propagation/install/path/normalize.js +1 -1
  36. package/lib/dataflow/propagation/install/pug/index.js +1 -1
  37. package/lib/dataflow/propagation/install/pug-runtime-escape.js +1 -1
  38. package/lib/dataflow/propagation/install/querystring/index.js +1 -1
  39. package/lib/dataflow/propagation/install/querystring/parse.js +1 -1
  40. package/lib/dataflow/propagation/install/reg-exp-prototype-exec.js +182 -0
  41. package/lib/dataflow/propagation/install/sequelize.js +1 -1
  42. package/lib/dataflow/propagation/install/sql-template-strings.js +1 -1
  43. package/lib/dataflow/propagation/install/string/concat.js +1 -1
  44. package/lib/dataflow/propagation/install/string/format-methods.js +1 -1
  45. package/lib/dataflow/propagation/install/string/html-methods.js +1 -1
  46. package/lib/dataflow/propagation/install/string/index.js +65 -1
  47. package/lib/dataflow/propagation/install/string/match-all.js +236 -0
  48. package/lib/dataflow/propagation/install/string/match.js +83 -37
  49. package/lib/dataflow/propagation/install/string/replace.js +2 -2
  50. package/lib/dataflow/propagation/install/string/slice.js +1 -1
  51. package/lib/dataflow/propagation/install/string/split.js +1 -1
  52. package/lib/dataflow/propagation/install/string/substring.js +1 -1
  53. package/lib/dataflow/propagation/install/string/trim.js +1 -1
  54. package/lib/dataflow/propagation/install/unescape.js +1 -1
  55. package/lib/dataflow/propagation/install/url/domain-parsers.js +1 -1
  56. package/lib/dataflow/propagation/install/url/index.js +3 -1
  57. package/lib/dataflow/propagation/install/url/parse.js +131 -0
  58. package/lib/dataflow/propagation/install/url/searchParams.js +133 -0
  59. package/lib/dataflow/propagation/install/url/url.js +9 -52
  60. package/lib/dataflow/propagation/install/validator/hooks.js +1 -1
  61. package/lib/dataflow/propagation/install/validator/index.js +1 -1
  62. package/lib/dataflow/propagation/install/validator/methods.js +1 -1
  63. package/lib/dataflow/sinks/common.js +1 -1
  64. package/lib/dataflow/sinks/index.js +1 -1
  65. package/lib/dataflow/sinks/install/child-process.js +1 -1
  66. package/lib/dataflow/sinks/install/eval.js +1 -1
  67. package/lib/dataflow/sinks/install/express/index.js +1 -1
  68. package/lib/dataflow/sinks/install/express/unvalidated-redirect.js +1 -1
  69. package/lib/dataflow/sinks/install/fastify/index.js +1 -1
  70. package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js +1 -1
  71. package/lib/dataflow/sinks/install/fs.js +1 -1
  72. package/lib/dataflow/sinks/install/function.js +1 -1
  73. package/lib/dataflow/sinks/install/http/index.js +1 -1
  74. package/lib/dataflow/sinks/install/http/request.js +1 -1
  75. package/lib/dataflow/sinks/install/http/server-response.js +1 -1
  76. package/lib/dataflow/sinks/install/koa/index.js +1 -1
  77. package/lib/dataflow/sinks/install/koa/unvalidated-redirect.js +1 -1
  78. package/lib/dataflow/sinks/install/marsdb.js +1 -1
  79. package/lib/dataflow/sinks/install/mongodb.js +31 -24
  80. package/lib/dataflow/sinks/install/mssql.js +1 -1
  81. package/lib/dataflow/sinks/install/mysql.js +1 -1
  82. package/lib/dataflow/sinks/install/postgres.js +1 -1
  83. package/lib/dataflow/sinks/install/sequelize.js +1 -1
  84. package/lib/dataflow/sinks/install/sqlite3.js +1 -1
  85. package/lib/dataflow/sinks/install/vm.js +1 -1
  86. package/lib/dataflow/sources/common.js +1 -1
  87. package/lib/dataflow/sources/handler.js +1 -1
  88. package/lib/dataflow/sources/index.js +1 -1
  89. package/lib/dataflow/sources/install/body-parser1.js +1 -1
  90. package/lib/dataflow/sources/install/busboy1.js +1 -1
  91. package/lib/dataflow/sources/install/cookie-parser1.js +1 -1
  92. package/lib/dataflow/sources/install/express/index.js +1 -1
  93. package/lib/dataflow/sources/install/express/params.js +1 -1
  94. package/lib/dataflow/sources/install/express/parsedUrl.js +1 -1
  95. package/lib/dataflow/sources/install/fastify/fastify.js +1 -1
  96. package/lib/dataflow/sources/install/fastify/index.js +1 -1
  97. package/lib/dataflow/sources/install/formidable1.js +1 -1
  98. package/lib/dataflow/sources/install/http.js +1 -1
  99. package/lib/dataflow/sources/install/koa/index.js +1 -1
  100. package/lib/dataflow/sources/install/koa/koa-bodyparsers.js +1 -1
  101. package/lib/dataflow/sources/install/koa/koa-routers.js +1 -1
  102. package/lib/dataflow/sources/install/koa/koa2.js +1 -1
  103. package/lib/dataflow/sources/install/qs6.js +1 -1
  104. package/lib/dataflow/sources/install/querystring.js +1 -1
  105. package/lib/dataflow/tag-utils.js +1 -1
  106. package/lib/dataflow/tracker.js +1 -1
  107. package/lib/dataflow/utils/is-safe-content-type.js +1 -1
  108. package/lib/dataflow/utils/is-vulnerable.js +1 -1
  109. package/lib/index.js +1 -1
  110. package/lib/response-scanning/handlers/index.js +36 -30
  111. package/lib/response-scanning/handlers/utils.js +1 -1
  112. package/lib/response-scanning/index.js +1 -1
  113. package/lib/response-scanning/install/http.js +3 -3
  114. package/lib/session-configuration/index.js +1 -1
  115. package/lib/session-configuration/install/http.js +1 -1
  116. package/package.json +2 -2
@@ -0,0 +1,236 @@
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
+ const { inspect } = require('@contrast/common');
18
+ const { patchType } = require('../../common');
19
+ const { createSubsetTags } = require('../../../tag-utils');
20
+
21
+ module.exports = function(core) {
22
+ const {
23
+ scopes: { sources, instrumentation },
24
+ patcher,
25
+ assess: {
26
+ dataflow: {
27
+ tracker,
28
+ eventFactory: { createPropagationEvent },
29
+ propagation: { stringInstrumentation },
30
+ },
31
+ },
32
+ } = core;
33
+ const name = 'String.prototype.matchAll';
34
+
35
+ function createPropagationEventForMatch({
36
+ objInfo,
37
+ startIdx,
38
+ match,
39
+ untrackedResult,
40
+ metadata,
41
+ }) {
42
+ const tags = createSubsetTags(objInfo.tags, startIdx, match.length);
43
+
44
+ if (!tags) return;
45
+
46
+ const { arg, hooked, orig } = metadata;
47
+
48
+ return createPropagationEvent({
49
+ name,
50
+ moduleName: 'String',
51
+ methodName: 'prototype.matchAll',
52
+ context: `'${objInfo.value}'.matcAll(${arg})`,
53
+ history: [objInfo],
54
+ object: {
55
+ value: objInfo.value,
56
+ tracked: true,
57
+ },
58
+ args: [
59
+ {
60
+ value: arg,
61
+ tracked: false,
62
+ },
63
+ ],
64
+ tags,
65
+ result: {
66
+ value: inspect(untrackedResult),
67
+ tracked: false,
68
+ },
69
+ stacktraceOpts: {
70
+ constructorOpt: hooked,
71
+ prependFrames: [orig],
72
+ },
73
+ source: 'O',
74
+ target: 'R',
75
+ });
76
+ }
77
+
78
+ return (stringInstrumentation.matchAll = {
79
+ install() {
80
+ patcher.patch(String.prototype, 'matchAll', {
81
+ name,
82
+ patchType,
83
+ around(origFn, data) {
84
+ const { args, obj, hooked, orig } = data;
85
+
86
+ if (
87
+ !obj ||
88
+ !args[0] ||
89
+ typeof obj !== 'string' ||
90
+ !sources.getStore()?.assess ||
91
+ instrumentation.isLocked()
92
+ )
93
+ return origFn();
94
+
95
+ const objInfo = tracker.getData(obj);
96
+
97
+ if (!objInfo) return origFn();
98
+
99
+ if (
100
+ !(args[0] instanceof RegExp) &&
101
+ typeof args[0][Symbol.matchAll] === 'function'
102
+ ) {
103
+ args[0][Symbol.matchAll] =
104
+ stringInstrumentation.utils.patchCustomMatcher(
105
+ args[0][Symbol.matchAll],
106
+ objInfo,
107
+ args[0],
108
+ name,
109
+ patchType
110
+ );
111
+
112
+ return origFn();
113
+ }
114
+
115
+ const result = origFn();
116
+ const newResult = {};
117
+
118
+ function next() {
119
+ const origRes = core.scopes.instrumentation.run(
120
+ { lock: true },
121
+ () => result.next()
122
+ );
123
+
124
+ if (!origRes?.value) return { done: true };
125
+
126
+ const resValue = origRes.value;
127
+ const untrackedResult = [...resValue];
128
+ untrackedResult.groups = resValue.groups && { ...resValue.groups };
129
+ untrackedResult.input = objInfo.value;
130
+ untrackedResult.index = resValue.index;
131
+ resValue.indices && (untrackedResult.indices = resValue.indices);
132
+
133
+ let searchIdx = resValue.index;
134
+ const metadata = { arg: inspect(args[0]), hooked, orig };
135
+
136
+ for (let i = 0; i < resValue.length; i++) {
137
+ let match = resValue[i];
138
+
139
+ if (!match) continue;
140
+
141
+ if (match === obj) {
142
+ // There is a case where the match
143
+ // is the whole original string in which case the value here is
144
+ // externalized and we need to make sure to track
145
+ // the non-exterrnalized one
146
+ match = objInfo.value;
147
+ }
148
+
149
+ const startIdx = objInfo.value.indexOf(match, searchIdx);
150
+ const event = createPropagationEventForMatch({
151
+ objInfo,
152
+ startIdx,
153
+ match,
154
+ untrackedResult,
155
+ metadata,
156
+ });
157
+
158
+ if (i > 0) {
159
+ searchIdx = startIdx + match.length;
160
+ }
161
+
162
+ if (!event) continue;
163
+
164
+ const { extern } = tracker.track(match, event);
165
+
166
+ if (extern) {
167
+ resValue[i] = extern;
168
+ }
169
+ }
170
+
171
+ if (resValue.groups) {
172
+ Object.keys(resValue.groups).forEach((key) => {
173
+ let res = resValue.groups[key];
174
+ let event;
175
+
176
+ if (!res) return;
177
+
178
+ if (res === obj) {
179
+ res = objInfo.value;
180
+ event = createPropagationEventForMatch({
181
+ objInfo,
182
+ startIdx: 0,
183
+ match: res,
184
+ untrackedResult,
185
+ metadata,
186
+ });
187
+ } else {
188
+ const startIdx = objInfo.value.indexOf(res, resValue.index);
189
+ event = createPropagationEventForMatch({
190
+ objInfo,
191
+ startIdx,
192
+ match: res,
193
+ untrackedResult,
194
+ metadata,
195
+ });
196
+ }
197
+
198
+ if (event) {
199
+ const { extern } = tracker.track(res, event);
200
+ if (extern) {
201
+ resValue.groups[key] = extern;
202
+ }
203
+ }
204
+ });
205
+ }
206
+
207
+ return { value: resValue, done: origRes.done };
208
+ }
209
+
210
+ Object.defineProperty(newResult, Symbol.iterator, {
211
+ enumerable: false,
212
+ value() {
213
+ return {
214
+ next,
215
+ };
216
+ },
217
+ });
218
+ Object.defineProperty(newResult, Symbol.toStringTag, {
219
+ enumerable: false,
220
+ value: 'RegExp String Iterator',
221
+ });
222
+
223
+ Object.setPrototypeOf(newResult, {
224
+ ...Object.getPrototypeOf(result),
225
+ next,
226
+ });
227
+
228
+ return newResult;
229
+ },
230
+ });
231
+ },
232
+ uninstall() {
233
+ String.prototype.matchAll = patcher.unwrap(String.prototype.matchAll);
234
+ },
235
+ });
236
+ };
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -23,28 +23,32 @@ module.exports = function(core) {
23
23
  scopes: { sources, instrumentation },
24
24
  patcher,
25
25
  assess: {
26
- dataflow: { tracker, eventFactory: { createPropagationEvent } }
27
- }
26
+ dataflow: {
27
+ tracker,
28
+ eventFactory: { createPropagationEvent },
29
+ propagation: { stringInstrumentation },
30
+ },
31
+ },
28
32
  } = core;
33
+ const name = 'String.prototype.match';
29
34
 
30
35
  function getPropagationEvent(data, res, objInfo, start) {
31
- const { name, args: origArgs, result, hooked, orig } = data;
36
+ const { args: origArgs, result, hooked, orig } = data;
32
37
  const tags = createSubsetTags(objInfo.tags, start, res.length);
33
38
  if (!tags) return;
34
39
 
35
- const args = origArgs.map((arg) => {
36
- const argInfo = tracker.getData(arg);
37
- return {
38
- value: argInfo ? argInfo.value : inspect(arg),
39
- tracked: !!argInfo
40
- };
41
- });
40
+ const args = [
41
+ {
42
+ value: inspect(origArgs[0]),
43
+ tracked: false,
44
+ },
45
+ ];
42
46
 
43
47
  return createPropagationEvent({
44
48
  name,
45
49
  moduleName: 'String',
46
50
  methodName: 'prototype.match',
47
- context: `'${objInfo.value}'.match(${join(args.map(a => a.value), ', ')})`,
51
+ context: `'${objInfo.value}'.match(${args[0].value})`,
48
52
  history: [objInfo],
49
53
  object: {
50
54
  value: objInfo.value,
@@ -54,48 +58,79 @@ module.exports = function(core) {
54
58
  tags,
55
59
  result: {
56
60
  value: join(result),
57
- tracked: false
61
+ tracked: false,
58
62
  },
59
63
  stacktraceOpts: {
60
64
  constructorOpt: hooked,
61
- prependFrames: [orig]
65
+ prependFrames: [orig],
62
66
  },
63
67
  source: 'O',
64
- target: 'R'
68
+ target: 'R',
65
69
  });
66
70
  }
67
71
 
68
- return core.assess.dataflow.propagation.stringInstrumentation.match = {
72
+ return (stringInstrumentation.match = {
69
73
  install() {
70
- const name = 'String.prototype.match';
71
-
72
74
  patcher.patch(String.prototype, 'match', {
73
75
  name,
74
76
  patchType,
75
- post(data) {
76
- const { args, obj, result } = data;
77
+ around(origFn, data) {
78
+ const { args, obj } = data;
77
79
  if (
78
80
  !obj ||
79
- !result ||
80
- args.length === 0 ||
81
- result.length === 0 ||
82
- !sources.getStore() ||
81
+ !args.length ||
82
+ !sources.getStore()?.assess ||
83
83
  typeof obj !== 'string' ||
84
84
  instrumentation.isLocked() ||
85
- (args.length === 1 && args[0] == null)
86
- ) return;
85
+ (args.length === 1 && !args[0])
86
+ )
87
+ return origFn();
87
88
 
88
89
  const objInfo = tracker.getData(obj);
89
- if (!objInfo) return;
90
+
91
+ if (!objInfo) return origFn();
92
+
93
+ if (
94
+ !(args[0] instanceof RegExp) &&
95
+ typeof args[0][Symbol.match] === 'function'
96
+ ) {
97
+ args[0][Symbol.match] =
98
+ stringInstrumentation.utils.patchCustomMatcher(
99
+ args[0][Symbol.match],
100
+ objInfo,
101
+ args[0],
102
+ name,
103
+ patchType
104
+ );
105
+
106
+ return origFn();
107
+ }
108
+
109
+ const result = (data.result = core.scopes.instrumentation.run(
110
+ { lock: true },
111
+ () => origFn()
112
+ ));
113
+
114
+ if (!result) return result;
90
115
 
91
116
  let idx = 0;
92
117
  const hasCaptureGroups = 'groups' in result;
93
118
  for (let i = 0; i < result.length; i++) {
94
- const res = result[i];
95
- if (!res || res === obj) continue;
96
- const start = obj.indexOf(res, idx);
97
- idx += hasCaptureGroups ? 0 : start + res.length;
98
- const event = getPropagationEvent(data, res, objInfo, start);
119
+ let res = result[i];
120
+ let event;
121
+
122
+ if (!res) continue;
123
+
124
+ if (res === obj) {
125
+ res = objInfo.value;
126
+ event = getPropagationEvent(data, res, objInfo, 0);
127
+ } else {
128
+ const start = obj.indexOf(res, idx);
129
+ idx += hasCaptureGroups && i === 0 ? 0 : res.length;
130
+
131
+ event = getPropagationEvent(data, res, objInfo, start);
132
+ }
133
+
99
134
  if (event) {
100
135
  const { extern } = tracker.track(res, event);
101
136
  if (extern) {
@@ -105,10 +140,19 @@ module.exports = function(core) {
105
140
  }
106
141
  if (hasCaptureGroups && result.groups) {
107
142
  Object.keys(result.groups).forEach((key) => {
108
- const res = result.groups[key];
109
- if (!res || res === obj) return;
110
- const start = obj.indexOf(res);
111
- const event = getPropagationEvent(data, res, objInfo, start);
143
+ let res = result.groups[key];
144
+ let event;
145
+
146
+ if (!res) return;
147
+
148
+ if (res === obj) {
149
+ res = objInfo.value;
150
+ event = getPropagationEvent(data, res, objInfo, 0);
151
+ } else {
152
+ const start = obj.indexOf(res);
153
+ event = getPropagationEvent(data, res, objInfo, start);
154
+ }
155
+
112
156
  if (event) {
113
157
  const { extern } = tracker.track(res, event);
114
158
  if (extern) {
@@ -117,11 +161,13 @@ module.exports = function(core) {
117
161
  }
118
162
  });
119
163
  }
164
+
165
+ return data.result;
120
166
  },
121
167
  });
122
168
  },
123
169
  uninstall() {
124
170
  String.prototype.match = patcher.unwrap(String.prototype.match);
125
171
  },
126
- };
172
+ });
127
173
  };
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -101,7 +101,7 @@ module.exports = function(core) {
101
101
  replacement = replaceSpecialCharacters(String(replacement), parsedArgs, data._replacementType);
102
102
 
103
103
  data._replacementInfo = tracker.getData(replacement);
104
- if (data._replacement) {
104
+ if (data._replacementInfo) {
105
105
  data._history.add(data._replacementInfo);
106
106
  }
107
107
  return { replacement, replacementInfo: data._replacementInfo };
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -28,6 +28,8 @@ module.exports = function(core) {
28
28
  };
29
29
 
30
30
  require('./domain-parsers')(core);
31
+ require('./parse')(core);
32
+ require('./searchParams')(core);
31
33
  require('./url')(core);
32
34
 
33
35
  return urlInstrumentation;
@@ -0,0 +1,131 @@
1
+ /*
2
+ * Copyright: 2022 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
+ const { inspect } = require('@contrast/common');
20
+
21
+ module.exports = function(core) {
22
+ const {
23
+ scopes: { sources, instrumentation },
24
+ patcher,
25
+ depHooks,
26
+ assess: {
27
+ dataflow: { tracker, eventFactory: { createPropagationEvent } }
28
+ }
29
+ } = core;
30
+
31
+ const keys = [
32
+ 'href',
33
+ [
34
+ 'protocol',
35
+ 'auth',
36
+ 'host',
37
+ [
38
+ 'hostname',
39
+ 'port'
40
+ ],
41
+ 'path',
42
+ [
43
+ 'pathname',
44
+ 'search',
45
+ [
46
+ 'query'
47
+ ]
48
+ ],
49
+ 'hash'
50
+ ]
51
+ ];
52
+
53
+
54
+ return core.assess.dataflow.propagation.urlInstrumentation.parse = {
55
+ install() {
56
+ depHooks.resolve({ name: 'url' }, (url) => {
57
+
58
+ const name = 'url.parse';
59
+
60
+ patcher.patch(url, 'parse', {
61
+ name,
62
+ patchType,
63
+ post(data) {
64
+ const { args, result, hooked, orig } = data;
65
+ if (!result || !args[0] || !sources.getStore()?.assess || instrumentation.isLocked()) return;
66
+
67
+ const url = args[0];
68
+ const argInfo = tracker.getData(url);
69
+
70
+ if (!argInfo) return;
71
+
72
+ const traverse = function(href, url, keys, idx = 0) {
73
+ let substr = href;
74
+ keys.forEach((key) => {
75
+ if (typeof key === 'string') {
76
+ const part = result[key];
77
+ if (part) {
78
+ const index = href.indexOf(part, idx - 1);
79
+ substr = href.substring(index, index + part.length);
80
+ idx += part.length;
81
+
82
+ const partInfo = tracker.getData(substr);
83
+ if (!partInfo) return;
84
+
85
+ const event = createPropagationEvent({
86
+ name,
87
+ moduleName: 'url',
88
+ methodName: 'parse',
89
+ context: `url.parse('${argInfo.value}')`,
90
+ object: {
91
+ value: 'url',
92
+ tracked: false
93
+ },
94
+ result: {
95
+ value: inspect(result),
96
+ tracked: true
97
+ },
98
+ args: [{ value: partInfo.value, tracked: true }],
99
+ tags: partInfo.tags,
100
+ history: [partInfo],
101
+ source: 'P',
102
+ target: 'R',
103
+ stacktraceOpts: {
104
+ constructorOpt: hooked,
105
+ prependFrames: [orig]
106
+ },
107
+ });
108
+ if (!event) return;
109
+
110
+ if (partInfo) {
111
+ Object.assign(partInfo, event);
112
+ }
113
+ const { extern } = partInfo || tracker.track(part, event);
114
+
115
+ if (extern) {
116
+ result[key] = extern;
117
+ }
118
+ }
119
+ } else {
120
+ traverse(substr, url, key, 0);
121
+ }
122
+ });
123
+ };
124
+
125
+ traverse(url, result, keys, 0);
126
+ }
127
+ });
128
+ });
129
+ },
130
+ };
131
+ };