@contrast/assess 1.53.0 → 1.54.1

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 (83) hide show
  1. package/lib/crypto-analysis/install/math.js +0 -1
  2. package/lib/dataflow/propagation/common.js +6 -6
  3. package/lib/dataflow/propagation/install/JSON/parse.js +7 -3
  4. package/lib/dataflow/propagation/install/JSON/stringify.js +7 -6
  5. package/lib/dataflow/propagation/install/array-prototype-join.js +5 -8
  6. package/lib/dataflow/propagation/install/buffer.js +4 -4
  7. package/lib/dataflow/propagation/install/contrast-methods/add.js +42 -38
  8. package/lib/dataflow/propagation/install/contrast-methods/string.js +4 -2
  9. package/lib/dataflow/propagation/install/contrast-methods/tag.js +3 -1
  10. package/lib/dataflow/propagation/install/decode-uri-component.js +5 -7
  11. package/lib/dataflow/propagation/install/ejs/escape-xml.js +4 -3
  12. package/lib/dataflow/propagation/install/ejs/template.js +1 -1
  13. package/lib/dataflow/propagation/install/encode-uri.js +5 -7
  14. package/lib/dataflow/propagation/install/escape-html.js +4 -3
  15. package/lib/dataflow/propagation/install/escape.js +5 -7
  16. package/lib/dataflow/propagation/install/handlebars-utils-escape-expression.js +4 -3
  17. package/lib/dataflow/propagation/install/joi/boolean.js +1 -3
  18. package/lib/dataflow/propagation/install/joi/expression.js +1 -3
  19. package/lib/dataflow/propagation/install/joi/number.js +1 -3
  20. package/lib/dataflow/propagation/install/joi/string-schema.js +2 -6
  21. package/lib/dataflow/propagation/install/joi/utils.js +2 -4
  22. package/lib/dataflow/propagation/install/joi/values.js +1 -3
  23. package/lib/dataflow/propagation/install/mongoose/schema-map.js +1 -3
  24. package/lib/dataflow/propagation/install/mongoose/schema-mixed.js +1 -3
  25. package/lib/dataflow/propagation/install/mongoose/schema-string.js +4 -5
  26. package/lib/dataflow/propagation/install/mustache-escape.js +4 -3
  27. package/lib/dataflow/propagation/install/mysql-connection-escape.js +9 -8
  28. package/lib/dataflow/propagation/install/path/basename.js +6 -7
  29. package/lib/dataflow/propagation/install/path/common.js +1 -0
  30. package/lib/dataflow/propagation/install/path/dirname.js +6 -8
  31. package/lib/dataflow/propagation/install/path/extname.js +8 -22
  32. package/lib/dataflow/propagation/install/path/format.js +6 -10
  33. package/lib/dataflow/propagation/install/path/join-and-resolve.js +7 -13
  34. package/lib/dataflow/propagation/install/path/normalize.js +8 -18
  35. package/lib/dataflow/propagation/install/path/parse.js +8 -18
  36. package/lib/dataflow/propagation/install/path/relative.js +8 -15
  37. package/lib/dataflow/propagation/install/path/toNamespacedPath.js +7 -18
  38. package/lib/dataflow/propagation/install/pug/index.js +1 -1
  39. package/lib/dataflow/propagation/install/pug-runtime-escape.js +6 -5
  40. package/lib/dataflow/propagation/install/querystring/escape.js +3 -1
  41. package/lib/dataflow/propagation/install/querystring/parse.js +3 -2
  42. package/lib/dataflow/propagation/install/querystring/stringify.js +4 -4
  43. package/lib/dataflow/propagation/install/reg-exp-prototype-exec.js +4 -3
  44. package/lib/dataflow/propagation/install/sequelize/query-generator.js +0 -1
  45. package/lib/dataflow/propagation/install/sequelize/sql-string.js +16 -17
  46. package/lib/dataflow/propagation/install/sql-template-strings.js +6 -10
  47. package/lib/dataflow/propagation/install/string/concat.js +4 -4
  48. package/lib/dataflow/propagation/install/string/format-methods.js +4 -4
  49. package/lib/dataflow/propagation/install/string/html-methods.js +5 -6
  50. package/lib/dataflow/propagation/install/string/index.js +4 -3
  51. package/lib/dataflow/propagation/install/string/match-all.js +7 -6
  52. package/lib/dataflow/propagation/install/string/match.js +10 -9
  53. package/lib/dataflow/propagation/install/string/replace.js +173 -158
  54. package/lib/dataflow/propagation/install/string/slice.js +4 -3
  55. package/lib/dataflow/propagation/install/string/split.js +11 -11
  56. package/lib/dataflow/propagation/install/string/substring.js +4 -3
  57. package/lib/dataflow/propagation/install/string/trim.js +4 -3
  58. package/lib/dataflow/propagation/install/unescape.js +6 -14
  59. package/lib/dataflow/propagation/install/url/domain-parsers.js +6 -5
  60. package/lib/dataflow/propagation/install/url/parse.js +17 -17
  61. package/lib/dataflow/propagation/install/url/searchParams.js +36 -25
  62. package/lib/dataflow/propagation/install/url/url.js +3 -2
  63. package/lib/dataflow/propagation/install/util-format.js +4 -3
  64. package/lib/dataflow/propagation/install/validator/hooks.js +0 -1
  65. package/lib/dataflow/sinks/install/eval.js +3 -1
  66. package/lib/dataflow/sinks/install/function.js +3 -4
  67. package/lib/dataflow/sinks/install/marsdb.js +3 -1
  68. package/lib/dataflow/sinks/install/mongodb.js +3 -1
  69. package/lib/dataflow/sinks/install/mssql.js +4 -3
  70. package/lib/dataflow/sinks/install/mysql.js +3 -1
  71. package/lib/dataflow/sinks/install/restify.js +3 -1
  72. package/lib/dataflow/sinks/install/sqlite3.js +4 -2
  73. package/lib/dataflow/sinks/install/vm.js +6 -4
  74. package/lib/dataflow/sources/handler.js +2 -3
  75. package/lib/dataflow/sources/install/fastify/fastify.js +4 -4
  76. package/lib/dataflow/tag-utils.js +15 -1
  77. package/lib/dataflow/tracker.js +0 -5
  78. package/lib/event-factory.js +1 -1
  79. package/lib/session-configuration/install/express-session.js +0 -1
  80. package/lib/session-configuration/install/fastify-cookie.js +0 -3
  81. package/lib/session-configuration/install/hapi.js +0 -1
  82. package/lib/session-configuration/install/koa.js +0 -3
  83. package/package.json +10 -10
@@ -23,7 +23,8 @@ const {
23
23
  StringPrototypeReplace,
24
24
  StringPrototypeReplaceAll,
25
25
  StringPrototypeSubstring
26
- }
26
+ },
27
+ isString,
27
28
  } = require('@contrast/common');
28
29
  const {
29
30
  createSubsetTags,
@@ -43,170 +44,176 @@ module.exports = function(core) {
43
44
  },
44
45
  } = core;
45
46
 
46
- function parseArgs(args) {
47
- //
48
- // convert replace args (match, p1, p2, ..., matchIdx, str, ?groups) to
49
- // re.exec(string) format [match, g1, g2, ..., index: N, input: '', groups?]
50
- //
51
- const r = [];
52
- let ix = -1;
53
- if (typeof args.at(-1) === 'object') {
54
- r.groups = args.at(-1);
55
- ix = -2;
56
- }
57
- r.input = args.at(ix);
58
- ix -= 1;
59
- r.index = args.at(ix);
47
+ const RE_REPS = /\$(\$|&|`|'|[1-9][0-9]?|<[a-zA-Z0-9_]+>)/g;
48
+ const STR_REPS = /\$(\$|&|`|')/g;
60
49
 
61
- // ix is negative from the end, so add it
62
- ix = args.length + ix;
63
- for (let i = 0; i < ix; i++) {
64
- r.push(args[i]);
65
- }
50
+ const propagator = core.assess.dataflow.propagation.stringInstrumentation.replace = {
51
+ //
52
+ parseArgs(args) {
53
+ //
54
+ // convert replace args (match, p1, p2, ..., matchIdx, str, ?groups) to
55
+ // re.exec(string) format [match, g1, g2, ..., index: N, input: '', groups?]
56
+ //
57
+ const r = [];
58
+ let ix = -1;
59
+ if (typeof args.at(-1) === 'object') {
60
+ r.groups = args.at(-1);
61
+ ix = -2;
62
+ }
63
+ r.input = args.at(ix);
64
+ ix -= 1;
65
+ r.index = args.at(ix);
66
66
 
67
- return r;
68
- }
67
+ // ix is negative from the end, so add it
68
+ ix = args.length + ix;
69
+ for (let i = 0; i < ix; i++) {
70
+ r.push(args[i]);
71
+ }
69
72
 
70
- const RE_REPS = /\$(\$|&|`|'|[1-9][0-9]?|<[a-zA-Z0-9_]+>)/g;
71
- const STR_REPS = /\$(\$|&|`|')/g;
73
+ return r;
74
+ },
75
+ //
76
+ getReplacementInfo(data, replacerArgs, parsedArgs) {
77
+ // patternIsRE is two different flags, both booleans.
78
+ // - set true if the first argument to String.prototype.replace is an RE.
79
+ // - after the $N, $<name> checks, it flags that a substitution was made.
80
+ const patternIsRE = data.args[0] instanceof RegExp;
81
+ let replacement;
82
+ if (typeof data._replacement === 'function') {
83
+ // no special replacements apply to the string returned by a replacement
84
+ // function.
85
+ replacement = data._replacement.call(global, ...replacerArgs);
86
+ } else {
87
+ // if it's not a function then the valid special replacements depend on
88
+ // whether the pattern is a regex or a string. first find substitution
89
+ // patterns present in the replacement string. Don't find patterns that
90
+ // aren't valid, e.g., $0, $<name> when the pattern is a string.
91
+ replacement = String(data._replacement);
72
92
 
73
- function getReplacementInfo(data, replacerArgs, parsedArgs) {
74
- // patternIsRE is two different flags, both booleans.
75
- // - set true if the first argument to String.prototype.replace is an RE.
76
- // - after the $N, $<name> checks, it flags that a substitution was made.
77
- const patternIsRE = data.args[0] instanceof RegExp;
78
- let replacement;
79
- if (typeof data._replacement === 'function') {
80
- // no special replacements apply to the string returned by a replacement
81
- // function.
82
- replacement = data._replacement.call(global, ...replacerArgs);
83
- } else {
84
- // if it's not a function then the valid special replacements depend on
85
- // whether the pattern is a regex or a string. first find substitution
86
- // patterns present in the replacement string. Don't find patterns that
87
- // aren't valid, e.g., $0, $<name> when the pattern is a string.
88
- replacement = String(data._replacement);
93
+ const matches = StringPrototypeMatchAll.call(replacement, patternIsRE ? RE_REPS : STR_REPS);
89
94
 
90
- const matches = StringPrototypeMatchAll.call(replacement, patternIsRE ? RE_REPS : STR_REPS);
95
+ for (const m of matches) {
96
+ let substitution;
97
+ let substitutionDone = false;
98
+ // format of m: ['$`', '`', index: 0, input: string, groups: undefined|{}]
99
+ // if the pattern is a regex, then $1 to $99 and $<name> are valid
100
+ if (patternIsRE) {
101
+ // my guess is $1 to $99 are most likely, after that, who knows? so
102
+ // we check named groups next, because 1) they seem more useful than
103
+ // the other $ patterns and 2) they are in the same patternIsRE test.
104
+ //
105
+ // in any case, the following will be false if m[1][0] is not a number.
106
+ if (m[1][0] >= 1 && m[1][0] <= 9) {
107
+ // a group might not be present, e.g., (pattern)?(a) or (a)|(b).
108
+ if (parsedArgs[m[1]] === undefined) {
109
+ if (m[1] in parsedArgs) {
110
+ // need to remove $m[1] from replacement pattern. N.B. this could
111
+ // mess up if the replacement text contains text that matches a
112
+ // subsequent replacement (either RE or string).
113
+ replacement = StringPrototypeReplaceAll.call(replacement, m[0], '');
114
+ } else {
115
+ // no capture groups in RegExp
116
+ substitution = replacement;
117
+ }
91
118
 
92
- for (const m of matches) {
93
- let substitution;
94
- let substitutionDone = false;
95
- // format of m: ['$`', '`', index: 0, input: string, groups: undefined|{}]
96
- // if the pattern is a regex, then $1 to $99 and $<name> are valid
97
- if (patternIsRE) {
98
- // my guess is $1 to $99 are most likely, after that, who knows? so
99
- // we check named groups next, because 1) they seem more useful than
100
- // the other $ patterns and 2) they are in the same patternIsRE test.
101
- //
102
- // in any case, the following will be false if m[1][0] is not a number.
103
- if (m[1][0] >= 1 && m[1][0] <= 9) {
104
- //const group = Number(m[1]);
105
- // a group might not be present, e.g., (pattern)?(a) or (a)|(b).
106
- if (parsedArgs[m[1]] === undefined && m[1] in parsedArgs) {
107
- // need to remove $m[1] from replacement pattern. N.B. this could
108
- // mess up if the replacement text contains text that matches a
109
- // subsequent replacement (either RE or string).
110
- replacement = StringPrototypeReplaceAll.call(replacement, m[0], '');
111
- continue;
119
+ continue;
120
+ }
121
+ substitution = parsedArgs[m[1]];
122
+ substitutionDone = true;
123
+ } else if (m[1][0] === '<') {
124
+ // named group
125
+ const groupName = StringPrototypeSubstring.call(m[1], 1, m[1].length - 1);
126
+ if (parsedArgs.groups[groupName] === undefined && groupName in parsedArgs.groups) {
127
+ // remove $<groupName> from the replacement pattern. N.B. this
128
+ // also could mess up if the replacement text containts text that
129
+ // matches a subsequent replacement.
130
+ replacement = StringPrototypeReplaceAll.call(replacement, m[0], '');
131
+ continue;
132
+ }
133
+ substitution = parsedArgs.groups[groupName];
134
+ substitutionDone = true;
112
135
  }
113
- substitution = parsedArgs[m[1]];
114
- substitutionDone = true;
115
- } else if (m[1][0] === '<') {
116
- // named group
117
- const groupName = StringPrototypeSubstring.call(m[1], 1, m[1].length - 1);
118
- if (parsedArgs.groups[groupName] === undefined && groupName in parsedArgs.groups) {
119
- // remove $<groupName> from the replacement pattern. N.B. this
120
- // also could mess up if the replacement text containts text that
121
- // matches a subsequent replacement.
122
- replacement = StringPrototypeReplaceAll.call(replacement, m[0], '');
123
- continue;
124
- }
125
- substitution = parsedArgs.groups[groupName];
126
- substitutionDone = true;
127
136
  }
128
- }
129
137
 
130
- // the following are valid whether the pattern is a regex or a string.
131
- //
132
- // any idea what order $&'` should be in from a most-common to least-
133
- // common perspective? nfi.
134
- let substitutionTags;
135
- if (!substitutionDone) {
136
- if (m[1] === '$') {
137
- // this could actually be tracked but in order to do this "right"
138
- // we have to be able to distinguish whether it is the first or
139
- // the second $ that is tracked. so punt, and just ignore it, as
140
- // originally implemented.
141
- substitution = '$';
142
- data._replacementOffset -= 1;
143
- } else if (m[1] === '&') {
144
- // replace these '$&' in parsedArgs[0] (replacerArgs[0], i.e., the
145
- // match). if tracked, handle it. do so by index, so we don't have to
146
- // call replace again? e.g., string[m.index..m.index+2]
147
- substitution = parsedArgs[0];
148
- } else if (m[1] === '`') {
149
- const info = tracker.getData(parsedArgs.input);
150
- substitution = StringPrototypeSubstring.call(parsedArgs.input, 0, parsedArgs.index);
151
- substitutionTags = createSubsetTags(info.tags, parsedArgs.index, substitution.length);
152
- } else if (m[1] === "'") {
153
- const info = tracker.getData(parsedArgs.input);
154
- substitution = StringPrototypeSubstring.call(parsedArgs.input, parsedArgs.index + parsedArgs[0].length);
155
- substitutionTags = createSubsetTags(info.tags, parsedArgs.index, substitution.length);
156
- } // else {
157
- // throw new Error('how can it have matched RE and gotten here?');
158
- // }
159
- // i'm not sure what the proper handling of this is. if either char
160
- // of the $$&`' sequence is tracked, then something the user input
161
- // is manipulating the output, even if it is just duplicating what
162
- // might be tracked or untracked input. does that count?
163
- }
164
- replacement = StringPrototypeReplace.call(replacement, m[0], substitution);
165
- if (!substitutionTags && tracker.getData(substitution)) substitutionTags = tracker.getData(substitution)?.tags;
166
- if (substitutionTags) {
167
- data._replacementTags = createAppendTags(data._replacementTags || {}, substitutionTags, m.index + data._replacementOffset);
168
- data._replacementOffset += (substitution.length - m[0].length);
138
+ // the following are valid whether the pattern is a regex or a string.
139
+ //
140
+ // any idea what order $&'` should be in from a most-common to least-
141
+ // common perspective? nfi.
142
+ let substitutionTags;
143
+ if (!substitutionDone) {
144
+ if (m[1] === '$') {
145
+ // this could actually be tracked but in order to do this "right"
146
+ // we have to be able to distinguish whether it is the first or
147
+ // the second $ that is tracked. so punt, and just ignore it, as
148
+ // originally implemented.
149
+ substitution = '$';
150
+ data._replacementOffset -= 1;
151
+ } else if (m[1] === '&') {
152
+ // replace these '$&' in parsedArgs[0] (replacerArgs[0], i.e., the
153
+ // match). if tracked, handle it. do so by index, so we don't have to
154
+ // call replace again? e.g., string[m.index..m.index+2]
155
+ substitution = parsedArgs[0];
156
+ } else if (m[1] === '`') {
157
+ const info = tracker.getData(parsedArgs.input);
158
+ substitution = StringPrototypeSubstring.call(parsedArgs.input, 0, parsedArgs.index);
159
+ substitutionTags = createSubsetTags(info.tags, parsedArgs.index, substitution.length);
160
+ } else if (m[1] === "'") {
161
+ const info = tracker.getData(parsedArgs.input);
162
+ substitution = StringPrototypeSubstring.call(parsedArgs.input, parsedArgs.index + parsedArgs[0].length);
163
+ substitutionTags = createSubsetTags(info.tags, parsedArgs.index, substitution.length);
164
+ } // else {
165
+ // throw new Error('how can it have matched RE and gotten here?');
166
+ // }
167
+ // i'm not sure what the proper handling of this is. if either char
168
+ // of the $$&`' sequence is tracked, then something the user input
169
+ // is manipulating the output, even if it is just duplicating what
170
+ // might be tracked or untracked input. does that count?
171
+ }
172
+ replacement = StringPrototypeReplace.call(replacement, m[0], substitution);
173
+ if (!substitutionTags && tracker.getData(substitution)) substitutionTags = tracker.getData(substitution)?.tags;
174
+ if (substitutionTags) {
175
+ data._replacementTags = createAppendTags(data._replacementTags || {}, substitutionTags, m.index + data._replacementOffset);
176
+ data._replacementOffset += (substitution.length - m[0].length);
177
+ }
169
178
  }
170
179
  }
171
- }
172
180
 
173
- // coerce the replacement to a string, e.g., null => 'null'
174
- replacement = String(replacement);
175
-
176
- data._replacementInfo = tracker.getData(replacement);
177
- if (data._replacementInfo) {
178
- data._history.add(data._replacementInfo);
179
- }
181
+ // coerce the replacement to a string, e.g., null => 'null'
182
+ replacement = String(replacement);
180
183
 
181
- return { replacement, replacementTags: data._replacementTags || data._replacementInfo?.tags };
182
- }
184
+ data._replacementInfo = tracker.getData(replacement);
185
+ if (data._replacementInfo) {
186
+ data._history.add(data._replacementInfo);
187
+ }
183
188
 
184
- function getReplacer(data) {
185
- return function replacer(...args) {
186
- const parsedArgs = parseArgs(args);
187
- const match = parsedArgs[0];
188
- const { index, input } = parsedArgs;
189
+ return { replacement, replacementTags: data._replacementTags || data._replacementInfo?.tags };
190
+ },
191
+ //
192
+ getReplacer(data) {
193
+ return function replacer(...args) {
194
+ const parsedArgs = propagator.parseArgs(args);
195
+ const match = parsedArgs[0];
196
+ const { index, input } = parsedArgs;
189
197
 
190
- const { _accumOffset, _accumTags } = data;
191
- const { replacement, replacementTags } = getReplacementInfo(data, args, parsedArgs);
198
+ const { _accumOffset, _accumTags } = data;
199
+ const { replacement, replacementTags } = propagator.getReplacementInfo(data, args, parsedArgs);
192
200
 
193
- const preTags = createSubsetTags(_accumTags, 0, _accumOffset + index);
194
- const postTags = createSubsetTags(_accumTags, _accumOffset + index + match.length, input.length - index - match.length);
195
- data._accumOffset += (replacement.length - match.length);
196
- if (preTags || postTags || replacementTags) {
197
- data._accumTags = createAppendTags(
198
- createAppendTags(preTags, replacementTags, _accumOffset + index),
199
- postTags,
200
- data._accumOffset + index + match.length
201
- );
202
- } else {
203
- data._accumTags = {};
204
- }
205
- return replacement;
206
- };
207
- }
201
+ const preTags = createSubsetTags(_accumTags, 0, _accumOffset + index);
202
+ const postTags = createSubsetTags(_accumTags, _accumOffset + index + match.length, input.length - index - match.length);
203
+ data._accumOffset += (replacement.length - match.length);
204
+ if (preTags || postTags || replacementTags) {
205
+ data._accumTags = createAppendTags(
206
+ createAppendTags(preTags, replacementTags, _accumOffset + index),
207
+ postTags,
208
+ data._accumOffset + index + match.length
209
+ );
210
+ } else {
211
+ data._accumTags = {};
212
+ }
213
+ return replacement;
214
+ };
215
+ },
208
216
 
209
- return core.assess.dataflow.propagation.stringInstrumentation.replace = {
210
217
  install() {
211
218
  const name = 'String.prototype.replace';
212
219
  const store = { name, lock: true };
@@ -215,20 +222,27 @@ module.exports = function(core) {
215
222
  patchType,
216
223
  usePerf: 'sync',
217
224
  around(next, data) {
218
- if (!getPropagatorContext()) return next();
225
+ const _next = !scopes.instrumentation.isLocked() ? () => scopes.instrumentation.run(store, next) : next;
226
+
227
+ if (!getPropagatorContext()) return _next();
219
228
 
220
229
  // setup state
221
230
  data._objInfo = tracker.getData(data.obj);
222
231
  data._replacement = data.args[1];
223
- data._replacementType = typeof data._replacement;
224
232
  data._history = data._objInfo ? new Set([data._objInfo]) : new Set();
225
233
  data._accumTags = data._objInfo?.tags || {};
226
234
  data._accumOffset = 0;
227
235
  data._replacementOffset = 0;
228
236
 
229
- data.args[1] = getReplacer(data);
237
+ // bail early if no constituents are tracked
238
+ if (
239
+ !data._objInfo &&
240
+ isString(data.args[1]) &&
241
+ !tracker.getData(data.args[1])
242
+ ) return _next();
230
243
 
231
- const result = !scopes.instrumentation.isLocked() ? scopes.instrumentation.run(store, next) : next();
244
+ data.args[1] = propagator.getReplacer(data);
245
+ const result = _next();
232
246
 
233
247
  if (
234
248
  !result ||
@@ -237,7 +251,7 @@ module.exports = function(core) {
237
251
  data.obj === result
238
252
  ) return result;
239
253
 
240
- const { obj, args: origArgs, hooked, orig } = data;
254
+ const { obj, args: origArgs, hooked } = data;
241
255
  const args = [];
242
256
  if (tracker.getData(origArgs[0])) {
243
257
  args.push({ tracked: true, value: origArgs[0] });
@@ -254,7 +268,9 @@ module.exports = function(core) {
254
268
  name,
255
269
  moduleName: 'String',
256
270
  methodName: 'prototype.replace',
257
- context: `'${obj}'.replace(${ArrayPrototypeJoin.call(args.map(a => a.value))})`,
271
+ get context() {
272
+ return `'${obj}'.replace(${ArrayPrototypeJoin.call(args.map(a => a.value))})`;
273
+ },
258
274
  history: Array.from(data._history),
259
275
  object: {
260
276
  value: obj,
@@ -268,16 +284,13 @@ module.exports = function(core) {
268
284
  tags: data._accumTags,
269
285
  stacktraceOpts: {
270
286
  constructorOpt: hooked,
271
- prependFrames: [orig]
272
287
  },
273
288
  source: data._objInfo ? (data._history.size > 1 ? 'A' : 'O') : 'P',
274
289
  target: 'R',
275
290
  });
276
-
277
- if (!event) return;
291
+ if (!event) return null;
278
292
 
279
293
  const { extern } = tracker.track(result, event);
280
-
281
294
  return extern;
282
295
  }
283
296
  });
@@ -286,4 +299,6 @@ module.exports = function(core) {
286
299
  String.prototype.replace = patcher.unwrap(String.prototype.replace);
287
300
  }
288
301
  };
302
+
303
+ return propagator;
289
304
  };
@@ -55,7 +55,7 @@ module.exports = function(core) {
55
55
  patchType,
56
56
  usePerf: 'sync',
57
57
  post(data) {
58
- const { name, args: origArgs, obj, result, hooked, orig } = data;
58
+ const { name, args: origArgs, obj, result, hooked } = data;
59
59
  if (!result || !getPropagatorContext()) return;
60
60
 
61
61
  const objInfo = tracker.getData(obj);
@@ -80,7 +80,9 @@ module.exports = function(core) {
80
80
  name,
81
81
  moduleName: 'String',
82
82
  methodName: 'prototype.slice',
83
- context: `'${objInfo.value}'.slice(${ArrayPrototypeJoin.call(args.map(a => a.value), ', ')})`,
83
+ get context() {
84
+ return `'${objInfo.value}'.slice(${ArrayPrototypeJoin.call(args.map(a => a.value), ', ')})`;
85
+ },
84
86
  history: [objInfo],
85
87
  object: {
86
88
  value: obj,
@@ -96,7 +98,6 @@ module.exports = function(core) {
96
98
  target: 'R',
97
99
  stacktraceOpts: {
98
100
  constructorOpt: hooked,
99
- prependFrames: [orig]
100
101
  }
101
102
  });
102
103
 
@@ -16,7 +16,7 @@
16
16
  'use strict';
17
17
 
18
18
  const { primordials: { ArrayPrototypeJoin, RegExpPrototypeExec } } = require('@contrast/common');
19
- const { createSubsetTags, getAdjustedUntrackedValue } = require('../../../tag-utils');
19
+ const { createSubsetTags } = require('../../../tag-utils');
20
20
  const { patchType } = require('../../common');
21
21
 
22
22
  module.exports = function(core) {
@@ -46,7 +46,7 @@ module.exports = function(core) {
46
46
  next();
47
47
  },
48
48
  post(data) {
49
- const { name, args: origArgs, obj, result, hooked, orig } = data;
49
+ const { name, args: origArgs, obj, result, hooked } = data;
50
50
  const splitterIsRx = origArgs[0] instanceof RegExp;
51
51
 
52
52
  if (
@@ -81,7 +81,7 @@ module.exports = function(core) {
81
81
  if (tags) {
82
82
  const metadata = makeEvent({
83
83
  result: { tracked: true, value: result[i] },
84
- tags: tags,
84
+ tags,
85
85
  });
86
86
 
87
87
  if (metadata) {
@@ -115,7 +115,7 @@ module.exports = function(core) {
115
115
  if (tags) {
116
116
  const metadata = makeEvent({
117
117
  result: { tracked: true, value: result[i] },
118
- tags: tags,
118
+ tags,
119
119
  });
120
120
  eventFactory.createdEvents.add(metadata);
121
121
  const { extern } = tracker.track(result[i], metadata);
@@ -137,28 +137,28 @@ module.exports = function(core) {
137
137
  const args = origArgs.map((arg) => {
138
138
  const argInfo = tracker.getData(arg);
139
139
  return argInfo ?
140
- { tracked: true, value: argInfo.value } :
141
- { tracked: false, value: `'${arg}'` };
140
+ { tracked: true, value: argInfo.value } :
141
+ { tracked: false, value: splitterIsRx ? arg.toString() : `'${arg}'` };
142
142
  });
143
143
  _event = eventFactory.createPropagationEvent({
144
144
  name,
145
145
  moduleName: 'String',
146
146
  methodName: 'prototype.split',
147
- context: `'${objInfo.value}'.split(${ArrayPrototypeJoin.call(args.map(a => a.value))})`,
147
+ get context() {
148
+ return `'${objInfo.value}'.split(${ArrayPrototypeJoin.call(args.map(a => a.value))})`;
149
+ },
148
150
  history: [objInfo],
149
151
  object: {
150
152
  value: obj,
151
153
  tracked: true,
152
154
  },
153
155
  args,
154
- tags: {},
155
156
  result: {
156
- value: getAdjustedUntrackedValue(result),
157
- tracked: false
157
+ value: `${result}`,
158
+ tracked: true
158
159
  },
159
160
  stacktraceOpts: {
160
161
  constructorOpt: hooked,
161
- prependFrames: [orig]
162
162
  },
163
163
  source: 'O',
164
164
  target: 'R'
@@ -63,7 +63,7 @@ module.exports = function(core) {
63
63
  patchType,
64
64
  usePerf: 'sync',
65
65
  post(data) {
66
- const { obj, args: origArgs, result, name, hooked, orig } = data;
66
+ const { obj, args: origArgs, result, name, hooked } = data;
67
67
  if (!result || !getPropagatorContext()) return;
68
68
 
69
69
  const objInfo = tracker.getData(obj);
@@ -90,7 +90,9 @@ module.exports = function(core) {
90
90
  name,
91
91
  moduleName: 'String',
92
92
  methodName: 'prototype.substring',
93
- context: `'${objInfo.value}'.substring(${ArrayPrototypeJoin.call(args.map(a => a.value))})`,
93
+ get context() {
94
+ return `'${objInfo.value}'.substring(${ArrayPrototypeJoin.call(args.map(a => a.value))})`;
95
+ },
94
96
  history: [objInfo],
95
97
  object: {
96
98
  value: obj,
@@ -105,7 +107,6 @@ module.exports = function(core) {
105
107
  source: 'O',
106
108
  stacktraceOpts: {
107
109
  constructorOpt: hooked,
108
- prependFrames: [orig]
109
110
  },
110
111
  target: 'R',
111
112
  });
@@ -30,7 +30,7 @@ module.exports = function(core) {
30
30
 
31
31
  function createPostHook(methodName, presetStart) {
32
32
  return function(data) {
33
- const { obj, result, hooked, orig } = data;
33
+ const { obj, result, hooked } = data;
34
34
 
35
35
  if (!result?.length || !getPropagatorContext()) {
36
36
  return;
@@ -56,7 +56,9 @@ module.exports = function(core) {
56
56
  name: `String.prototype.${methodName}`,
57
57
  moduleName: 'String',
58
58
  methodName: `prototype.${methodName}`,
59
- context: `'${obj}'.${methodName}()`,
59
+ get context() {
60
+ return `'${obj}'.${methodName}()`;
61
+ },
60
62
  history,
61
63
  object: {
62
64
  value: obj,
@@ -69,7 +71,6 @@ module.exports = function(core) {
69
71
  tags: newTags,
70
72
  stacktraceOpts: {
71
73
  constructorOpt: hooked,
72
- prependFrames: [orig]
73
74
  },
74
75
  source: 'O',
75
76
  target: 'R'
@@ -17,7 +17,7 @@
17
17
 
18
18
  const { DataflowTag: { WEAK_URL_ENCODED } } = require('@contrast/common');
19
19
  const { createFullLengthCopyTags } = require('../../tag-utils');
20
- const { patchType, createObjectLabel } = require('../common');
20
+ const { patchType, globalObject: object } = require('../common');
21
21
 
22
22
  module.exports = function(core) {
23
23
  const {
@@ -38,29 +38,26 @@ module.exports = function(core) {
38
38
  patchType,
39
39
  usePerf: 'sync',
40
40
  post(data) {
41
- const { args, result, hooked, orig } = data;
41
+ const { args, result, hooked } = data;
42
42
  if (!result || !args[0] || !getPropagatorContext()) return;
43
43
 
44
44
  const argInfo = tracker.getData(args[0]);
45
-
46
45
  if (!argInfo) return;
47
46
 
48
47
  const resultInfo = tracker.getData(result);
49
48
  const history = [argInfo];
50
49
  const newTags = createFullLengthCopyTags(argInfo.tags, result.length);
51
50
  delete newTags[WEAK_URL_ENCODED];
52
-
53
51
  if (!Object.keys(newTags).length) return;
54
52
 
55
53
  const event = createPropagationEvent({
56
54
  name,
57
55
  moduleName: 'global',
58
56
  methodName: 'unescape',
59
- context: `unescape('${argInfo.value}')`,
60
- object: {
61
- value: createObjectLabel('global'),
62
- tracked: false
57
+ get context() {
58
+ return `unescape('${argInfo.value}')`;
63
59
  },
60
+ object,
64
61
  result: {
65
62
  value: resultInfo ? resultInfo.value : result,
66
63
  tracked: true
@@ -73,18 +70,13 @@ module.exports = function(core) {
73
70
  removedTags: [WEAK_URL_ENCODED],
74
71
  stacktraceOpts: {
75
72
  constructorOpt: hooked,
76
- prependFrames: [orig]
77
73
  },
78
74
  });
79
-
80
75
  if (!event) return;
81
76
 
82
- if (resultInfo) {
83
- Object.assign(resultInfo, event);
84
- }
77
+ if (resultInfo) Object.assign(resultInfo, event);
85
78
 
86
79
  const { extern } = tracker.track(result, event);
87
-
88
80
  if (extern) {
89
81
  data.result = extern;
90
82
  }