@contrast/agent 4.5.0 → 4.7.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 (157) 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/source-membrane.js +4 -18
  6. package/lib/assess/policy/propagators.json +11 -21
  7. package/lib/assess/policy/rules.json +5 -0
  8. package/lib/assess/policy/signatures.json +15 -0
  9. package/lib/assess/propagators/dustjs/escape-html.js +22 -0
  10. package/lib/assess/propagators/dustjs/escape-js.js +22 -0
  11. package/lib/assess/propagators/encode-uri/encode-uri-component.js +22 -0
  12. package/lib/assess/propagators/encode-uri/encode-uri.js +22 -0
  13. package/lib/assess/propagators/index.js +0 -2
  14. package/lib/assess/propagators/joi/values.js +26 -11
  15. package/lib/assess/propagators/mustache/escape.js +22 -0
  16. package/lib/assess/propagators/path/common.js +155 -46
  17. package/lib/assess/propagators/path/join.js +5 -1
  18. package/lib/assess/propagators/path/normalize.js +1 -2
  19. package/lib/assess/propagators/path/resolve.js +11 -2
  20. package/lib/assess/propagators/template-escape.js +84 -0
  21. package/lib/assess/propagators/templates.js +2 -3
  22. package/lib/assess/sinks/dustjs-linkedin-xss.js +131 -0
  23. package/lib/core/arch-components/dynamodb.js +1 -2
  24. package/lib/core/arch-components/dynamodbv3.js +44 -0
  25. package/lib/core/arch-components/index.js +1 -0
  26. package/lib/core/arch-components/rethinkdb.js +53 -0
  27. package/lib/core/async-storage/hooks/bluebird.js +20 -0
  28. package/lib/core/config/options.js +2 -1
  29. package/lib/core/stacktrace.js +3 -4
  30. package/lib/feature-set.js +2 -1
  31. package/lib/hooks/frameworks/base.js +8 -2
  32. package/lib/hooks/frameworks/http.js +23 -16
  33. package/lib/hooks/frameworks/http2.js +73 -0
  34. package/lib/hooks/frameworks/index.js +8 -3
  35. package/lib/hooks/http.js +112 -128
  36. package/lib/hooks/patcher.js +69 -48
  37. package/lib/hooks/require.js +16 -22
  38. package/lib/instrumentation.js +0 -3
  39. package/lib/protect/rules/cmd-injection-command-backdoors/backdoor-detector.js +3 -3
  40. package/lib/protect/rules/signatures/reflected-xss/helpers/function-call.js +1 -1
  41. package/lib/protect/rules/xss/helpers/function-call.js +1 -1
  42. package/lib/util/clean-stack.js +1 -1
  43. package/lib/util/clean-string/brackets.js +3 -3
  44. package/lib/util/ip-analyzer.js +1 -1
  45. package/lib/util/some.js +27 -0
  46. package/lib/util/source-map.js +1 -1
  47. package/lib/util/xml-analyzer/external-entity-finder.js +1 -1
  48. package/package.json +14 -16
  49. package/lib/hooks/frameworks/https.js +0 -42
  50. package/node_modules/bindings/LICENSE.md +0 -22
  51. package/node_modules/bindings/README.md +0 -98
  52. package/node_modules/bindings/bindings.js +0 -221
  53. package/node_modules/bindings/package.json +0 -32
  54. package/node_modules/file-uri-to-path/.npmignore +0 -1
  55. package/node_modules/file-uri-to-path/.travis.yml +0 -30
  56. package/node_modules/file-uri-to-path/History.md +0 -21
  57. package/node_modules/file-uri-to-path/LICENSE +0 -20
  58. package/node_modules/file-uri-to-path/README.md +0 -74
  59. package/node_modules/file-uri-to-path/index.d.ts +0 -2
  60. package/node_modules/file-uri-to-path/index.js +0 -66
  61. package/node_modules/file-uri-to-path/package.json +0 -36
  62. package/node_modules/file-uri-to-path/test/test.js +0 -24
  63. package/node_modules/file-uri-to-path/test/tests.json +0 -13
  64. package/node_modules/glossy/LICENSE +0 -19
  65. package/node_modules/glossy/README.md +0 -129
  66. package/node_modules/glossy/index.js +0 -12
  67. package/node_modules/glossy/lib/glossy/parse.js +0 -520
  68. package/node_modules/glossy/lib/glossy/produce.js +0 -459
  69. package/node_modules/glossy/package.json +0 -47
  70. package/node_modules/glossy/test/decide.js +0 -7
  71. package/node_modules/glossy/test/decode_pri.js +0 -24
  72. package/node_modules/glossy/test/parse_3164.js +0 -104
  73. package/node_modules/glossy/test/parse_5424.js +0 -106
  74. package/node_modules/glossy/test/parse_5848.js +0 -40
  75. package/node_modules/glossy/test/parse_8601.js +0 -14
  76. package/node_modules/glossy/test/parse_rfc3339.js +0 -9
  77. package/node_modules/glossy/test/produce.js +0 -162
  78. package/node_modules/glossy/test/runner.js +0 -40
  79. package/node_modules/glossy/test/structure_data.js +0 -24
  80. package/node_modules/nan/CHANGELOG.md +0 -537
  81. package/node_modules/nan/LICENSE.md +0 -13
  82. package/node_modules/nan/README.md +0 -455
  83. package/node_modules/nan/doc/asyncworker.md +0 -146
  84. package/node_modules/nan/doc/buffers.md +0 -54
  85. package/node_modules/nan/doc/callback.md +0 -76
  86. package/node_modules/nan/doc/converters.md +0 -41
  87. package/node_modules/nan/doc/errors.md +0 -226
  88. package/node_modules/nan/doc/json.md +0 -62
  89. package/node_modules/nan/doc/maybe_types.md +0 -583
  90. package/node_modules/nan/doc/methods.md +0 -664
  91. package/node_modules/nan/doc/new.md +0 -147
  92. package/node_modules/nan/doc/node_misc.md +0 -123
  93. package/node_modules/nan/doc/object_wrappers.md +0 -263
  94. package/node_modules/nan/doc/persistent.md +0 -296
  95. package/node_modules/nan/doc/scopes.md +0 -73
  96. package/node_modules/nan/doc/script.md +0 -38
  97. package/node_modules/nan/doc/string_bytes.md +0 -62
  98. package/node_modules/nan/doc/v8_internals.md +0 -199
  99. package/node_modules/nan/doc/v8_misc.md +0 -85
  100. package/node_modules/nan/include_dirs.js +0 -1
  101. package/node_modules/nan/nan.h +0 -2898
  102. package/node_modules/nan/nan_callbacks.h +0 -88
  103. package/node_modules/nan/nan_callbacks_12_inl.h +0 -514
  104. package/node_modules/nan/nan_callbacks_pre_12_inl.h +0 -520
  105. package/node_modules/nan/nan_converters.h +0 -72
  106. package/node_modules/nan/nan_converters_43_inl.h +0 -68
  107. package/node_modules/nan/nan_converters_pre_43_inl.h +0 -42
  108. package/node_modules/nan/nan_define_own_property_helper.h +0 -29
  109. package/node_modules/nan/nan_implementation_12_inl.h +0 -430
  110. package/node_modules/nan/nan_implementation_pre_12_inl.h +0 -263
  111. package/node_modules/nan/nan_json.h +0 -166
  112. package/node_modules/nan/nan_maybe_43_inl.h +0 -356
  113. package/node_modules/nan/nan_maybe_pre_43_inl.h +0 -268
  114. package/node_modules/nan/nan_new.h +0 -340
  115. package/node_modules/nan/nan_object_wrap.h +0 -156
  116. package/node_modules/nan/nan_persistent_12_inl.h +0 -132
  117. package/node_modules/nan/nan_persistent_pre_12_inl.h +0 -242
  118. package/node_modules/nan/nan_private.h +0 -73
  119. package/node_modules/nan/nan_string_bytes.h +0 -305
  120. package/node_modules/nan/nan_typedarray_contents.h +0 -96
  121. package/node_modules/nan/nan_weak.h +0 -437
  122. package/node_modules/nan/package.json +0 -41
  123. package/node_modules/nan/tools/1to2.js +0 -412
  124. package/node_modules/nan/tools/README.md +0 -14
  125. package/node_modules/nan/tools/package.json +0 -19
  126. package/node_modules/unix-dgram/LICENSE +0 -13
  127. package/node_modules/unix-dgram/README.md +0 -107
  128. package/node_modules/unix-dgram/binding.gyp +0 -20
  129. package/node_modules/unix-dgram/build/Makefile +0 -324
  130. package/node_modules/unix-dgram/build/Release/.deps/Release/obj.target/unix_dgram/src/unix_dgram.o.d +0 -58
  131. package/node_modules/unix-dgram/build/Release/.deps/Release/obj.target/unix_dgram.node.d +0 -1
  132. package/node_modules/unix-dgram/build/Release/.deps/Release/unix_dgram.node.d +0 -1
  133. package/node_modules/unix-dgram/build/Release/obj.target/unix_dgram/src/unix_dgram.o +0 -0
  134. package/node_modules/unix-dgram/build/Release/obj.target/unix_dgram.node +0 -0
  135. package/node_modules/unix-dgram/build/Release/unix_dgram.node +0 -0
  136. package/node_modules/unix-dgram/build/binding.Makefile +0 -6
  137. package/node_modules/unix-dgram/build/config.gypi +0 -213
  138. package/node_modules/unix-dgram/build/unix_dgram.target.mk +0 -159
  139. package/node_modules/unix-dgram/lib/unix_dgram.js +0 -168
  140. package/node_modules/unix-dgram/package.json +0 -36
  141. package/node_modules/unix-dgram/src/unix_dgram.cc +0 -404
  142. package/node_modules/unix-dgram/src/win_dummy.cc +0 -7
  143. package/node_modules/unix-dgram/test/test-connect-callback.js +0 -68
  144. package/node_modules/unix-dgram/test/test-connect.js +0 -53
  145. package/node_modules/unix-dgram/test/test-dgram-unix.js +0 -58
  146. package/node_modules/unix-dgram/test/test-send-error.js +0 -26
  147. package/node_modules/winston-syslog/.eslintrc +0 -7
  148. package/node_modules/winston-syslog/.travis.yml +0 -14
  149. package/node_modules/winston-syslog/CHANGELOG.md +0 -9
  150. package/node_modules/winston-syslog/LICENSE +0 -20
  151. package/node_modules/winston-syslog/README.md +0 -135
  152. package/node_modules/winston-syslog/lib/utils.js +0 -26
  153. package/node_modules/winston-syslog/lib/winston-syslog.js +0 -385
  154. package/node_modules/winston-syslog/package.json +0 -56
  155. package/node_modules/winston-syslog/test/format-test.js +0 -122
  156. package/node_modules/winston-syslog/test/syslog-test.js +0 -95
  157. package/node_modules/winston-syslog/test/unix-connect-test.js +0 -133
package/bin/VERSION CHANGED
@@ -1 +1 @@
1
- 2.26.0
1
+ 2.28.0
Binary file
Binary file
Binary file
@@ -39,14 +39,6 @@ const signature = new Signature({
39
39
  isModule: false
40
40
  });
41
41
 
42
- const numericCheck = /^[0-9]+$/;
43
- function isNumeric(input) {
44
- // regex from validator.js/lib/isNumeric.js:
45
- // https://github.com/validatorjs/validator.js/blob/master/lib/isNumeric.js
46
- // do we want to allow symbols (plus/minus sign, decimal point?)
47
- return numericCheck.test(input);
48
- }
49
-
50
42
  module.exports = class SourceMembrane extends Membrane {
51
43
  /**
52
44
  * @param {object} config agent config to use for array sampling.
@@ -294,10 +286,9 @@ module.exports = class SourceMembrane extends Membrane {
294
286
  if (!(metadata.sourceType && metadata.path)) {
295
287
  return false;
296
288
  }
297
- const koaQueryString = metadata.path.match(/(\w+)=/);
298
- if (koaQueryString) {
299
- // get 1st capture group, fall back to `metadata.path` if for some reason this does not exist
300
- metadata.path = koaQueryString[1] || metadata.path;
289
+ const koaQueryString = metadata.path.split('=');
290
+ if (koaQueryString[1]) {
291
+ metadata.path = koaQueryString[0];
301
292
  }
302
293
  return true;
303
294
  }
@@ -305,8 +296,7 @@ module.exports = class SourceMembrane extends Membrane {
305
296
  /**
306
297
  * The `TagRanges` returned will include the `untrusted` tag. There will be
307
298
  * `exclusion:${ruleId}` tags for input exclusions pertaining to specific
308
- * rules and whose name matches the property name described in metadata. And
309
- * `limited-chars` tags are included if string is numeric.
299
+ * rules and whose name matches the property name described in metadata.
310
300
  * @param {string} str string being tracked by membrane
311
301
  * @param {object} metadata metadata about source type and key name
312
302
  * @returns {TagRange[]}
@@ -343,10 +333,6 @@ module.exports = class SourceMembrane extends Membrane {
343
333
  }
344
334
  }
345
335
 
346
- if (isNumeric(str)) {
347
- tagRanges.push(new TagRange(start, stop, 'limited-chars'));
348
- }
349
-
350
336
  if (metadata.sourceType === 'header') {
351
337
  if (metadata.path.toLocaleLowerCase() !== 'referer') {
352
338
  tagRanges.push(new TagRange(start, stop, 'header'));
@@ -59,13 +59,15 @@
59
59
  },
60
60
  "mustache.escape": {
61
61
  "enabled": true,
62
- "source": "P",
63
- "target": "R",
64
- "tags": ["html-encoded"],
65
- "type": "overload",
66
- "command": {
67
- "type": "keep"
68
- }
62
+ "provider": "./propagators/mustache/escape.js"
63
+ },
64
+ "dust.escapeHtml": {
65
+ "enabled": true,
66
+ "provider": "./propagators/dustjs/escape-html.js"
67
+ },
68
+ "dust.escapeJs": {
69
+ "enabled": true,
70
+ "provider": "./propagators/dustjs/escape-js.js"
69
71
  },
70
72
  "pug.compile": {
71
73
  "enabled": true,
@@ -329,23 +331,11 @@
329
331
  },
330
332
  "encodeURI": {
331
333
  "enabled": true,
332
- "type": "overload",
333
- "tags": ["weak-url-encoded"],
334
- "source": "P",
335
- "target": "R",
336
- "command": {
337
- "type": "keep"
338
- }
334
+ "provider": "./propagators/encode-uri/encode-uri.js"
339
335
  },
340
336
  "encodeURIComponent": {
341
337
  "enabled": true,
342
- "type": "overload",
343
- "tags": ["url-encoded"],
344
- "source": "P",
345
- "target": "R",
346
- "command": {
347
- "type": "keep"
348
- }
338
+ "provider": "./propagators/encode-uri/encode-uri-component.js"
349
339
  },
350
340
  "process.__add": {
351
341
  "enabled": true,
@@ -1371,6 +1371,11 @@
1371
1371
  "type": "http",
1372
1372
  "provider": "./sinks/hapi-16-xss"
1373
1373
  },
1374
+ "reflected-xss_dustjs-linkedin": {
1375
+ "enabled": true,
1376
+ "type": "http",
1377
+ "provider": "./sinks/dustjs-linkedin-xss"
1378
+ },
1374
1379
  "reflected-xss": {
1375
1380
  "enabled": true,
1376
1381
  "type": "hook",
@@ -233,6 +233,16 @@
233
233
  "methodName": "escape",
234
234
  "isModule": true
235
235
  },
236
+ "dust.escapeHtml": {
237
+ "moduleName": "dustjs-linkedin",
238
+ "methodName": "escapeHtml",
239
+ "isModule": true
240
+ },
241
+ "dust.escapeJs": {
242
+ "moduleName": "dustjs-linkedin",
243
+ "methodName": "escapeJs",
244
+ "isModule": true
245
+ },
236
246
  "express.response.send": {
237
247
  "moduleName": "express",
238
248
  "version": ">=4.0.0",
@@ -1307,6 +1317,11 @@
1307
1317
  "moduleName": "node-serialize",
1308
1318
  "methodName": "unserialize",
1309
1319
  "isModule": true
1320
+ },
1321
+ "dustjs-linkedin": {
1322
+ "moduleName": "dustjs-linkedin",
1323
+ "methodName": "pipe",
1324
+ "isModule": true
1310
1325
  }
1311
1326
  }
1312
1327
  }
@@ -0,0 +1,22 @@
1
+ /**
2
+ Copyright: 2021 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 { propagate } = require('../template-escape');
17
+
18
+ function handler(data) {
19
+ propagate(data, 'html-encoded', 'dustjs-linkedin.escapeHtml');
20
+ }
21
+
22
+ module.exports.handle = handler;
@@ -0,0 +1,22 @@
1
+ /**
2
+ Copyright: 2021 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 { propagate } = require('../template-escape');
17
+
18
+ function handler(data) {
19
+ propagate(data, 'javascript-encoded', 'dustjs-linkedin.escapeJs');
20
+ }
21
+
22
+ module.exports.handle = handler;
@@ -0,0 +1,22 @@
1
+ /**
2
+ Copyright: 2021 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 { propagate } = require('../template-escape');
17
+
18
+ function handler(data) {
19
+ propagate(data, 'url-encoded', 'global.encodeURIComponent');
20
+ }
21
+
22
+ module.exports.handle = handler;
@@ -0,0 +1,22 @@
1
+ /**
2
+ Copyright: 2021 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 { propagate } = require('../template-escape');
17
+
18
+ function handler(data) {
19
+ propagate(data, 'weak-url-encoded', 'global.encodeURI');
20
+ }
21
+
22
+ module.exports.handle = handler;
@@ -128,8 +128,6 @@ const generateHookWrappers = (agent, policyNode, key) => {
128
128
  } else {
129
129
  ({ pre, post } = provider.handle);
130
130
  }
131
-
132
- propagatorDescriptor.provider = provider.handle;
133
131
  } else {
134
132
  // generic propagator
135
133
  post = new Propagator(agent, propagatorDescriptor);
@@ -51,18 +51,9 @@ function instrumentJoiValues(values) {
51
51
  return;
52
52
  }
53
53
 
54
- const resultIsString = _.isString(result.value);
55
- const argIsString = _.isString(value);
56
-
57
54
  if (result.ref) {
58
- // result === false means ref resolution failed
59
- if (resultIsString && argIsString) {
60
- const resolvedTrackData = tracker.getData(result.value);
61
- const refTrackData = tracker.getData(value);
62
- const handler = getRefHandler(resolvedTrackData, refTrackData);
63
- handler && handler(data, resolvedTrackData, refTrackData);
64
- }
65
- } else if (resultIsString) {
55
+ handler(result.value, value, data);
56
+ } else if (_.isString(result.value)) {
66
57
  // use case is .valid() - safe
67
58
  tracker.untrack(result.value);
68
59
  }
@@ -70,6 +61,30 @@ function instrumentJoiValues(values) {
70
61
  });
71
62
  }
72
63
 
64
+ const stringHandler = (resultValue, argValue, data) => {
65
+ const resultIsString = _.isString(resultValue);
66
+ const argIsString = _.isString(argValue);
67
+
68
+ if (resultIsString && argIsString) {
69
+ const resolvedTrackData = tracker.getData(resultValue);
70
+ const refTrackData = tracker.getData(argValue);
71
+ const handler = getRefHandler(resolvedTrackData, refTrackData);
72
+ handler && handler(data, resolvedTrackData, refTrackData);
73
+ }
74
+ };
75
+
76
+ const handler = (resultValue, argValue, data) => {
77
+ if (_.isString(resultValue)) {
78
+ return stringHandler(resultValue, argValue, data);
79
+ }
80
+
81
+ if (_.isObject(resultValue)) {
82
+ for (const [key, value] of Object.entries(resultValue)) {
83
+ handler(value, argValue[key], data);
84
+ }
85
+ }
86
+ };
87
+
73
88
  /**
74
89
  * Depending on which values are tracked, ref and/or target, returns the
75
90
  * appropriate function to handle the scenario.
@@ -0,0 +1,22 @@
1
+ /**
2
+ Copyright: 2021 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 { propagate } = require('../template-escape');
17
+
18
+ function handler(data) {
19
+ propagate(data, 'html-encoded', 'mustache.escape');
20
+ }
21
+
22
+ module.exports.handle = handler;
@@ -19,7 +19,6 @@ const TagRange = require('../../models/tag-range');
19
19
  const tagRangeUtil = require('../../models/tag-range/util');
20
20
  const tracker = require('../../../tracker');
21
21
  const path = require('path');
22
- let trackingSeparators;
23
22
 
24
23
  /**
25
24
  * Splits string by separator
@@ -31,8 +30,9 @@ let trackingSeparators;
31
30
  */
32
31
  function splitString(str, win32) {
33
32
  // windows treats both (forward and backward) slashes as separators
34
- const posixRegEx = trackingSeparators ? /(?=\/)/g : /(\/)/g;
35
- const win32RegEx = trackingSeparators ? /(?=[/\\])/g : /[/\\]/g;
33
+ const posixRegEx = /(?=\/)/g;
34
+ const win32RegEx = /(?=[/\\])/g;
35
+
36
36
  if (win32) {
37
37
  str = str.replace(/\//g, '\\').split(win32RegEx);
38
38
  } else {
@@ -55,29 +55,26 @@ function splitString(str, win32) {
55
55
  *
56
56
  * @param {Object} meta object of metadata from result/arg
57
57
  * @param {(segmentOffset: number) => boolean} meta.evaluator expression to determine the proper position of segment in string
58
- * @param {number} meta.offset offset of full arg from result of path.resolve
59
58
  * @param {string} meta.str result from path.resolve or arg
59
+ * @param {number} offset offset of full arg from result of path.resolve
60
60
  * @param {string} segment path segment from one of the args to path.resolve
61
61
  */
62
- function getSegmentOffset({ str, offset, evaluator }, segment, win32) {
62
+ function getSegmentOffset({ str, evaluator }, offset, segment, win32) {
63
63
  // as the segments in each arg and in the result get traversed,
64
64
  // winnow away the string to ensure we are finding the proper
65
65
  // segment within the path
66
66
  const substr = str.substring(offset);
67
67
  let segmentOffset = substr.indexOf(segment);
68
68
  // If tracking separators, evaluator will fail on extensions
69
- if (trackingSeparators && substr === `.${segment}`) {
69
+ if (substr === `.${segment}`) {
70
70
  return segmentOffset + offset;
71
71
  }
72
72
  // Adjust offset if the segment does not start with a separator
73
73
  const { sep } = path[win32 ? 'win32' : 'posix'];
74
- if (
75
- trackingSeparators &&
76
- substr.startsWith(sep) &&
77
- !segment.startsWith(sep)
78
- ) {
74
+ if (substr.startsWith(sep) && !segment.startsWith(sep)) {
79
75
  segmentOffset--;
80
76
  }
77
+
81
78
  return evaluator(segmentOffset) ? segmentOffset + offset : -1;
82
79
  }
83
80
 
@@ -90,7 +87,12 @@ function getSegmentOffset({ str, offset, evaluator }, segment, win32) {
90
87
  */
91
88
  function isWithinTag(str, start, tag) {
92
89
  const stop = str.length - 1 + start;
93
- return tag.overlaps({ start, stop, tag: tag.tag });
90
+
91
+ return (
92
+ (stop <= tag.stop && stop >= tag.start) ||
93
+ (start >= tag.start && start <= tag.stop) ||
94
+ (tag.start >= start && tag.start <= stop)
95
+ );
94
96
  }
95
97
 
96
98
  /**
@@ -103,6 +105,7 @@ function isWithinTag(str, start, tag) {
103
105
  */
104
106
  function adjustStart({ tag, argOffset, offset }) {
105
107
  const start = tag.start - argOffset;
108
+
106
109
  return start > 0 ? start + offset : offset;
107
110
  }
108
111
 
@@ -154,16 +157,13 @@ function maybeUpdateResultMeta({ index, arg, resultMeta }) {
154
157
  }
155
158
 
156
159
  /**
157
- * Encapsulates moving the resultOffset based on a segment
160
+ * Calculates a new offset based on a segment
158
161
  *
159
- * @param {Object} resultMeta metadata from result of path.resolve
160
- * @param {Object} argMeta metadata for a given argument
162
+ * @param {Object} offset offset of the object
161
163
  * @param {string} segment path segment from one of the args to path.resolve
162
164
  */
163
- function calculateNewOffset(resultMeta, argMeta, segment) {
164
- const separatorOffset = trackingSeparators ? 0 : 1;
165
- resultMeta.offset = resultMeta.offset + segment.length + separatorOffset;
166
- argMeta.offset = argMeta.offset + segment.length + separatorOffset;
165
+ function calculateNewOffset(offset, segment) {
166
+ return offset + segment.length;
167
167
  }
168
168
 
169
169
  /**
@@ -173,10 +173,89 @@ function calculateNewOffset(resultMeta, argMeta, segment) {
173
173
  * @param {string} segment path segment to check
174
174
  * @return {boolean}
175
175
  */
176
- function applicableSegment(segment) {
176
+ function isApplicableSegment(segment) {
177
177
  return segment !== '.' && segment !== '..' && segment !== '';
178
178
  }
179
179
 
180
+ /**
181
+ * Filters invalid segments like those that are not part of the end result.
182
+ * It also calculates additional offset.
183
+ *
184
+ * @param {*} segments splitted path into segments
185
+ * @param {Object} resultMeta metadata from result of path.resolve
186
+ * @param {Object} data the data object of the propagate function
187
+ * @param {number} index the iteration index
188
+ * @param {boolean} win32 indicating win32 to replace `/` with `\'
189
+ */
190
+ function filterSegmentsAndCalculateOffset(
191
+ segments,
192
+ resultMeta,
193
+ data,
194
+ index,
195
+ win32
196
+ ) {
197
+ const { sep } = path[win32 ? 'win32' : 'posix'];
198
+ const isFirstIteration = index === 0;
199
+ const validSegments = [];
200
+ let additionalOffset = 0;
201
+
202
+ segments.reduce(
203
+ // eslint-disable-next-line complexity
204
+ (accumulator, segment) => {
205
+ let hasChange = false;
206
+ if (!isApplicableSegment(segment)) return accumulator;
207
+
208
+ if (!accumulator.isModified && !isFirstIteration) {
209
+ const previousArg = data.args[index - 1];
210
+
211
+ const sepAdded = !segment.startsWith(sep) && !previousArg.endsWith(sep);
212
+ const sepTaken = segment.startsWith(sep) && previousArg.endsWith(sep);
213
+ accumulator.isModified = sepAdded || sepTaken;
214
+ hasChange = sepAdded || sepTaken;
215
+ additionalOffset = hasChange ? (sepAdded ? 1 : -1) : 0;
216
+ }
217
+
218
+ const offset = getSegmentOffset(
219
+ resultMeta,
220
+ accumulator.offset + additionalOffset + accumulator.fileDotSeparator,
221
+ segment,
222
+ win32
223
+ );
224
+
225
+ // no reason to proceed if segment is not in final result
226
+ if (offset === -1) {
227
+ if (hasChange) {
228
+ additionalOffset = 0;
229
+ accumulator.isModified = false;
230
+ }
231
+
232
+ return accumulator;
233
+ }
234
+
235
+ // in case we have a file we need to increment the offset
236
+ if (resultMeta.str[offset + segment.length] === '.') {
237
+ accumulator.fileDotSeparator = 1;
238
+ }
239
+
240
+ validSegments.push(segment);
241
+ accumulator.offset = calculateNewOffset(accumulator.offset, segment);
242
+ accumulator.isModified = true;
243
+
244
+ return accumulator;
245
+ },
246
+ {
247
+ offset: resultMeta.offset,
248
+ fileDotSeparator: 0,
249
+ isModified: false
250
+ }
251
+ );
252
+
253
+ return {
254
+ additionalOffset,
255
+ segments: validSegments
256
+ };
257
+ }
258
+
180
259
  /**
181
260
  * Moves the tag ranges properly based on if a path segment exists in the
182
261
  * result
@@ -184,43 +263,66 @@ function applicableSegment(segment) {
184
263
  * @param {Object} resultMeta metadata for the result
185
264
  * @param {Object} argMeta meta for a given argument
186
265
  * @param {boolean} win32 indicating win32 to replace `/` with `\'
266
+ * @param {Object} data the data object of the propagate function
267
+ * @param {number} index the iteration index
187
268
  */
188
- function adjustTagsToPart(resultMeta, argMeta, win32) {
269
+ function adjustTagsToPart(resultMeta, argMeta, win32, data, index) {
189
270
  const { str, tagRanges } = argMeta;
190
- const segments = splitString(str, win32);
191
- const newTags = [];
192
- segments.forEach((segment) => {
193
- if (!applicableSegment(segment)) {
194
- return;
195
- }
196
- const offset = getSegmentOffset(resultMeta, segment, win32);
197
- // no reason to proceed if segment is not in final result
198
- if (offset === -1) {
199
- return;
200
- }
271
+ const { segments, additionalOffset } = filterSegmentsAndCalculateOffset(
272
+ splitString(str, win32),
273
+ resultMeta,
274
+ data,
275
+ index,
276
+ win32
277
+ );
278
+
279
+ // updating the offset
280
+ resultMeta.offset += additionalOffset;
281
+
282
+ return segments.reduce((newTags, segment) => {
283
+ const offset = getSegmentOffset(
284
+ resultMeta,
285
+ resultMeta.offset,
286
+ segment,
287
+ win32
288
+ );
289
+
290
+ const argOffset = getSegmentOffset(argMeta, argMeta.offset, segment, win32);
201
291
 
202
- const argOffset = getSegmentOffset(argMeta, segment, win32);
292
+ // updating the offset
293
+ resultMeta.offset = calculateNewOffset(resultMeta.offset, segment);
294
+ argMeta.offset = calculateNewOffset(argMeta.offset, segment);
203
295
 
204
- calculateNewOffset(resultMeta, argMeta, segment);
296
+ // in case we have a file we need to increment the offset
297
+ if (resultMeta.str[offset + segment.length] === '.') {
298
+ resultMeta.offset += 1;
299
+ }
205
300
 
206
301
  tagRanges.forEach((tag) => {
207
- if (isWithinTag(segment, argOffset, tag)) {
208
- const start = adjustStart({ tag, argOffset, offset });
209
- const stop = adjustStop({ tag, argOffset, offset, segment });
302
+ if (!isWithinTag(segment, argOffset, tag)) return;
210
303
 
211
- newTags.push(new TagRange(start, stop, tag.tag));
212
- }
304
+ const start = adjustStart({
305
+ tag,
306
+ argOffset,
307
+ offset
308
+ });
309
+ const stop = adjustStop({
310
+ tag,
311
+ argOffset,
312
+ offset,
313
+ segment
314
+ });
315
+
316
+ newTags.push(new TagRange(start, stop, tag.tag));
213
317
  });
214
- });
215
318
 
216
- return newTags;
319
+ return newTags;
320
+ }, []);
217
321
  }
218
322
 
219
- function propagate({ resultMeta, data, win32, trackSeparators }) {
220
- trackingSeparators = trackSeparators;
323
+ function propagate({ resultMeta, data, win32 }) {
221
324
  if (!resultMeta.evaluator) {
222
- resultMeta.evaluator = (segmentOffset) =>
223
- segmentOffset === (trackingSeparators ? 0 : 1);
325
+ resultMeta.evaluator = (segmentOffset) => segmentOffset === 0;
224
326
  }
225
327
 
226
328
  data.args.forEach((arg, index) => {
@@ -234,13 +336,20 @@ function propagate({ resultMeta, data, win32, trackSeparators }) {
234
336
  tagRanges: argData.tracked ? argData.tagRanges : []
235
337
  };
236
338
 
237
- const targetTagRanges = adjustTagsToPart(resultMeta, argMeta, win32);
339
+ const targetTagRanges = adjustTagsToPart(
340
+ resultMeta,
341
+ argMeta,
342
+ win32,
343
+ data,
344
+ index
345
+ );
238
346
 
239
347
  if (targetTagRanges.length > 0) {
240
348
  resultMeta.parents.push(argData.event);
241
349
  tagRangeUtil.addAllInPlace(resultMeta.tagRanges, targetTagRanges);
242
350
  }
243
351
  });
352
+
244
353
  trackResult(resultMeta, data);
245
354
  }
246
355
 
@@ -31,7 +31,11 @@ function hookPath(path) {
31
31
  resultMeta.offset = 0;
32
32
  resultMeta.evaluator = (segmentOffset) => segmentOffset === 0;
33
33
  }
34
- propagate({ data, resultMeta, win32: os === 'win32' });
34
+ propagate({
35
+ data,
36
+ resultMeta,
37
+ win32: os === 'win32'
38
+ });
35
39
  }
36
40
  });
37
41
  }
@@ -46,8 +46,7 @@ module.exports.handle = function handle() {
46
46
  propagate({
47
47
  resultMeta,
48
48
  data,
49
- win32: os === 'win32',
50
- trackSeparators: true
49
+ win32: os === 'win32'
51
50
  });
52
51
  }
53
52
  });