@contrast/agent 4.7.0 → 4.7.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 (76) hide show
  1. package/bin/VERSION +1 -1
  2. package/bin/linux/contrast-service +0 -0
  3. package/bin/mac/contrast-service +0 -0
  4. package/bin/windows/contrast-service.exe +0 -0
  5. package/lib/assess/membrane/deserialization-membrane.js +4 -5
  6. package/lib/assess/membrane/source-membrane.js +15 -19
  7. package/lib/assess/models/call-context.js +1 -1
  8. package/lib/assess/policy/propagators.json +8 -0
  9. package/lib/assess/policy/rules.json +2 -2
  10. package/lib/assess/policy/signatures.json +27 -0
  11. package/lib/assess/policy/util.js +2 -1
  12. package/lib/assess/propagators/JSON/parse.js +1 -1
  13. package/lib/assess/propagators/JSON/stringify.js +3 -3
  14. package/lib/assess/propagators/array-prototype-join.js +7 -8
  15. package/lib/assess/propagators/common.js +7 -5
  16. package/lib/assess/propagators/handlebars-escape-expresssion.js +1 -1
  17. package/lib/assess/propagators/joi/boolean.js +1 -1
  18. package/lib/assess/propagators/joi/expression.js +1 -1
  19. package/lib/assess/propagators/joi/number.js +1 -1
  20. package/lib/assess/propagators/joi/string-base.js +1 -1
  21. package/lib/assess/propagators/joi/string-schema.js +12 -13
  22. package/lib/assess/propagators/joi/values.js +11 -11
  23. package/lib/assess/propagators/manager.js +12 -10
  24. package/lib/assess/propagators/mongoose/helpers.js +20 -0
  25. package/lib/assess/propagators/mongoose/index.js +18 -0
  26. package/lib/assess/propagators/mongoose/map.js +74 -0
  27. package/lib/assess/propagators/mongoose/string.js +104 -0
  28. package/lib/assess/propagators/number.js +54 -0
  29. package/lib/assess/propagators/object.js +6 -7
  30. package/lib/assess/propagators/path/basename.js +14 -13
  31. package/lib/assess/propagators/path/common.js +1 -1
  32. package/lib/assess/propagators/path/dirname.js +14 -13
  33. package/lib/assess/propagators/path/extname.js +14 -13
  34. package/lib/assess/propagators/path/parse.js +1 -1
  35. package/lib/assess/propagators/path/relative.js +7 -5
  36. package/lib/assess/propagators/querystring/escape.js +20 -18
  37. package/lib/assess/propagators/querystring/parse.js +7 -5
  38. package/lib/assess/propagators/querystring/stringify.js +25 -24
  39. package/lib/assess/propagators/querystring/unescape.js +20 -18
  40. package/lib/assess/propagators/sequelize/sql-string-escape.js +1 -1
  41. package/lib/assess/propagators/sequelize/sql-string-format-named-parameters.js +1 -1
  42. package/lib/assess/propagators/sequelize/sql-string-format.js +3 -3
  43. package/lib/assess/propagators/sequelize/utils.js +2 -2
  44. package/lib/assess/propagators/string-prototype-replace.js +30 -28
  45. package/lib/assess/propagators/string-prototype-split.js +36 -36
  46. package/lib/assess/propagators/string-prototype-trim.js +15 -17
  47. package/lib/assess/propagators/string.js +12 -16
  48. package/lib/assess/propagators/template-escape.js +21 -18
  49. package/lib/assess/propagators/templates.js +8 -8
  50. package/lib/assess/propagators/url/url-prototype-parse.js +5 -6
  51. package/lib/assess/propagators/url/url-url.js +51 -43
  52. package/lib/assess/propagators/util/format.js +1 -1
  53. package/lib/assess/propagators/v8/init-hooks.js +3 -3
  54. package/lib/assess/propagators/validator/init-hooks.js +22 -22
  55. package/lib/assess/sinks/common.js +10 -5
  56. package/lib/assess/sinks/libxmljs-xxe.js +1 -1
  57. package/lib/assess/sinks/mongodb.js +2 -1
  58. package/lib/assess/sinks/ssrf-url.js +1 -1
  59. package/lib/constants.js +4 -1
  60. package/lib/core/config/options.js +1 -1
  61. package/lib/core/rewrite/injections.js +8 -0
  62. package/lib/feature-set.js +1 -1
  63. package/lib/hooks/object-to-primitive.js +6 -7
  64. package/lib/hooks/patcher.js +1 -1
  65. package/lib/protect/rules/nosqli/nosql-injection-rule.js +228 -0
  66. package/lib/protect/rules/rule-factory.js +2 -2
  67. package/lib/protect/service.js +23 -11
  68. package/lib/protect/sinks/mongodb.js +56 -55
  69. package/lib/reporter/translations/to-protobuf/dtm/index.js +1 -1
  70. package/lib/reporter/translations/to-protobuf/dtm/ip-denylist-details.js +1 -1
  71. package/lib/reporter/translations/to-protobuf/dtm/rasp-rule-sample.js +1 -1
  72. package/lib/reporter/translations/to-protobuf/settings/defend-features.js +8 -6
  73. package/lib/reporter/translations/to-protobuf/settings/exclusions.js +5 -4
  74. package/lib/tracker.js +13 -65
  75. package/package.json +2 -1
  76. package/lib/protect/rules/nosqli/no-sql-injection-rule.js +0 -109
@@ -80,7 +80,7 @@ module.exports.handle = function() {
80
80
  patchType: PATCH_TYPES.ASSESS_PROPAGATOR,
81
81
  post(data) {
82
82
  const trackingData = tracker.getData(data.result);
83
- if (!trackingData.tracked) {
83
+ if (!trackingData) {
84
84
  return;
85
85
  }
86
86
 
@@ -44,7 +44,7 @@ module.exports.handle = function() {
44
44
  post(data) {
45
45
  // else either the replacement value or a values in the array replacement is being tracked
46
46
  const trackingData = tracker.getData(data.result);
47
- if (!trackingData.tracked) {
47
+ if (!trackingData) {
48
48
  return;
49
49
  }
50
50
 
@@ -59,7 +59,7 @@ module.exports.handle = function() {
59
59
  const len = positions.length; // micro optomized since used multiple times
60
60
 
61
61
  for (let i = 0; i < len; i++) {
62
- const isTracked = tracker.getData(replacements[i]).tracked;
62
+ const props = tracker.getData(replacements[i]);
63
63
  // we don't need to run in a no instrumentation scope here since
64
64
  // patcher will do so automatically since we're in a post hook
65
65
  const escapedVal = getSequelizeString().escape(
@@ -69,7 +69,7 @@ module.exports.handle = function() {
69
69
  true // true is required to get format function behavior.
70
70
  );
71
71
 
72
- if (isTracked) {
72
+ if (props && props.tracked) {
73
73
  tagRangeUtil.addInPlace(
74
74
  trackingData.tagRanges,
75
75
  new TagRange(
@@ -22,8 +22,8 @@ const tracker = require('../../../tracker');
22
22
  */
23
23
  module.exports.isTracked = function isTracked(value) {
24
24
  return Array.isArray(value)
25
- ? value.some((v) => typeof v === 'string' && tracker.getData(v).tracked)
26
- : tracker.getData(value).tracked;
25
+ ? value.some((v) => typeof v === 'string' && tracker.getData(v))
26
+ : !!tracker.getData(value);
27
27
  };
28
28
 
29
29
  /**
@@ -115,13 +115,13 @@ function getTrackData(callContext) {
115
115
  };
116
116
 
117
117
  const objData = tracker.getData(callContext.obj);
118
- if (objData.tracked) {
118
+ if (objData) {
119
119
  trackData.objData = objData;
120
120
  trackData.resultTagRanges = objData.tagRanges.map((r) => r.clone());
121
121
  }
122
122
 
123
123
  const replacerData = tracker.getData(callContext.args[1]);
124
- if (replacerData.tracked) {
124
+ if (replacerData) {
125
125
  trackData.replacerData = replacerData;
126
126
  }
127
127
 
@@ -243,7 +243,7 @@ function getReplacementAndCaptureDynamicTags({ callContext, args }) {
243
243
  // Allow propagation within replacer function so we can track return value.
244
244
  _replacer = invokeReplacer({ callContext, args, replacer });
245
245
  const data = tracker.getData(_replacer);
246
- if (data.tracked) {
246
+ if (data) {
247
247
  trackData.dynamicTagRanges = data.tagRanges;
248
248
  trackData.dynamicEvents.add(data.event);
249
249
  } else {
@@ -297,7 +297,7 @@ function wrapOriginalReplacerArguments({ callContext, args }) {
297
297
  // Only wrap arguments that are used by replacer,
298
298
  for (let idx = 0; idx < replacer.length; idx++) {
299
299
  // and only track if needed
300
- if (tracker.getData(args[idx]).tracked) {
300
+ if (tracker.getData(args[idx])) {
301
301
  continue;
302
302
  }
303
303
  trackReplacerArgAtPath({
@@ -330,20 +330,21 @@ function trackReplacerArgAtPath({ callContext, target, path, tagRanges }) {
330
330
  continue;
331
331
  }
332
332
 
333
- const result = tracker.track(_result);
334
- const trackData = tracker.getData(result);
335
- // TODO: Improve accuracy here by deriving actual ranges from source,
336
- // rather than applying "blanket" ranges the length of argument.
337
- const tagRange = new TagRange(0, result.length - 1, tag);
338
- trackData.tagRanges.push(tagRange);
339
- trackData.event = createPropagationEvent({
340
- callContext,
341
- tagRanges: [tagRange],
342
- result
343
- });
344
-
345
- captured.add(tag);
346
- target[path] = result;
333
+ const tracked = tracker.track(_result);
334
+ if (tracked) {
335
+ // TODO: Improve accuracy here by deriving actual ranges from source,
336
+ // rather than applying "blanket" ranges the length of argument.
337
+ const tagRange = new TagRange(0, tracked.str.length - 1, tag);
338
+ tracked.props.tagRanges.push(tagRange);
339
+ tracked.props.event = createPropagationEvent({
340
+ callContext,
341
+ tagRanges: [tagRange],
342
+ result: tracked.str
343
+ });
344
+
345
+ captured.add(tag);
346
+ target[path] = tracked.str;
347
+ }
347
348
  }
348
349
  } else if (_.isObject(_result)) {
349
350
  for (const path of Object.keys(_result)) {
@@ -582,20 +583,21 @@ function trackFinalResult(callContext) {
582
583
  return;
583
584
  }
584
585
 
585
- if (tracker.getData(callContext.result).tracked) {
586
+ if (tracker.getData(callContext.result)) {
586
587
  return;
587
588
  }
588
589
 
589
- const result = tracker.track(callContext.result);
590
- callContext.result = result;
590
+ const tracked = tracker.track(callContext.result);
591
591
 
592
- const resultData = tracker.getData(result);
593
- resultData.event = createPropagationEvent({
594
- callContext,
595
- tagRanges: trackData.resultTagRanges,
596
- result
597
- });
598
- resultData.tagRanges = trackData.resultTagRanges;
592
+ if (tracked) {
593
+ tracked.props.event = createPropagationEvent({
594
+ callContext,
595
+ tagRanges: trackData.resultTagRanges,
596
+ result: tracked.str
597
+ });
598
+ tracked.props.tagRanges = trackData.resultTagRanges;
599
+ callContext.result = tracked.str;
600
+ }
599
601
  }
600
602
 
601
603
  /**
@@ -164,28 +164,27 @@ module.exports.handle = {
164
164
  if (tagStart !== null) {
165
165
  newTagRanges.push(new TagRange(tagStart, tagStop, tag.tag));
166
166
  const tracked = tracker.track(stringPart);
167
- const props = tracker.getData(tracked);
168
- if (!props.tagRanges) {
169
- props.tagRanges = [];
167
+ if (tracked) {
168
+ tracked.props.tagRanges.push(new TagRange(tagStart, tagStop, tag.tag));
169
+ result[i] = tracked.str;
170
170
  }
171
- props.tagRanges.push(new TagRange(tagStart, tagStop, tag.tag));
172
- result[i] = tracked;
173
171
  }
174
172
  });
175
173
  if (newTagRanges.length > 0) {
176
174
  const tracked = tracker.track(stringPart);
177
- const props = tracker.getData(tracked);
178
- props.tagRanges = newTagRanges;
179
- result[i] = tracked;
180
- const event = new PropagationEvent({
181
- context: ctxt,
182
- signature: sig,
183
- tagRanges: tracked.tagRanges,
184
- source: 'O',
185
- target: 'R'
186
- });
187
- event.parents.push(oldEvent);
188
- props.event = event;
175
+ if (tracked) {
176
+ tracked.props.tagRanges = newTagRanges;
177
+ result[i] = tracked.str;
178
+ const event = new PropagationEvent({
179
+ context: ctxt,
180
+ signature: sig,
181
+ tagRanges: tracked.props.tagRanges,
182
+ source: 'O',
183
+ target: 'R'
184
+ });
185
+ event.parents.push(oldEvent);
186
+ tracked.props.event = event;
187
+ }
189
188
  }
190
189
  }
191
190
  data.result = result;
@@ -225,21 +224,21 @@ function handleEmptySeperator(data, oldTagRanges, oldEvent) {
225
224
  if (sharedCharInfo.has(i)) {
226
225
  info = sharedCharInfo.get(i);
227
226
  } else {
228
- const trackedString = tracker.track(char);
229
- const trackedProperties = tracker.getData(trackedString);
230
-
231
- const event = new PropagationEvent({
232
- context: ctxt,
233
- signature: sig,
234
- tagRanges: getTagRanges(trackedString),
235
- source: 'O',
236
- target: 'R'
237
- });
238
-
239
- trackedProperties.event = event;
240
- info = { event, tagRanges: trackedProperties.tagRanges };
241
- sharedCharInfo.set(i, info);
242
- result[i] = trackedString;
227
+ const tracked = tracker.track(char);
228
+ if (tracked) {
229
+ const event = new PropagationEvent({
230
+ context: ctxt,
231
+ signature: sig,
232
+ tagRanges: getTagRanges(tracked.str),
233
+ source: 'O',
234
+ target: 'R'
235
+ });
236
+
237
+ tracked.props.event = event;
238
+ info = { event, tagRanges: tracked.props.tagRanges };
239
+ sharedCharInfo.set(i, info);
240
+ result[i] = tracked.str;
241
+ }
243
242
  }
244
243
 
245
244
  info.tagRanges.push(new TagRange(0, 0, tag.tag));
@@ -257,10 +256,11 @@ function transferTracking(origString, resultArray) {
257
256
 
258
257
  for (let i = 0; i < resultArray.length; i++) {
259
258
  const tracked = tracker.track(resultArray[i]);
260
- const trackedProperties = tracker.getData(tracked);
261
- trackedProperties.tagRanges = getTagRanges(origString);
262
- trackedProperties.event = getEvent(origString);
263
- resultArray[i] = tracked;
259
+ if (tracked) {
260
+ tracked.props.tagRanges = getTagRanges(origString);
261
+ tracked.props.event = getEvent(origString);
262
+ resultArray[i] = tracked.str;
263
+ }
264
264
  }
265
265
  return resultArray;
266
266
  }
@@ -25,7 +25,7 @@ const signature = new Signature('String.prototype.trim');
25
25
  function handle(data) {
26
26
  const { obj, result } = data;
27
27
  const sourceMetadata = tracker.getData(obj);
28
- if (!sourceMetadata.tracked) {
28
+ if (!sourceMetadata) {
29
29
  return;
30
30
  }
31
31
 
@@ -43,21 +43,19 @@ function handle(data) {
43
43
  }
44
44
 
45
45
  const tracked = tracker.track(result);
46
- const trackedData = tracker.getData(tracked);
47
- if (!trackedData.tracked) {
48
- return;
46
+ if (tracked) {
47
+ tracked.props.tagRanges = targetRanges;
48
+ const context = new CallContext(data);
49
+ const event = new PropagationEvent({
50
+ context,
51
+ signature,
52
+ tagRanges: targetRanges,
53
+ source: 'O',
54
+ target: 'R'
55
+ });
56
+ event.parents.push(sourceEvent);
57
+ tracked.props.event = event;
58
+
59
+ data.result = tracked.str;
49
60
  }
50
- trackedData.tagRanges = targetRanges;
51
- const context = new CallContext(data);
52
- const event = new PropagationEvent({
53
- context,
54
- signature,
55
- tagRanges: targetRanges,
56
- source: 'O',
57
- target: 'R'
58
- });
59
- event.parents.push(sourceEvent);
60
- trackedData.event = event;
61
-
62
- data.result = tracked;
63
61
  }
@@ -41,30 +41,26 @@ function handle() {
41
41
 
42
42
  // Checks if the string literal form of the new String object is tracked.
43
43
  // If so, we will want to copy the existing tag ranges below.
44
- if (data.obj && !argData.tracked) {
44
+ if (data.obj && !argData) {
45
45
  argData = tracker.getData(data.result.toString());
46
46
  }
47
47
 
48
- if (!arg || !argData.tracked) {
48
+ if (!arg || !argData) {
49
49
  return;
50
50
  }
51
51
 
52
- const newStr = tracker.track(data.result);
53
- const newStrData = tracker.getData(newStr);
52
+ const tracked = tracker.track(data.result);
53
+ if (tracked) {
54
+ // In a constructor context, data.obj is set to the new String object.
55
+ // When a new String object is instantiated we must copy the existing tag
56
+ // ranges from the string literal to the new String object.
57
+ if (data.obj) {
58
+ tracked.props.event = argData.event;
59
+ tracked.props.tagRanges = tracked.props.tagRanges.concat(argData.tagRanges);
60
+ }
54
61
 
55
- if (!newStrData.tracked) {
56
- return;
57
- }
58
-
59
- // In a constructor context, data.obj is set to the new String object.
60
- // When a new String object is instantiated we must copy the existing tag
61
- // ranges from the string literal to the new String object.
62
- if (data.obj) {
63
- newStrData.event = argData.event;
64
- newStrData.tagRanges = newStrData.tagRanges.concat(argData.tagRanges);
62
+ data.result = tracked.str;
65
63
  }
66
-
67
- data.result = newStr;
68
64
  }
69
65
  });
70
66
  }
@@ -50,35 +50,38 @@ function getEscapedTagRanges(input, result, start, stop, tag) {
50
50
  function propagator(data, tagName, signatureName) {
51
51
  const input = data.args[0];
52
52
 
53
- if (!input || !tracker.getData(input).tracked) {
53
+ const trackedData = tracker.getData(input);
54
+
55
+ if (!input || !trackedData) {
54
56
  return;
55
57
  }
56
58
 
57
59
  // adjust tag ranges
58
60
  const tagRanges = [];
59
- tracker.getData(input).tagRanges.forEach((range) => {
61
+ trackedData.tagRanges.forEach((range) => {
60
62
  const { start, stop, tag } = range;
61
63
  tagRanges.push(
62
64
  ...getEscapedTagRanges(input, data.result, start, stop, tag)
63
65
  );
64
66
  });
65
67
  tagRanges.push(new TagRange(0, data.result.length - 1, tagName));
66
- const result = tracker.track(data.result);
67
- const trackData = tracker.getData(result);
68
- trackData.tagRanges = tagRanges;
69
- trackData.event = new PropagationEvent({
70
- context: new CallContext({
71
- ...data,
72
- obj: null
73
- }),
74
- parents: [trackData.event],
75
- signature: new Signature(signatureName),
76
- source: 'P',
77
- target: 'R',
78
- tagRanges,
79
- tags: tagName
80
- });
81
- data.result = result;
68
+ const tracked = tracker.track(data.result);
69
+ if (tracked) {
70
+ tracked.props.tagRanges = tagRanges;
71
+ tracked.props.event = new PropagationEvent({
72
+ context: new CallContext({
73
+ ...data,
74
+ obj: null
75
+ }),
76
+ parents: [tracked.props.event],
77
+ signature: new Signature(signatureName),
78
+ source: 'P',
79
+ target: 'R',
80
+ tagRanges,
81
+ tags: tagName
82
+ });
83
+ data.result = tracked.str;
84
+ }
82
85
  }
83
86
 
84
87
  module.exports.propagate = propagator;
@@ -75,17 +75,17 @@ function __contrastTag(...args) {
75
75
  });
76
76
 
77
77
  if (tagRanges.length > 0) {
78
- result = tracker.track(result);
79
- const resultContrastProperties = tracker.getData(result);
80
- if (resultContrastProperties.tracked) {
78
+ const tracked = tracker.track(result);
79
+ if (tracked) {
81
80
  buildProperties(
82
- resultContrastProperties,
81
+ tracked.props,
83
82
  tagRanges,
84
83
  expressions,
85
- result,
84
+ tracked.str,
86
85
  strings,
87
86
  sourceEvents
88
87
  );
88
+ result = tracked.str;
89
89
  }
90
90
  }
91
91
 
@@ -143,18 +143,18 @@ function buildProperties(props, tagRanges, exps, result, strings, events) {
143
143
  */
144
144
  function moveTags(exp, sourceEvents, tagRanges, offset) {
145
145
  const contrastProperties = tracker.getData(exp);
146
- const tracked = contrastProperties.tracked || tagRanges.length > 0;
146
+ const tracked = contrastProperties || tagRanges.length > 0;
147
147
  if (!tracked) {
148
148
  return tagRanges;
149
149
  }
150
150
 
151
- const event = contrastProperties.tracked && contrastProperties.event;
151
+ const event = contrastProperties && contrastProperties.event;
152
152
  if (event) {
153
153
  sourceEvents.push(event);
154
154
  }
155
155
 
156
156
  let newTagRanges = [];
157
- if (contrastProperties.tracked) newTagRanges = contrastProperties.tagRanges;
157
+ if (contrastProperties) newTagRanges = contrastProperties.tagRanges;
158
158
 
159
159
  return tagRangeUtil.addAllWithOffset(tagRanges, newTagRanges, offset);
160
160
  }
@@ -45,7 +45,7 @@ function handle(data) {
45
45
  const address = data.args[0];
46
46
  const sourceMetadata = tracker.getData(address);
47
47
 
48
- if (!sourceMetadata.tracked) {
48
+ if (!sourceMetadata) {
49
49
  return;
50
50
  }
51
51
 
@@ -71,9 +71,8 @@ function propagate(sourceMetadata, address, data) {
71
71
 
72
72
  if (part && typeof part === 'string' && part !== '') {
73
73
  const tracked = tracker.track(part);
74
- const trackedData = tracker.getData(tracked);
75
74
 
76
- if (!trackedData.tracked) {
75
+ if (!tracked) {
77
76
  continue;
78
77
  }
79
78
 
@@ -105,10 +104,10 @@ function propagate(sourceMetadata, address, data) {
105
104
  );
106
105
  event.parents.push(sourceEvent);
107
106
 
108
- trackedData.tagRanges = targetTagRanges;
109
- trackedData.event = event;
107
+ tracked.props.tagRanges = targetTagRanges;
108
+ tracked.props.event = event;
110
109
 
111
- url[key] = tracked;
110
+ url[key] = tracked.str;
112
111
  }
113
112
  }
114
113
  }
@@ -71,6 +71,9 @@ function setHostnamePortTags(urlObj, stack, args, sourceEvent) {
71
71
  }
72
72
 
73
73
  const hostData = tracker.getData(urlObj._contrast_host);
74
+ if (!hostData) {
75
+ return;
76
+ }
74
77
  const hostnameTags = tagRangeUtil.trim(hostData.tagRanges, 0, splitIndex - 1);
75
78
  const portTags = tagRangeUtil.trim(
76
79
  hostData.tagRanges,
@@ -81,21 +84,25 @@ function setHostnamePortTags(urlObj, stack, args, sourceEvent) {
81
84
 
82
85
  if (hostnameTags.length > 0) {
83
86
  const hostnameTracked = tracker.track(hostname);
84
- tracker.getData(hostnameTracked).tagRanges = hostnameTags;
85
- urlObj._contrast_hostname = hostnameTracked;
86
- event = createEvent('url.URL', stack, hostnameTags, hostname, args, urlObj);
87
- event.parents.push(sourceEvent);
88
- event.tagRanges = hostnameTags;
89
- tracker.getData(hostnameTracked).event = event;
87
+ if (hostnameTracked) {
88
+ hostnameTracked.props.tagRanges = hostnameTags;
89
+ urlObj._contrast_hostname = hostnameTracked.str;
90
+ event = createEvent('url.URL', stack, hostnameTags, hostname, args, urlObj);
91
+ event.parents.push(sourceEvent);
92
+ event.tagRanges = hostnameTags;
93
+ hostnameTracked.props.event = event;
94
+ }
90
95
  }
91
96
  if (portTags.length > 0) {
92
97
  const portTracked = tracker.track(port);
93
- tracker.getData(portTracked).tagRanges = portTags;
94
- urlObj._contrast_port = portTracked;
95
- event = createEvent('url.URL', stack, portTags, port, args, urlObj);
96
- event.parents.push(sourceEvent);
97
- event.tagRanges = portTags;
98
- tracker.getData(portTracked).event = event;
98
+ if (portTracked) {
99
+ portTracked.props.tagRanges = portTags;
100
+ urlObj._contrast_port = portTracked.str;
101
+ event = createEvent('url.URL', stack, portTags, port, args, urlObj);
102
+ event.parents.push(sourceEvent);
103
+ event.tagRanges = portTags;
104
+ portTracked.props.event = event;
105
+ }
99
106
  }
100
107
  }
101
108
 
@@ -136,26 +143,28 @@ function joinProperties(urlObj, sourceProps, separators) {
136
143
  }
137
144
 
138
145
  // copy tag ranges
139
- const result = tracker.track(value);
140
146
  let valIdx = 0;
141
147
  sepIdx = 0;
142
148
  let tags = [];
143
149
 
144
150
  for (const prop of sourceProps) {
145
- if (urlObj[`_contrast_${prop}`]) {
151
+ const trackedPropData = tracker.getData(urlObj[`_contrast_${prop}`]);
152
+ if (trackedPropData) {
146
153
  tags = tagRangeUtil.addAll(
147
154
  tags,
148
155
  offsetTagRanges(
149
- tracker.getData(urlObj[`_contrast_${prop}`]).tagRanges,
156
+ trackedPropData.tagRanges,
150
157
  valIdx
151
158
  )
152
159
  );
153
160
  }
154
161
  valIdx += urlObj[prop].length + separators[sepIdx++].length;
155
162
  }
156
-
157
- tracker.getData(result).tagRanges = tags;
158
- return result;
163
+ const tracked = tracker.track(value);
164
+ if (tracked) {
165
+ tracked.props.tagRanges = tags;
166
+ return tracked.str;
167
+ }
159
168
  }
160
169
 
161
170
  /**
@@ -172,7 +181,8 @@ function setOriginTags(urlObj, stack, args, sourceEvent) {
172
181
  //
173
182
 
174
183
  const trackedOrigin = joinProperties(urlObj, SET_ORIGIN_TAGS, ['//', '']);
175
- if (tracker.getData(trackedOrigin).tagRanges.length === 0) {
184
+ const trackedOriginData = tracker.getData(trackedOrigin);
185
+ if (!trackedOriginData || trackedOriginData.tagRanges.length === 0) {
176
186
  return;
177
187
  }
178
188
  urlObj._contrast_origin = trackedOrigin;
@@ -180,14 +190,14 @@ function setOriginTags(urlObj, stack, args, sourceEvent) {
180
190
  const event = createEvent(
181
191
  'url.URL',
182
192
  stack,
183
- tracker.getData(trackedOrigin).tagRanges,
193
+ trackedOriginData.tagRanges,
184
194
  trackedOrigin,
185
195
  args,
186
196
  urlObj
187
197
  );
188
198
  event.parents.push(sourceEvent);
189
- event.tagRanges = tracker.getData(trackedOrigin).tagRanges;
190
- tracker.getData(trackedOrigin).event = event;
199
+ event.tagRanges = trackedOriginData.tagRanges;
200
+ trackedOriginData.event = event;
191
201
  }
192
202
 
193
203
  /**
@@ -221,7 +231,7 @@ function setHrefTags(urlObj, stack, args, sourceEvent) {
221
231
 
222
232
  const joinedHref = joinProperties(urlObj, properties, separators);
223
233
  const joinedHrefData = tracker.getData(joinedHref);
224
- if (joinedHrefData.tagRanges.length === 0) {
234
+ if (!joinedHrefData || joinedHrefData.tagRanges.length === 0) {
225
235
  return;
226
236
  }
227
237
  urlObj._contrast_href = joinedHref;
@@ -235,7 +245,7 @@ function setHrefTags(urlObj, stack, args, sourceEvent) {
235
245
  );
236
246
  event.parents.push(sourceEvent);
237
247
  event.tagRanges = joinedHrefData.tagRanges;
238
- tracker.getData(urlObj._contrast_href).event = event;
248
+ joinedHrefData.event = event;
239
249
  }
240
250
 
241
251
  /**
@@ -300,21 +310,22 @@ function copyTagsSingleSource(sourceTagRanges, sourceEvent, data) {
300
310
  copied = true;
301
311
  const trackedKey = `_contrast_${key}`;
302
312
  const tracked = tracker.track(val);
303
- const trackedData = tracker.getData(tracked);
304
- trackedData.tagRanges = trimmed;
305
- urlObj[trackedKey] = tracked;
306
-
307
- // only create the stack once because stack creation is expensive
308
- stack =
309
- stack ||
310
- stackFactory.createSnapshot({
311
- constructorOpt: data.hooked
312
- })();
313
-
314
- event = createEvent('url.URL', stack, trimmed, val, args, urlObj);
315
- event.parents.push(sourceEvent);
316
- event.tagRanges = trimmed;
317
- trackedData.event = event;
313
+ if (tracked) {
314
+ tracked.props.tagRanges = trimmed;
315
+ urlObj[trackedKey] = tracked.str;
316
+
317
+ // only create the stack once because stack creation is expensive
318
+ stack =
319
+ stack ||
320
+ stackFactory.createSnapshot({
321
+ constructorOpt: data.hooked
322
+ })();
323
+
324
+ event = createEvent('url.URL', stack, trimmed, val, args, urlObj);
325
+ event.parents.push(sourceEvent);
326
+ event.tagRanges = trimmed;
327
+ tracked.props.event = event;
328
+ }
318
329
  }
319
330
 
320
331
  if (!copied) {
@@ -351,10 +362,7 @@ function callUnwrapped(input) {
351
362
  */
352
363
  function skipTracking(inputData, baseData) {
353
364
  // if neither input or base are tracked we can just skip tracking
354
- if (
355
- (!inputData.tracked && baseData && !baseData.tracked) ||
356
- (!inputData.tracked && !baseData)
357
- ) {
365
+ if (!inputData && !baseData) {
358
366
  return true;
359
367
  }
360
368
  return false;
@@ -266,7 +266,7 @@ const propagate = function propagate(util, data) {
266
266
  };
267
267
 
268
268
  const fmtStrMeta = tracker.getData(fmtStr);
269
- if (fmtStrMeta.tracked) {
269
+ if (fmtStrMeta) {
270
270
  resultMeta.fmtStrTagRanges = fmtStrMeta.tagRanges.map((tagRange) =>
271
271
  tagRangeWithOriginals(
272
272
  new TagRange(tagRange.start, tagRange.stop, tagRange.tag)