@contrast/assess 1.8.0 → 1.9.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 (62) hide show
  1. package/lib/dataflow/event-factory.js +17 -13
  2. package/lib/dataflow/propagation/index.js +3 -0
  3. package/lib/dataflow/propagation/install/JSON/index.js +1 -0
  4. package/lib/dataflow/propagation/install/JSON/parse-fn.js +248 -0
  5. package/lib/dataflow/propagation/install/JSON/parse.js +196 -0
  6. package/lib/dataflow/propagation/install/JSON/stringify.js +5 -3
  7. package/lib/dataflow/propagation/install/array-prototype-join.js +21 -14
  8. package/lib/dataflow/propagation/install/buffer.js +2 -0
  9. package/lib/dataflow/propagation/install/contrast-methods/add.js +2 -0
  10. package/lib/dataflow/propagation/install/contrast-methods/index.js +1 -0
  11. package/lib/dataflow/propagation/install/contrast-methods/number.js +58 -0
  12. package/lib/dataflow/propagation/install/contrast-methods/string.js +53 -6
  13. package/lib/dataflow/propagation/install/contrast-methods/tag.js +3 -1
  14. package/lib/dataflow/propagation/install/decode-uri-component.js +9 -2
  15. package/lib/dataflow/propagation/install/ejs/escape-xml.js +8 -2
  16. package/lib/dataflow/propagation/install/encode-uri-component.js +9 -2
  17. package/lib/dataflow/propagation/install/escape-html.js +13 -5
  18. package/lib/dataflow/propagation/install/escape.js +9 -2
  19. package/lib/dataflow/propagation/install/handlebars-utils-escape-expression.js +11 -4
  20. package/lib/dataflow/propagation/install/isnumeric-0.js +59 -0
  21. package/lib/dataflow/propagation/install/mongoose/index.js +36 -0
  22. package/lib/dataflow/propagation/install/mongoose/schema-string.js +156 -0
  23. package/lib/dataflow/propagation/install/mysql-connection-escape.js +5 -0
  24. package/lib/dataflow/propagation/install/parse-int.js +60 -0
  25. package/lib/dataflow/propagation/install/pug-runtime-escape.js +9 -2
  26. package/lib/dataflow/propagation/install/querystring/parse.js +11 -9
  27. package/lib/dataflow/propagation/install/sequelize.js +6 -3
  28. package/lib/dataflow/propagation/install/sql-template-strings.js +9 -2
  29. package/lib/dataflow/propagation/install/string/concat.js +8 -2
  30. package/lib/dataflow/propagation/install/string/format-methods.js +7 -2
  31. package/lib/dataflow/propagation/install/string/html-methods.js +15 -5
  32. package/lib/dataflow/propagation/install/string/match.js +14 -9
  33. package/lib/dataflow/propagation/install/string/replace.js +22 -14
  34. package/lib/dataflow/propagation/install/string/slice.js +13 -5
  35. package/lib/dataflow/propagation/install/string/split.js +15 -11
  36. package/lib/dataflow/propagation/install/string/substring.js +16 -6
  37. package/lib/dataflow/propagation/install/string/trim.js +3 -0
  38. package/lib/dataflow/propagation/install/unescape.js +9 -2
  39. package/lib/dataflow/propagation/install/url/domain-parsers.js +7 -2
  40. package/lib/dataflow/propagation/install/validator/hooks.js +6 -2
  41. package/lib/dataflow/sinks/install/child-process.js +116 -50
  42. package/lib/dataflow/sinks/install/express/unvalidated-redirect.js +6 -3
  43. package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js +7 -4
  44. package/lib/dataflow/sinks/install/fs.js +44 -12
  45. package/lib/dataflow/sinks/install/http.js +5 -2
  46. package/lib/dataflow/sinks/install/koa/unvalidated-redirect.js +7 -4
  47. package/lib/dataflow/sinks/install/marsdb.js +3 -0
  48. package/lib/dataflow/sinks/install/mongodb.js +3 -1
  49. package/lib/dataflow/sinks/install/mssql.js +9 -2
  50. package/lib/dataflow/sinks/install/mysql.js +9 -4
  51. package/lib/dataflow/sinks/install/postgres.js +6 -3
  52. package/lib/dataflow/sinks/install/sequelize.js +7 -5
  53. package/lib/dataflow/sinks/install/sqlite3.js +7 -3
  54. package/lib/dataflow/sources/handler.js +2 -1
  55. package/lib/dataflow/sources/install/http.js +1 -1
  56. package/lib/dataflow/tag-utils.js +25 -1
  57. package/lib/dataflow/tracker.js +6 -6
  58. package/lib/index.js +2 -0
  59. package/lib/response-scanning/handlers/utils.js +2 -2
  60. package/lib/session-configuration/index.js +34 -0
  61. package/lib/session-configuration/install/http.js +79 -0
  62. package/package.json +2 -2
@@ -15,8 +15,7 @@
15
15
 
16
16
  'use strict';
17
17
 
18
-
19
- const { InputType, signatures } = require('@contrast/common');
18
+ const { InputType, match } = require('@contrast/common');
20
19
  const annotationRegExp = /^(A|O|R|P|P\d+)$/;
21
20
 
22
21
  module.exports = function(core) {
@@ -48,7 +47,7 @@ module.exports = function(core) {
48
47
  }
49
48
 
50
49
  if (!name) {
51
- logger.debug({ name }, baseMessage, 'invalid name');
50
+ logger.debug({ data }, baseMessage, 'invalid name');
52
51
  return null;
53
52
  }
54
53
 
@@ -77,6 +76,8 @@ module.exports = function(core) {
77
76
  eventFactory.createPropagationEvent = function(data) {
78
77
  const {
79
78
  name = '',
79
+ moduleName,
80
+ methodName,
80
81
  history = [],
81
82
  object = { value: null, tracked: false },
82
83
  args = [],
@@ -102,10 +103,8 @@ module.exports = function(core) {
102
103
  return null;
103
104
  }
104
105
 
105
- const signature = signatures.get(name);
106
-
107
- if (!signature) {
108
- logger.debug({ name }, 'Propagation event not created: no signature found for this name');
106
+ if (!name) {
107
+ logger.debug({ name }, 'Propagation event not created: invalid name');
109
108
  return null;
110
109
  }
111
110
 
@@ -115,8 +114,8 @@ module.exports = function(core) {
115
114
  }
116
115
 
117
116
  if (
118
- ((source && !source.match(annotationRegExp)) || (!source && !signature.source)) ||
119
- ((target && !target.match(annotationRegExp)) || (!target && !signature.target))
117
+ (!source || !match(source, annotationRegExp)) ||
118
+ (!target || !match(target, annotationRegExp))
120
119
  ) {
121
120
  logger.debug({ name, source, target }, 'Propagation event not created: %s', 'invalid source/target');
122
121
  return null;
@@ -135,6 +134,8 @@ module.exports = function(core) {
135
134
  context,
136
135
  history,
137
136
  name,
137
+ moduleName,
138
+ methodName,
138
139
  object,
139
140
  removedTags,
140
141
  result,
@@ -155,6 +156,8 @@ module.exports = function(core) {
155
156
  const {
156
157
  context,
157
158
  name = '',
159
+ moduleName,
160
+ methodName,
158
161
  history = [],
159
162
  object = { value: null, tracked: false },
160
163
  args = [],
@@ -169,9 +172,8 @@ module.exports = function(core) {
169
172
  logger.debug('no sourceContext found during sink event creation');
170
173
  return null;
171
174
  }
172
- const signature = signatures.get(name);
173
- if (!signature) {
174
- logger.debug({ name }, 'no signature found for sink event name');
175
+ if (!name) {
176
+ logger.debug({ data }, 'no sink event name');
175
177
  return null;
176
178
  }
177
179
  if (!history.length) {
@@ -179,7 +181,7 @@ module.exports = function(core) {
179
181
  return null;
180
182
  }
181
183
  if (
182
- ((source && !source.match(annotationRegExp)) || (!source && !signature.source))
184
+ (!source || !source.match(annotationRegExp))
183
185
  ) {
184
186
  logger.debug({ data }, 'malformed or missing sink event source field');
185
187
  return null;
@@ -197,6 +199,8 @@ module.exports = function(core) {
197
199
  context,
198
200
  history,
199
201
  name,
202
+ moduleName,
203
+ methodName,
200
204
  object,
201
205
  result,
202
206
  source,
@@ -22,6 +22,7 @@ module.exports = function(core) {
22
22
 
23
23
  require('./install/contrast-methods')(core);
24
24
  require('./install/ejs')(core);
25
+ require('./install/mongoose')(core);
25
26
  require('./install/pug')(core);
26
27
  require('./install/string')(core);
27
28
  require('./install/url')(core);
@@ -33,7 +34,9 @@ module.exports = function(core) {
33
34
  require('./install/escape-html')(core);
34
35
  require('./install/escape')(core);
35
36
  require('./install/handlebars-utils-escape-expression')(core);
37
+ require('./install/isnumeric-0')(core);
36
38
  require('./install/mysql-connection-escape')(core);
39
+ require('./install/parse-int')(core);
37
40
  require('./install/pug-runtime-escape')(core);
38
41
  require('./install/sql-template-strings')(core);
39
42
  require('./install/unescape')(core);
@@ -28,6 +28,7 @@ module.exports = function(core) {
28
28
  };
29
29
 
30
30
  require('./stringify')(core);
31
+ require('./parse')(core);
31
32
 
32
33
  return jsonInstrumentation;
33
34
  };
@@ -0,0 +1,248 @@
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
+ 'use strict';
16
+ const { trim } = require('@contrast/common');
17
+
18
+ function isNumber(value) {
19
+ return !isNaN(value);
20
+ }
21
+
22
+ function array(input, index, accumulator) {
23
+ const startIndex = index - 1;
24
+ const valueIndexes = [];
25
+
26
+ while (index < input.length) {
27
+ const cv = input[index];
28
+ if (cv === ']') {
29
+ index += 1;
30
+ break;
31
+ }
32
+
33
+ if (trim(cv) === '' || (valueIndexes.length > 0 && cv === ',')) {
34
+ index += 1;
35
+ continue;
36
+ }
37
+
38
+ const {
39
+ startIndex,
40
+ endIndex,
41
+ index: nextIndex,
42
+ } = getValueIndexes(input, index, accumulator);
43
+
44
+ valueIndexes.push([startIndex, endIndex]);
45
+ accumulator.push({ key: [null, null], value: [startIndex, endIndex] });
46
+
47
+ index = nextIndex;
48
+ }
49
+
50
+ return {
51
+ startIndex,
52
+ endIndex: index - 1,
53
+ index,
54
+ accumulator,
55
+ };
56
+ }
57
+
58
+ function string(input, index) {
59
+ const startIndex = index;
60
+
61
+ while (index < input.length) {
62
+ const cv = input[index];
63
+ if (cv === '\\') {
64
+ index += 2;
65
+ continue;
66
+ }
67
+
68
+ if (input[index] !== '"') {
69
+ index += 1;
70
+ continue;
71
+ }
72
+
73
+ break;
74
+ }
75
+
76
+ if (startIndex === index) {
77
+ return { startIndex: null, endIndex: null, index: index + 1 };
78
+ }
79
+
80
+ return { startIndex, endIndex: index - 1, index: index + 1 };
81
+ }
82
+
83
+ function bool(input, index = 0) {
84
+ const trueLength = 'true'.length;
85
+ const falseLength = 'false'.length;
86
+ let endIndex = 0;
87
+
88
+ if (input[index] === 't') {
89
+ endIndex = index + trueLength - 1;
90
+ } else {
91
+ endIndex = index + falseLength - 1;
92
+ }
93
+
94
+ return { startIndex: index, endIndex, index: endIndex + 1 };
95
+ }
96
+
97
+ function nully(index = 0) {
98
+ const nullLength = 'null'.length;
99
+ const endIndex = index + nullLength - 1;
100
+ return { startIndex: index, endIndex, index: endIndex + 1 };
101
+ }
102
+
103
+ function number(input, index) {
104
+ const startIndex = index;
105
+
106
+ if (input[startIndex] === '-') {
107
+ index += 1;
108
+ }
109
+
110
+ while (index < input.length) {
111
+ if (isNumber(input[index]) || input[index] === '.') {
112
+ index += 1;
113
+ continue;
114
+ }
115
+
116
+ break;
117
+ }
118
+
119
+ return { startIndex, endIndex: index - 1, index };
120
+ }
121
+
122
+ function object(value, index, accumulator) {
123
+ const startIndex = index - 1;
124
+ const keyIndexes = [];
125
+ const valueIndexes = [];
126
+
127
+ while (index < value.length) {
128
+ const cv = value[index];
129
+ const keyIndexesLength = keyIndexes.length;
130
+ const valueIndexesLength = valueIndexes.length;
131
+ const areKeysEqualToValues = keyIndexesLength - valueIndexesLength === 0;
132
+
133
+ if (cv === '}' && areKeysEqualToValues) {
134
+ break;
135
+ }
136
+
137
+ if (
138
+ trim(cv) === '' ||
139
+ (cv === ':' && keyIndexesLength > 0) ||
140
+ (cv === ',' && areKeysEqualToValues)
141
+ ) {
142
+ index += 1;
143
+ continue;
144
+ }
145
+
146
+ // Object key
147
+ if (cv === '"' && areKeysEqualToValues) {
148
+ index += 1;
149
+ const { startIndex, endIndex, index: nextIndex } = string(value, index);
150
+ keyIndexes.push([startIndex, endIndex]);
151
+ index = nextIndex;
152
+
153
+ continue;
154
+ }
155
+
156
+ const {
157
+ startIndex,
158
+ endIndex,
159
+ index: nextIndex,
160
+ } = getValueIndexes(value, index, accumulator);
161
+
162
+ valueIndexes.push([startIndex, endIndex]);
163
+ accumulator.push({
164
+ key: keyIndexes[keyIndexes.length - 1],
165
+ value: [startIndex, endIndex],
166
+ });
167
+
168
+ index = nextIndex;
169
+ }
170
+
171
+ return {
172
+ accumulator,
173
+ startIndex,
174
+ endIndex: index,
175
+ index: index + 1,
176
+ };
177
+ }
178
+
179
+ function getValueIndexes(value, index, accumulator) {
180
+ const cv = value[index];
181
+
182
+ switch (cv) {
183
+ case '{':
184
+ return object(value, index + 1, accumulator);
185
+ case '"':
186
+ return string(value, index + 1);
187
+ case '[':
188
+ return array(value, index + 1, accumulator);
189
+ case 't':
190
+ case 'f':
191
+ return bool(value, index);
192
+ case 'n':
193
+ return nully(index);
194
+ default:
195
+ if (cv === '-' || (cv && cv >= 0 && cv <= 9)) {
196
+ return number(value, index);
197
+ } else {
198
+ throw new Error('bad JSON');
199
+ }
200
+ }
201
+ }
202
+
203
+ function processInput(input) {
204
+ const firstElement = input[0];
205
+ const lastElement = input[input.length - 1];
206
+ const accumulator = [];
207
+
208
+ if (firstElement === '{' && lastElement === '}') {
209
+ return object(input, 1, accumulator);
210
+ }
211
+
212
+ if (firstElement === '[' && lastElement === ']') {
213
+ return array(input, 1, accumulator);
214
+ }
215
+
216
+ if (isNumber(input)) {
217
+ return number(input, 0);
218
+ }
219
+
220
+ if (firstElement === '"') {
221
+ return string(input, 1);
222
+ }
223
+
224
+ switch (input) {
225
+ case 'true':
226
+ case 'false':
227
+ return bool(input);
228
+ case 'null':
229
+ return nully();
230
+ default:
231
+ throw new Error('bad JSON');
232
+ }
233
+ }
234
+
235
+ function wrapEndResult({ accumulator, startIndex, endIndex }) {
236
+ accumulator = accumulator || [];
237
+ accumulator.push({ key: '', value: [startIndex, endIndex] });
238
+
239
+ return accumulator;
240
+ }
241
+
242
+ function getKeyValueIndexes(input) {
243
+ return wrapEndResult(processInput(input));
244
+ }
245
+
246
+ module.exports = {
247
+ getKeyValueIndexes,
248
+ };
@@ -0,0 +1,196 @@
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 { isString, inspect } = require('@contrast/common');
19
+ const { patchType } = require('../../common');
20
+ const { getKeyValueIndexes } = require('./parse-fn');
21
+ const {
22
+ createOverlappingTags
23
+ } = require('../../../tag-utils');
24
+
25
+ /*
26
+ When we return a string as a result of a reviver call
27
+ we loose tracking. We keep the values in a stack and when
28
+ the reviver gets called with an object - we apply the values
29
+ */
30
+ const applyValuesToObject = (object, stack) => {
31
+ let i = 0;
32
+
33
+ if (Array.isArray(object)) {
34
+ i = object.length;
35
+ } else {
36
+ i = Object.keys(object).length;
37
+ }
38
+
39
+ while (i > 0) {
40
+ const { key, value } = stack.pop();
41
+ if (object[key]) {
42
+ object[key] = value;
43
+ }
44
+
45
+ i--;
46
+ }
47
+ };
48
+
49
+ module.exports = function (core) {
50
+ const {
51
+ logger,
52
+ scopes: { sources, instrumentation },
53
+ patcher,
54
+ assess: {
55
+ dataflow: {
56
+ tracker,
57
+ eventFactory: { createPropagationEvent },
58
+ },
59
+ },
60
+ } = core;
61
+
62
+ function createEvent(data, value, newTags, reviver, strInfo) {
63
+ const method = 'JSON.parse';
64
+ return createPropagationEvent({
65
+ context: method,
66
+ name: method,
67
+ history: [strInfo],
68
+ moduleName: 'JSON',
69
+ methodName: 'parse',
70
+ object: {
71
+ value: inspect(data.obj),
72
+ tracked: false,
73
+ },
74
+ args: [
75
+ {
76
+ value: data.args[0],
77
+ tracked: true,
78
+ },
79
+ reviver && {
80
+ value: reviver.toString(),
81
+ tracked: false,
82
+ },
83
+ ].filter(Boolean),
84
+ result: {
85
+ value,
86
+ tracked: true,
87
+ },
88
+ value,
89
+ tags: newTags,
90
+ stacktraceOpts: {
91
+ constructorOpt: data.hooked,
92
+ prependFrames: [data.orig],
93
+ },
94
+ source: 'P0',
95
+ target: 'R',
96
+ });
97
+ }
98
+
99
+ function getNewTags(strInfo, startIndex, endIndex) {
100
+ const overlappingRanges = createOverlappingTags(
101
+ strInfo.tags,
102
+ startIndex,
103
+ endIndex
104
+ );
105
+
106
+ const tags = {};
107
+ const startingOffset = startIndex;
108
+ const normalizedEndIndex = endIndex - startingOffset;
109
+
110
+ Object.entries(overlappingRanges).forEach(([tag, ranges]) => {
111
+ tags[tag] = [];
112
+
113
+ ranges.forEach(([start, end]) => {
114
+ const transferredStartIndex = start - startingOffset;
115
+ const transferredEndIndex = end - startingOffset;
116
+
117
+ tags[tag].push(transferredStartIndex < 0 ? 0 : transferredStartIndex);
118
+ tags[tag].push(
119
+ transferredEndIndex > normalizedEndIndex
120
+ ? normalizedEndIndex
121
+ : transferredEndIndex
122
+ );
123
+ });
124
+ });
125
+
126
+ return tags;
127
+ }
128
+
129
+ return (core.assess.dataflow.propagation.jsonInstrumentation.parse = {
130
+ install() {
131
+ patcher.patch(JSON, 'parse', {
132
+ name: 'JSON.prototype.parse',
133
+ patchType,
134
+ pre(data) {
135
+ if (!sources.getStore()?.assess || instrumentation.isLocked()) return;
136
+ const input = data.args[0];
137
+
138
+ const strInfo = tracker.getData(input);
139
+ if (!strInfo) return;
140
+
141
+ const reviver = data.args[1];
142
+ const stack = [];
143
+ let keyValueIndexes;
144
+
145
+ try {
146
+ keyValueIndexes = getKeyValueIndexes(input);
147
+ } catch (error) {
148
+ logger.warn({ error, string: input }, 'JSON.parse() propagation failed');
149
+ keyValueIndexes = [];
150
+ }
151
+
152
+ // Error found, continue without instrumentation
153
+ if (keyValueIndexes.length === 0) return;
154
+
155
+ let i = 0;
156
+ function contrastParseReviver(key, value) {
157
+ const { value: [startIndex, endIndex] } = keyValueIndexes[i];
158
+
159
+ if (!isString(value)) {
160
+ return reviver ? reviver(key, value) : value;
161
+ }
162
+
163
+ const newTags = getNewTags(strInfo, startIndex, endIndex);
164
+
165
+ const event = createEvent(data, value, newTags, reviver, strInfo);
166
+ if (!event || Object.keys(newTags).length === 0) {
167
+ return reviver ? reviver(key, value) : value;
168
+ }
169
+
170
+ const { extern } = tracker.track(value, event);
171
+
172
+ if (reviver) return reviver(key, extern);
173
+
174
+ return extern;
175
+ }
176
+
177
+ data.args[1] = function (key, value) {
178
+ if (typeof value === 'object') {
179
+ applyValuesToObject(value, stack);
180
+ }
181
+
182
+ const result = contrastParseReviver(key, value);
183
+ stack.push({ key, value: result });
184
+
185
+ i += 1;
186
+
187
+ return result;
188
+ };
189
+ },
190
+ });
191
+ },
192
+ uninstall() {
193
+ JSON.parse = patcher.unwrap(JSON.parse);
194
+ },
195
+ });
196
+ };
@@ -81,8 +81,8 @@ module.exports = function(core) {
81
81
  } = core;
82
82
 
83
83
  function getUntrustedSpaceProps(space) {
84
- // it can't be a problem if it's not a string, if the string is all spaces, or
85
- // if the string is zero length.
84
+ // it can't be a problem if it's not a string, if the string is all spaces, or
85
+ // if the string is zero length.
86
86
  if (!isString(space) || match(space, /^\s+$/) || !space) {
87
87
  return null;
88
88
  }
@@ -239,8 +239,10 @@ module.exports = function(core) {
239
239
 
240
240
  const method = 'JSON.stringify';
241
241
  const event = createPropagationEvent({
242
- context: method,
242
+ context: `${method}(${ret})`,
243
243
  name: method,
244
+ moduleName: 'JSON',
245
+ methodName: 'stringify',
244
246
  history: Array.from(metadata.history),
245
247
  object: {
246
248
  value: inspect(data.obj),
@@ -19,7 +19,7 @@ const {
19
19
  createAppendTags
20
20
  } = require('../../tag-utils');
21
21
  const { patchType } = require('../common');
22
- const { isString } = require('@contrast/common');
22
+ const { isString, join, inspect } = require('@contrast/common');
23
23
 
24
24
  module.exports = function(core) {
25
25
  const {
@@ -63,36 +63,43 @@ module.exports = function(core) {
63
63
 
64
64
  return core.assess.dataflow.propagation.arrayJoin = {
65
65
  install() {
66
- const originalJoin = Array.prototype.join;
66
+ const name = 'Array.prototype.join';
67
+
67
68
  patcher.patch(Array.prototype, 'join', {
68
- name: 'Array.prototype.join',
69
+ name,
69
70
  patchType,
70
71
  post(data) {
71
- const { args, obj, result, hooked, orig } = data;
72
+ const { args: origArgs, obj, result, hooked, orig } = data;
72
73
  if (!result || !sources.getStore()?.assess || instrumentation.isLocked()) return;
73
74
 
74
75
  const resultInfo = tracker.getData(result);
75
- const delimiter = args[0] === undefined ? ',' : args[0];
76
+ const delimiter = origArgs[0] === undefined ? ',' : origArgs[0];
76
77
  const delimiterLength = delimiter.length;
77
78
  const delimiterInfo = tracker.getData(delimiter);
78
79
  const initHistory = delimiterInfo ? new Set([delimiterInfo]) : new Set();
79
80
  const { newTags, newHistory: history } = accumulateTags(obj, {}, 0, initHistory, delimiterLength, delimiterInfo?.tags);
81
+ const object = {
82
+ value: obj && join(obj),
83
+ tracked: false
84
+ };
85
+
86
+ const args = [{
87
+ value: delimiterInfo ? delimiterInfo.value : delimiter,
88
+ tracked: !!delimiterInfo
89
+ }];
80
90
 
81
91
  if (history.size) {
82
92
  const event = createPropagationEvent({
83
- name: 'Array.prototype.join',
84
- object: {
85
- value: originalJoin.call(obj),
86
- tracked: false
87
- },
93
+ name,
94
+ moduleName: 'Array',
95
+ methodName: 'prototype.join',
96
+ context: `${object.value}.join('${inspect(args[0].value) || ''})`,
97
+ object,
88
98
  result: {
89
99
  value: resultInfo ? resultInfo.value : result,
90
100
  tracked: true
91
101
  },
92
- args: [{
93
- value: delimiterInfo ? delimiterInfo.value : delimiter,
94
- tracked: !!delimiterInfo
95
- }],
102
+ args,
96
103
  tags: newTags,
97
104
  history: Array.from(history),
98
105
  source: delimiterInfo ? (history.size > 1 ? 'A' : 'P') : 'O',
@@ -46,6 +46,8 @@ module.exports = function(core) {
46
46
 
47
47
  const event = eventFactory.createPropagationEvent({
48
48
  args: data.args.map((a) => ({ tracked: false, value: a })),
49
+ moduleName: 'Buffer',
50
+ methodName: 'prototype.toString',
49
51
  context: 'buffer.toString()',
50
52
  object: { tracked: true, value: 'Buffer' },
51
53
  history: [bufferInfo],
@@ -78,6 +78,8 @@ module.exports = function(core) {
78
78
  }
79
79
  ],
80
80
  context: `${inspect(leftArg)} + ${inspect(rightArg)}`,
81
+ moduleName: 'global',
82
+ methodName: 'ContrastMethods.add',
81
83
  history,
82
84
  object: {
83
85
  value: 'String Addition',
@@ -28,6 +28,7 @@ module.exports = function(core) {
28
28
  };
29
29
 
30
30
  require('./add')(core);
31
+ require('./number')(core);
31
32
  require('./tag')(core);
32
33
  require('./string')(core);
33
34