@contrast/assess 1.9.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 +3 -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 +124 -0
  32. package/lib/dataflow/propagation/install/path/common.js +176 -0
  33. package/lib/dataflow/propagation/install/path/index.js +32 -0
  34. package/lib/dataflow/propagation/install/path/join-and-resolve.js +141 -0
  35. package/lib/dataflow/propagation/install/path/normalize.js +123 -0
  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 +2 -2
  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 +84 -38
  49. package/lib/dataflow/propagation/install/string/replace.js +3 -3
  50. package/lib/dataflow/propagation/install/string/slice.js +2 -2
  51. package/lib/dataflow/propagation/install/string/split.js +2 -2
  52. package/lib/dataflow/propagation/install/string/substring.js +3 -3
  53. package/lib/dataflow/propagation/install/string/trim.js +2 -2
  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 +4 -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 +185 -0
  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 +9 -5
  65. package/lib/dataflow/sinks/install/child-process.js +1 -1
  66. package/lib/dataflow/sinks/install/eval.js +138 -0
  67. package/lib/dataflow/sinks/install/express/index.js +1 -1
  68. package/lib/dataflow/sinks/install/express/unvalidated-redirect.js +2 -2
  69. package/lib/dataflow/sinks/install/fastify/index.js +1 -1
  70. package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js +3 -2
  71. package/lib/dataflow/sinks/install/fs.js +4 -4
  72. package/lib/dataflow/sinks/install/function.js +160 -0
  73. package/lib/dataflow/sinks/install/http/index.js +31 -0
  74. package/lib/dataflow/sinks/install/http/request.js +152 -0
  75. package/lib/dataflow/sinks/install/{http.js → http/server-response.js} +3 -3
  76. package/lib/dataflow/sinks/install/koa/index.js +1 -1
  77. package/lib/dataflow/sinks/install/koa/unvalidated-redirect.js +2 -2
  78. package/lib/dataflow/sinks/install/marsdb.js +1 -1
  79. package/lib/dataflow/sinks/install/mongodb.js +34 -46
  80. package/lib/dataflow/sinks/install/mssql.js +45 -32
  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 +276 -0
  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 +71 -2
  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;
32
- const tags = createSubsetTags(objInfo.tags, start, res.length - 1);
36
+ const { args: origArgs, result, hooked, orig } = data;
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 : 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 };
@@ -115,7 +115,7 @@ module.exports = function(core) {
115
115
  const { _accumOffset, _accumTags } = data;
116
116
  const { replacement, replacementInfo } = getReplacementInfo(data, args, parsedArgs);
117
117
 
118
- const preTags = createSubsetTags(_accumTags, 0, _accumOffset + matchIdx - 1);
118
+ const preTags = createSubsetTags(_accumTags, 0, _accumOffset + matchIdx);
119
119
  const postTags = createSubsetTags(_accumTags, _accumOffset + matchIdx + match.length, str.length - matchIdx - match.length);
120
120
  data._accumOffset += (replacement.length - match.length);
121
121
  if (preTags || postTags || 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
 
@@ -37,7 +37,7 @@ module.exports = function(core) {
37
37
  end = obj.length - (Math.abs(end) || 0);
38
38
  }
39
39
 
40
- const subsetLen = (hasSingleArg ? obj.length - start : Math.abs(end - start)) - 1;
40
+ const subsetLen = (hasSingleArg ? obj.length - start : Math.abs(end - start));
41
41
 
42
42
  return {
43
43
  startIdx: start,
@@ -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
 
@@ -60,7 +60,7 @@ module.exports = function(core) {
60
60
  const objSubstr = obj.substring(start, start + res.length);
61
61
  const objSubstrInfo = tracker.getData(objSubstr);
62
62
  if (objSubstrInfo) {
63
- const tags = createSubsetTags(objInfo.tags, start, res.length - 1);
63
+ const tags = createSubsetTags(objInfo.tags, start, res.length);
64
64
  if (!tags) continue;
65
65
 
66
66
  const args = origArgs.map((arg) => {
@@ -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
 
@@ -30,7 +30,7 @@ module.exports = function(core) {
30
30
 
31
31
  function calculateSubsetRangeForSubstr({ args, result }) {
32
32
  const startIdx = args[0] || 0;
33
- const subsetLen = args[1] === undefined ? result.length - startIdx - 1 : args[1] - 1;
33
+ const subsetLen = args[1] === undefined ? result.length - startIdx : args[1];
34
34
 
35
35
  return {
36
36
  startIdx,
@@ -41,7 +41,7 @@ module.exports = function(core) {
41
41
  function calculateSubsetRangeForSubstring({ obj, args }) {
42
42
  const hasSingleArg = args[1] === undefined;
43
43
  const startIdx = hasSingleArg ? args[0] : Math.min(...args);
44
- const subsetLen = (hasSingleArg ? obj.length - args[0] : Math.abs(args[1] - args[0])) - 1;
44
+ const subsetLen = (hasSingleArg ? obj.length - args[0] : Math.abs(args[1] - args[0]));
45
45
 
46
46
  return {
47
47
  startIdx,
@@ -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
 
@@ -52,7 +52,7 @@ module.exports = function(core) {
52
52
  const start = presetStart || obj.indexOf(result);
53
53
  const newTags = {};
54
54
  const objTags = objInfo.tags || {};
55
- Object.assign(newTags, createSubsetTags(objTags, start, result.length - 1));
55
+ Object.assign(newTags, createSubsetTags(objTags, start, result.length));
56
56
 
57
57
  if (!newTags.untrusted) {
58
58
  return;
@@ -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,9 @@ module.exports = function(core) {
28
28
  };
29
29
 
30
30
  require('./domain-parsers')(core);
31
+ require('./parse')(core);
32
+ require('./searchParams')(core);
33
+ require('./url')(core);
31
34
 
32
35
  return urlInstrumentation;
33
36
  };
@@ -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
+ };