@contrast/agent 4.4.1 → 4.6.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 (156) 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/hapi/route-coverage.js +3 -3
  6. package/lib/assess/membrane/index.js +2 -8
  7. package/lib/assess/membrane/source-membrane.js +3 -4
  8. package/lib/assess/models/base-event.js +2 -2
  9. package/lib/assess/models/call-context.js +0 -3
  10. package/lib/assess/policy/propagators.json +20 -0
  11. package/lib/assess/policy/signatures.json +103 -0
  12. package/lib/assess/propagators/path/common.js +165 -36
  13. package/lib/assess/propagators/path/join.js +5 -1
  14. package/lib/assess/propagators/path/normalize.js +5 -1
  15. package/lib/assess/propagators/path/resolve.js +11 -2
  16. package/lib/assess/response-scanning/autocomplete-missing.js +0 -2
  17. package/lib/assess/response-scanning/parameter-pollution.js +0 -2
  18. package/lib/core/arch-components/dynamodb.js +1 -2
  19. package/lib/core/arch-components/dynamodbv3.js +44 -0
  20. package/lib/core/arch-components/index.js +1 -0
  21. package/lib/core/async-storage/hooks/bluebird.js +20 -0
  22. package/lib/core/config/options.js +3 -2
  23. package/lib/core/express/utils.js +1 -1
  24. package/lib/core/logger/debug-logger.js +15 -17
  25. package/lib/core/stacktrace.js +3 -4
  26. package/lib/feature-set.js +2 -1
  27. package/lib/hooks/encoding.js +1 -1
  28. package/lib/hooks/frameworks/base.js +8 -2
  29. package/lib/hooks/frameworks/http.js +23 -16
  30. package/lib/hooks/frameworks/http2.js +73 -0
  31. package/lib/hooks/frameworks/index.js +8 -3
  32. package/lib/hooks/http.js +112 -128
  33. package/lib/hooks/patcher.js +10 -12
  34. package/lib/hooks/require.js +16 -22
  35. package/lib/instrumentation.js +0 -3
  36. package/lib/protect/analysis/aho-corasick.js +13 -30
  37. package/lib/protect/rules/cmd-injection-command-backdoors/backdoor-detector.js +3 -3
  38. package/lib/protect/rules/signatures/reflected-xss/helpers/function-call.js +1 -1
  39. package/lib/protect/rules/xss/helpers/function-call.js +1 -1
  40. package/lib/util/clean-stack.js +1 -1
  41. package/lib/util/clean-string/brackets.js +3 -3
  42. package/lib/util/clean-string/concatenations.js +1 -1
  43. package/lib/util/clean-string/util.js +1 -2
  44. package/lib/util/ip-analyzer.js +1 -1
  45. package/lib/util/some.js +27 -0
  46. package/lib/util/xml-analyzer/external-entity-finder.js +1 -1
  47. package/package.json +14 -15
  48. package/lib/hooks/frameworks/https.js +0 -42
  49. package/node_modules/bindings/LICENSE.md +0 -22
  50. package/node_modules/bindings/README.md +0 -98
  51. package/node_modules/bindings/bindings.js +0 -221
  52. package/node_modules/bindings/package.json +0 -32
  53. package/node_modules/file-uri-to-path/.npmignore +0 -1
  54. package/node_modules/file-uri-to-path/.travis.yml +0 -30
  55. package/node_modules/file-uri-to-path/History.md +0 -21
  56. package/node_modules/file-uri-to-path/LICENSE +0 -20
  57. package/node_modules/file-uri-to-path/README.md +0 -74
  58. package/node_modules/file-uri-to-path/index.d.ts +0 -2
  59. package/node_modules/file-uri-to-path/index.js +0 -66
  60. package/node_modules/file-uri-to-path/package.json +0 -36
  61. package/node_modules/file-uri-to-path/test/test.js +0 -24
  62. package/node_modules/file-uri-to-path/test/tests.json +0 -13
  63. package/node_modules/glossy/LICENSE +0 -19
  64. package/node_modules/glossy/README.md +0 -129
  65. package/node_modules/glossy/index.js +0 -12
  66. package/node_modules/glossy/lib/glossy/parse.js +0 -520
  67. package/node_modules/glossy/lib/glossy/produce.js +0 -459
  68. package/node_modules/glossy/package.json +0 -47
  69. package/node_modules/glossy/test/decide.js +0 -7
  70. package/node_modules/glossy/test/decode_pri.js +0 -24
  71. package/node_modules/glossy/test/parse_3164.js +0 -104
  72. package/node_modules/glossy/test/parse_5424.js +0 -106
  73. package/node_modules/glossy/test/parse_5848.js +0 -40
  74. package/node_modules/glossy/test/parse_8601.js +0 -14
  75. package/node_modules/glossy/test/parse_rfc3339.js +0 -9
  76. package/node_modules/glossy/test/produce.js +0 -162
  77. package/node_modules/glossy/test/runner.js +0 -40
  78. package/node_modules/glossy/test/structure_data.js +0 -24
  79. package/node_modules/nan/CHANGELOG.md +0 -537
  80. package/node_modules/nan/LICENSE.md +0 -13
  81. package/node_modules/nan/README.md +0 -455
  82. package/node_modules/nan/doc/asyncworker.md +0 -146
  83. package/node_modules/nan/doc/buffers.md +0 -54
  84. package/node_modules/nan/doc/callback.md +0 -76
  85. package/node_modules/nan/doc/converters.md +0 -41
  86. package/node_modules/nan/doc/errors.md +0 -226
  87. package/node_modules/nan/doc/json.md +0 -62
  88. package/node_modules/nan/doc/maybe_types.md +0 -583
  89. package/node_modules/nan/doc/methods.md +0 -664
  90. package/node_modules/nan/doc/new.md +0 -147
  91. package/node_modules/nan/doc/node_misc.md +0 -123
  92. package/node_modules/nan/doc/object_wrappers.md +0 -263
  93. package/node_modules/nan/doc/persistent.md +0 -296
  94. package/node_modules/nan/doc/scopes.md +0 -73
  95. package/node_modules/nan/doc/script.md +0 -38
  96. package/node_modules/nan/doc/string_bytes.md +0 -62
  97. package/node_modules/nan/doc/v8_internals.md +0 -199
  98. package/node_modules/nan/doc/v8_misc.md +0 -85
  99. package/node_modules/nan/include_dirs.js +0 -1
  100. package/node_modules/nan/nan.h +0 -2898
  101. package/node_modules/nan/nan_callbacks.h +0 -88
  102. package/node_modules/nan/nan_callbacks_12_inl.h +0 -514
  103. package/node_modules/nan/nan_callbacks_pre_12_inl.h +0 -520
  104. package/node_modules/nan/nan_converters.h +0 -72
  105. package/node_modules/nan/nan_converters_43_inl.h +0 -68
  106. package/node_modules/nan/nan_converters_pre_43_inl.h +0 -42
  107. package/node_modules/nan/nan_define_own_property_helper.h +0 -29
  108. package/node_modules/nan/nan_implementation_12_inl.h +0 -430
  109. package/node_modules/nan/nan_implementation_pre_12_inl.h +0 -263
  110. package/node_modules/nan/nan_json.h +0 -166
  111. package/node_modules/nan/nan_maybe_43_inl.h +0 -356
  112. package/node_modules/nan/nan_maybe_pre_43_inl.h +0 -268
  113. package/node_modules/nan/nan_new.h +0 -340
  114. package/node_modules/nan/nan_object_wrap.h +0 -156
  115. package/node_modules/nan/nan_persistent_12_inl.h +0 -132
  116. package/node_modules/nan/nan_persistent_pre_12_inl.h +0 -242
  117. package/node_modules/nan/nan_private.h +0 -73
  118. package/node_modules/nan/nan_string_bytes.h +0 -305
  119. package/node_modules/nan/nan_typedarray_contents.h +0 -96
  120. package/node_modules/nan/nan_weak.h +0 -437
  121. package/node_modules/nan/package.json +0 -41
  122. package/node_modules/nan/tools/1to2.js +0 -412
  123. package/node_modules/nan/tools/README.md +0 -14
  124. package/node_modules/nan/tools/package.json +0 -19
  125. package/node_modules/unix-dgram/LICENSE +0 -13
  126. package/node_modules/unix-dgram/README.md +0 -107
  127. package/node_modules/unix-dgram/binding.gyp +0 -20
  128. package/node_modules/unix-dgram/build/Makefile +0 -324
  129. package/node_modules/unix-dgram/build/Release/.deps/Release/obj.target/unix_dgram/src/unix_dgram.o.d +0 -58
  130. package/node_modules/unix-dgram/build/Release/.deps/Release/obj.target/unix_dgram.node.d +0 -1
  131. package/node_modules/unix-dgram/build/Release/.deps/Release/unix_dgram.node.d +0 -1
  132. package/node_modules/unix-dgram/build/Release/obj.target/unix_dgram/src/unix_dgram.o +0 -0
  133. package/node_modules/unix-dgram/build/Release/obj.target/unix_dgram.node +0 -0
  134. package/node_modules/unix-dgram/build/Release/unix_dgram.node +0 -0
  135. package/node_modules/unix-dgram/build/binding.Makefile +0 -6
  136. package/node_modules/unix-dgram/build/config.gypi +0 -213
  137. package/node_modules/unix-dgram/build/unix_dgram.target.mk +0 -159
  138. package/node_modules/unix-dgram/lib/unix_dgram.js +0 -168
  139. package/node_modules/unix-dgram/package.json +0 -36
  140. package/node_modules/unix-dgram/src/unix_dgram.cc +0 -404
  141. package/node_modules/unix-dgram/src/win_dummy.cc +0 -7
  142. package/node_modules/unix-dgram/test/test-connect-callback.js +0 -68
  143. package/node_modules/unix-dgram/test/test-connect.js +0 -53
  144. package/node_modules/unix-dgram/test/test-dgram-unix.js +0 -58
  145. package/node_modules/unix-dgram/test/test-send-error.js +0 -26
  146. package/node_modules/winston-syslog/.eslintrc +0 -7
  147. package/node_modules/winston-syslog/.travis.yml +0 -14
  148. package/node_modules/winston-syslog/CHANGELOG.md +0 -9
  149. package/node_modules/winston-syslog/LICENSE +0 -20
  150. package/node_modules/winston-syslog/README.md +0 -135
  151. package/node_modules/winston-syslog/lib/utils.js +0 -26
  152. package/node_modules/winston-syslog/lib/winston-syslog.js +0 -385
  153. package/node_modules/winston-syslog/package.json +0 -56
  154. package/node_modules/winston-syslog/test/format-test.js +0 -122
  155. package/node_modules/winston-syslog/test/syslog-test.js +0 -95
  156. package/node_modules/winston-syslog/test/unix-connect-test.js +0 -133
@@ -30,14 +30,21 @@ const path = require('path');
30
30
  */
31
31
  function splitString(str, win32) {
32
32
  // windows treats both (forward and backward) slashes as separators
33
- str = str.split(win32 ? /[/\\]/ : /\//);
33
+ const posixRegEx = /(?=\/)/g;
34
+ const win32RegEx = /(?=[/\\])/g;
35
+
36
+ if (win32) {
37
+ str = str.replace(/\//g, '\\').split(win32RegEx);
38
+ } else {
39
+ str = str.split(posixRegEx);
40
+ }
34
41
  if (str.length) {
35
42
  // if there is an extension split it out.
36
43
  const ix = str[str.length - 1].lastIndexOf('.');
37
44
  if (ix > 0) {
38
45
  const last = str[str.length - 1];
39
46
  str[str.length - 1] = last.substr(0, ix);
40
- str.push(last.substr(ix));
47
+ str.push(last.substr(ix + 1));
41
48
  }
42
49
  }
43
50
  return str;
@@ -48,16 +55,25 @@ function splitString(str, win32) {
48
55
  *
49
56
  * @param {Object} meta object of metadata from result/arg
50
57
  * @param {(segmentOffset: number) => boolean} meta.evaluator expression to determine the proper position of segment in string
51
- * @param {number} meta.offset offset of full arg from result of path.resolve
52
58
  * @param {string} meta.str result from path.resolve or arg
59
+ * @param {number} offset offset of full arg from result of path.resolve
53
60
  * @param {string} segment path segment from one of the args to path.resolve
54
61
  */
55
- function getSegmentOffset({ str, offset, evaluator }, segment) {
62
+ function getSegmentOffset({ str, evaluator }, offset, segment, win32) {
56
63
  // as the segments in each arg and in the result get traversed,
57
64
  // winnow away the string to ensure we are finding the proper
58
65
  // segment within the path
59
66
  const substr = str.substring(offset);
60
- const segmentOffset = substr.indexOf(segment);
67
+ let segmentOffset = substr.indexOf(segment);
68
+ // If tracking separators, evaluator will fail on extensions
69
+ if (substr === `.${segment}`) {
70
+ return segmentOffset + offset;
71
+ }
72
+ // Adjust offset if the segment does not start with a separator
73
+ const { sep } = path[win32 ? 'win32' : 'posix'];
74
+ if (substr.startsWith(sep) && !segment.startsWith(sep)) {
75
+ segmentOffset--;
76
+ }
61
77
 
62
78
  return evaluator(segmentOffset) ? segmentOffset + offset : -1;
63
79
  }
@@ -71,7 +87,12 @@ function getSegmentOffset({ str, offset, evaluator }, segment) {
71
87
  */
72
88
  function isWithinTag(str, start, tag) {
73
89
  const stop = str.length - 1 + start;
74
- 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
+ );
75
96
  }
76
97
 
77
98
  /**
@@ -84,6 +105,7 @@ function isWithinTag(str, start, tag) {
84
105
  */
85
106
  function adjustStart({ tag, argOffset, offset }) {
86
107
  const start = tag.start - argOffset;
108
+
87
109
  return start > 0 ? start + offset : offset;
88
110
  }
89
111
 
@@ -135,15 +157,13 @@ function maybeUpdateResultMeta({ index, arg, resultMeta }) {
135
157
  }
136
158
 
137
159
  /**
138
- * Encapsulates moving the resultOffset based on a segment
160
+ * Calculates a new offset based on a segment
139
161
  *
140
- * @param {Object} resultMeta metadata from result of path.resolve
141
- * @param {Object} argMeta metadata for a given argument
162
+ * @param {Object} offset offset of the object
142
163
  * @param {string} segment path segment from one of the args to path.resolve
143
164
  */
144
- function calculateNewOffset(resultMeta, argMeta, segment) {
145
- resultMeta.offset = resultMeta.offset + segment.length + 1;
146
- argMeta.offset = argMeta.offset + segment.length + 1;
165
+ function calculateNewOffset(offset, segment) {
166
+ return offset + segment.length;
147
167
  }
148
168
 
149
169
  /**
@@ -153,10 +173,89 @@ function calculateNewOffset(resultMeta, argMeta, segment) {
153
173
  * @param {string} segment path segment to check
154
174
  * @return {boolean}
155
175
  */
156
- function applicableSegment(segment) {
176
+ function isApplicableSegment(segment) {
157
177
  return segment !== '.' && segment !== '..' && segment !== '';
158
178
  }
159
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
+
160
259
  /**
161
260
  * Moves the tag ranges properly based on if a path segment exists in the
162
261
  * result
@@ -164,42 +263,66 @@ function applicableSegment(segment) {
164
263
  * @param {Object} resultMeta metadata for the result
165
264
  * @param {Object} argMeta meta for a given argument
166
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
167
268
  */
168
- function adjustTagsToPart(resultMeta, argMeta, win32) {
269
+ function adjustTagsToPart(resultMeta, argMeta, win32, data, index) {
169
270
  const { str, tagRanges } = argMeta;
170
- const segments = splitString(str, win32);
171
- const newTags = [];
172
- segments.forEach((segment) => {
173
- if (!applicableSegment(segment)) {
174
- return;
175
- }
271
+ const { segments, additionalOffset } = filterSegmentsAndCalculateOffset(
272
+ splitString(str, win32),
273
+ resultMeta,
274
+ data,
275
+ index,
276
+ win32
277
+ );
176
278
 
177
- const offset = getSegmentOffset(resultMeta, segment);
178
- // no reason to proceed if segment is not in final result
179
- if (offset === -1) {
180
- return;
181
- }
279
+ // updating the offset
280
+ resultMeta.offset += additionalOffset;
182
281
 
183
- const argOffset = getSegmentOffset(argMeta, segment);
282
+ return segments.reduce((newTags, segment) => {
283
+ const offset = getSegmentOffset(
284
+ resultMeta,
285
+ resultMeta.offset,
286
+ segment,
287
+ win32
288
+ );
184
289
 
185
- calculateNewOffset(resultMeta, argMeta, segment);
290
+ const argOffset = getSegmentOffset(argMeta, argMeta.offset, segment, win32);
291
+
292
+ // updating the offset
293
+ resultMeta.offset = calculateNewOffset(resultMeta.offset, segment);
294
+ argMeta.offset = calculateNewOffset(argMeta.offset, segment);
295
+
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
+ }
186
300
 
187
301
  tagRanges.forEach((tag) => {
188
- if (isWithinTag(segment, argOffset, tag)) {
189
- const start = adjustStart({ tag, argOffset, offset });
190
- const stop = adjustStop({ tag, argOffset, offset, segment });
302
+ if (!isWithinTag(segment, argOffset, tag)) return;
191
303
 
192
- newTags.push(new TagRange(start, stop, tag.tag));
193
- }
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));
194
317
  });
195
- });
196
318
 
197
- return newTags;
319
+ return newTags;
320
+ }, []);
198
321
  }
199
322
 
200
323
  function propagate({ resultMeta, data, win32 }) {
201
324
  if (!resultMeta.evaluator) {
202
- resultMeta.evaluator = (segmentOffset) => segmentOffset === 1;
325
+ resultMeta.evaluator = (segmentOffset) => segmentOffset === 0;
203
326
  }
204
327
 
205
328
  data.args.forEach((arg, index) => {
@@ -213,7 +336,13 @@ function propagate({ resultMeta, data, win32 }) {
213
336
  tagRanges: argData.tracked ? argData.tagRanges : []
214
337
  };
215
338
 
216
- const targetTagRanges = adjustTagsToPart(resultMeta, argMeta, win32);
339
+ const targetTagRanges = adjustTagsToPart(
340
+ resultMeta,
341
+ argMeta,
342
+ win32,
343
+ data,
344
+ index
345
+ );
217
346
 
218
347
  if (targetTagRanges.length > 0) {
219
348
  resultMeta.parents.push(argData.event);
@@ -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
  }
@@ -43,7 +43,11 @@ module.exports.handle = function handle() {
43
43
  resultMeta.evaluator = (segmentOffset) => segmentOffset > -1;
44
44
  }
45
45
 
46
- propagate({ resultMeta, data, win32: os === 'win32' });
46
+ propagate({
47
+ resultMeta,
48
+ data,
49
+ win32: os === 'win32'
50
+ });
47
51
  }
48
52
  });
49
53
  }
@@ -34,7 +34,12 @@ module.exports.handle = function handle() {
34
34
  if (!data.args[0]) return;
35
35
  const resultMeta = getResultMeta(data, 'resolve');
36
36
  const isAbsolute = absolutePath(data.args[0]);
37
- resultMeta.offset = isAbsolute ? 0 : process.cwd().length;
37
+
38
+ /*
39
+ plus one in case isAbsolute is false since our working deretory ends without slash
40
+ example: /Users/John/Documents/Projects/node-agent
41
+ */
42
+ resultMeta.offset = isAbsolute ? 0 : process.cwd().length + 1;
38
43
 
39
44
  // a work-around for paths like C:\Windows
40
45
  if (
@@ -45,7 +50,11 @@ module.exports.handle = function handle() {
45
50
  resultMeta.evaluator = (segmentOffset) => segmentOffset > -1;
46
51
  }
47
52
 
48
- propagate({ data, resultMeta, win32: os === 'win32' });
53
+ propagate({
54
+ data,
55
+ resultMeta,
56
+ win32: os === 'win32'
57
+ });
49
58
  }
50
59
  });
51
60
  }
@@ -42,8 +42,6 @@ module.exports = ({ common }) => {
42
42
  start: 0,
43
43
  end: elements[i].length
44
44
  };
45
- } else {
46
- return;
47
45
  }
48
46
  }
49
47
  };
@@ -34,8 +34,6 @@ module.exports = ({ agent, common: { emitFinding } }) => {
34
34
  attribute: action,
35
35
  html: elements[i]
36
36
  };
37
- } else {
38
- return undefined;
39
37
  }
40
38
  }
41
39
  };
@@ -18,7 +18,6 @@ const agentEmitter = require('../../agent-emitter');
18
18
  const { PATCH_TYPES } = require('../../constants');
19
19
  const ModuleHook = require('../../hooks/require');
20
20
  const patcher = require('../../hooks/patcher');
21
- const _ = require('lodash');
22
21
 
23
22
  ModuleHook.resolve({ name: 'aws-sdk' }, (AWS) => {
24
23
  patcher.patch(AWS.DynamoDB.prototype, 'makeRequest', {
@@ -27,7 +26,7 @@ ModuleHook.resolve({ name: 'aws-sdk' }, (AWS) => {
27
26
  alwaysRun: true,
28
27
  post(ctx) {
29
28
  try {
30
- const endpoint = _.get(this, 'endpoint');
29
+ const { endpoint } = this;
31
30
  agentEmitter.emit('architectureComponent', {
32
31
  vendor: 'DynamoDB',
33
32
  url: new URL(`${endpoint.protocol}//${endpoint.hostname}`).toString(),
@@ -0,0 +1,44 @@
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 logger = require('../logger')('contrast:arch-component');
17
+ const agentEmitter = require('../../agent-emitter');
18
+ const { PATCH_TYPES } = require('../../constants');
19
+ const ModuleHook = require('../../hooks/require');
20
+ const patcher = require('../../hooks/patcher');
21
+
22
+ function emitArchitectureComponent(endpoint) {
23
+ agentEmitter.emit('architectureComponent', {
24
+ vendor: 'DynamoDB',
25
+ url: new URL(`${endpoint.protocol}//${endpoint.hostname}`).toString(),
26
+ remoteHost: '',
27
+ remotePort: endpoint.port
28
+ });
29
+ }
30
+
31
+ ModuleHook.resolve({ name: '@aws-sdk/client-dynamodb' }, (AWS) => {
32
+ patcher.patch(AWS.DynamoDBClient.prototype, 'send', {
33
+ name: 'DynamoDBv3.arch_component',
34
+ patchType: PATCH_TYPES.ARCH_COMPONENT,
35
+ alwaysRun: true,
36
+ post(ctx) {
37
+ try {
38
+ this.config.endpoint().then(emitArchitectureComponent);
39
+ } catch (err) {
40
+ logger.warn('unable to report DynamoDB architecture component\n', err);
41
+ }
42
+ }
43
+ });
44
+ });
@@ -17,3 +17,4 @@ require('./mysql');
17
17
  require('./sqlite3');
18
18
  require('./postgres');
19
19
  require('./dynamodb');
20
+ require('./dynamodbv3');
@@ -23,6 +23,26 @@ module.exports = function() {
23
23
  moduleHook.resolve({ name: 'bluebird' }, (bluebird) => {
24
24
  module.exports.patchConfig(bluebird);
25
25
  module.exports.patchAddCallbacks(bluebird);
26
+ module.exports.patchGetNewLibraryCopy(bluebird);
27
+ });
28
+ };
29
+
30
+ /**
31
+ * Ensures that new library copies are also instrumented.
32
+ * @param {function} bluebird the library export
33
+ */
34
+ module.exports.patchGetNewLibraryCopy = function(bluebird) {
35
+ if (typeof bluebird.getNewLibraryCopy !== 'function') {
36
+ return;
37
+ }
38
+
39
+ patcher.patch(bluebird, 'getNewLibraryCopy', {
40
+ alwaysRun: true,
41
+ name: 'bluebird.getNewLibraryCopy',
42
+ patchType: PATCH_TYPES.ASYNC_CONTEXT,
43
+ post(data) {
44
+ module.exports.patchAddCallbacks(data.result);
45
+ }
26
46
  });
27
47
  };
28
48
 
@@ -481,9 +481,10 @@ const agent = [
481
481
  {
482
482
  name: 'agent.stack_trace_limit',
483
483
  arg: '<limit>',
484
- default: 10,
484
+ default: 25,
485
485
  fn: parseNum,
486
- desc: 'set limit for stack trace size'
486
+ desc:
487
+ 'set limit for stack trace size (larger limits will improve accuracy but increase memory usage)'
487
488
  },
488
489
  {
489
490
  name: 'agent.polling.app_activity_ms',
@@ -273,7 +273,7 @@ const normalizeSegment = (segment) => {
273
273
  }
274
274
  }
275
275
  } else if (segment instanceof RegExp) {
276
- segment = segment.toString().replace(/^\/?|\/?$/g, '');
276
+ segment = segment.toString().replace(/(^\/?)|(\/?$)/g, '');
277
277
  segment = `/{${segment}}`;
278
278
  } else if (Array.isArray(segment)) {
279
279
  segment = segment.map((s) => `${normalizeSignatureArg(`${s}`)}`);
@@ -314,23 +314,21 @@ class DebugLogFactory {
314
314
  };
315
315
 
316
316
  [...levelNames, 'console'].forEach((level) => {
317
- if (level !== noop) {
318
- if (level === 'console') {
319
- const orig = logger[level].bind(logger);
320
- logger[level] = function(...args) {
321
- return Scopes.runInNoInstrumentationScope(
322
- () => orig(...args),
323
- DEADZONE_NAME
324
- );
325
- };
326
- } else {
327
- logger[level] = function(message, ...splat) {
328
- return Scopes.runInNoInstrumentationScope(
329
- () => logger.log({ level, message, splat }),
330
- DEADZONE_NAME
331
- );
332
- };
333
- }
317
+ if (level === 'console') {
318
+ const orig = logger[level].bind(logger);
319
+ logger[level] = function(...args) {
320
+ return Scopes.runInNoInstrumentationScope(
321
+ () => orig(...args),
322
+ DEADZONE_NAME
323
+ );
324
+ };
325
+ } else {
326
+ logger[level] = function(message, ...splat) {
327
+ return Scopes.runInNoInstrumentationScope(
328
+ () => logger.log({ level, message, splat }),
329
+ DEADZONE_NAME
330
+ );
331
+ };
334
332
  }
335
333
  });
336
334
 
@@ -15,13 +15,13 @@ Copyright: 2021 Contrast Security, Inc
15
15
  'use strict';
16
16
 
17
17
  const semver = require('semver');
18
+ const agent = require('../agent');
18
19
  const process = require('process');
19
20
  const sourceMap = require('../util/source-map');
20
21
  const _isAgentPath = require('../util/is-agent-path');
21
22
 
22
- const STACK_TRACE_LIMIT = 25;
23
+ const STACK_TRACE_LIMIT = agent.config.agent.stack_trace_limit;
23
24
  const EVAL_ORIGIN_REGEX = /\((.*?):(\d+):\d+\)/;
24
- const EJS_EVAL_ORIGIN_REGEX = /(.*\.ejs$)/;
25
25
  const EVENTS_FILE = semver.gte(process.version, '16.0.0')
26
26
  ? 'node:events'
27
27
  : 'events.js';
@@ -146,8 +146,7 @@ class Factory {
146
146
  if (callsite.isEval()) {
147
147
  evalOrigin = Factory.formatFileName(callsite.getEvalOrigin());
148
148
  [, file, lineNumber, columnNumber] =
149
- evalOrigin.match(EVAL_ORIGIN_REGEX) ||
150
- evalOrigin.match(EJS_EVAL_ORIGIN_REGEX);
149
+ evalOrigin.match(EVAL_ORIGIN_REGEX) || evalOrigin.endsWith('.ejs');
151
150
  }
152
151
 
153
152
  file = file || callsite.getFileName();
@@ -160,7 +160,8 @@ class FeatureSet {
160
160
  );
161
161
 
162
162
  return disabledConfig
163
- .split(/\s*,\s*/)
163
+ .split(',')
164
+ .map((c) => c.trim())
164
165
  .some((disabledId) => id === disabledId);
165
166
  }
166
167
 
@@ -31,7 +31,7 @@ module.exports = function() {
31
31
  hardPatchEncoding(Buffer.prototype, 'Buffer', 'lastIndexOf', 0);
32
32
  hardPatchEncoding(Buffer.prototype, 'Buffer', 'includes', 0);
33
33
 
34
- hardPatchEncoding(Buffer, 'Buffer', 'alloc', 0, 1);
34
+ hardPatchEncoding(Buffer, 'Buffer', 'alloc', 0);
35
35
  hardPatchEncoding(Buffer.prototype, 'Buffer', 'fill', 0);
36
36
  };
37
37
 
@@ -22,8 +22,14 @@ const RequestFactory = require('../../reporter/models/utils/request-factory');
22
22
  const CleanStack = require('../../util/clean-stack');
23
23
  const agentEmitter = require('../../agent-emitter');
24
24
 
25
+ /** @typedef {import('../../agent').ContrastAgent} Agent */
26
+
25
27
  class BaseFramework {
26
- // sets options
28
+ /**
29
+ * @param {Object} opts
30
+ * @param {string} opts.id
31
+ * @param {Agent} opts.agent
32
+ */
27
33
  constructor({ id, agent } = {}) {
28
34
  this.id = id;
29
35
  this.agent = agent;
@@ -46,7 +52,7 @@ class BaseFramework {
46
52
  * -- Note --
47
53
  * The current imlementation uses a CleanStack instance in order to generate
48
54
  * a stackframe from which to obtain the information. This can be expensive.
49
- * @returns {String}
55
+ * @returns {string}
50
56
  */
51
57
  getAppCodeLocation() {
52
58
  const limit = Error.stackTraceLimit;
@@ -19,39 +19,41 @@ const moduleHook = require('../require');
19
19
  const patcher = require('../patcher');
20
20
  const { HTTP_EVENTS, PATCH_TYPES } = require('../../constants');
21
21
 
22
- class HttpFramework extends BaseFramework {
23
- constructor(agent, id = 'http') {
24
- super({ id, agent });
25
-
26
- this.init();
27
- }
22
+ /** @typedef {import('../../agent').ContrastAgent} Agent */
23
+ /** @typedef {import('net').Server} Server */
28
24
 
25
+ class HttpFramework extends BaseFramework {
29
26
  /**
30
- * Initialization logic.
27
+ * @param {Agent} agent
28
+ * @param {string} id
31
29
  */
32
- init() {
30
+ constructor(agent, id = 'http') {
31
+ super({ agent, id });
33
32
  moduleHook.resolve({ name: this.id }, this.onRequire.bind(this));
34
33
  }
35
34
 
35
+ /**
36
+ * @template {import('http') | import('https')} T
37
+ * @param {T} xport
38
+ * @returns {T}
39
+ */
36
40
  onRequire(xport) {
37
- const { id } = this;
38
-
39
41
  patcher.patch(xport, 'Server', {
40
- name: `${id}.Server`,
42
+ name: `${this.id}.Server`,
41
43
  patchType: PATCH_TYPES.FRAMEWORK,
42
44
  alwaysRun: true,
43
45
  post: (fnData) => this.handleServerCreate(fnData.args, fnData.result)
44
46
  });
45
47
 
46
48
  patcher.patch(xport, 'createServer', {
47
- name: `${id}.createServer`,
49
+ name: `${this.id}.createServer`,
48
50
  patchType: PATCH_TYPES.FRAMEWORK,
49
51
  alwaysRun: true,
50
52
  post: (fnData) => this.handleServerCreate(fnData.args, fnData.result)
51
53
  });
52
54
 
53
55
  patcher.patch(xport.Server.prototype, 'listen', {
54
- name: `${id}.Server.prototype`,
56
+ name: `${this.id}.Server.prototype`,
55
57
  patchType: PATCH_TYPES.FRAMEWORK,
56
58
  alwaysRun: true,
57
59
  pre: (fnData) => this.handleServerListen(fnData.args, fnData.obj)
@@ -62,6 +64,7 @@ class HttpFramework extends BaseFramework {
62
64
 
63
65
  /**
64
66
  * Emits a listen event if one has not been pubished for the server instance.
67
+ * @param {any[]}
65
68
  * @param {Server} server Server on which `listen` was called
66
69
  */
67
70
  handleServerListen(args, server) {
@@ -70,12 +73,16 @@ class HttpFramework extends BaseFramework {
70
73
 
71
74
  /**
72
75
  * Emits a create event for the new Server instance.
73
- * @param {*[]} args The arguments passed to the Server constructor
76
+ * @param {any[]} args The arguments passed to the Server constructor
74
77
  * @param {Server} server The http Server instance
75
78
  */
76
79
  handleServerCreate(args, server) {
77
- const [callback] = args;
78
- this.emitter.emit(HTTP_EVENTS.SERVER_CREATE, callback, server);
80
+ const [options] = args;
81
+ let [, handler] = args;
82
+ if (typeof options === 'function') {
83
+ handler = options;
84
+ }
85
+ this.emitter.emit(HTTP_EVENTS.SERVER_CREATE, handler, server);
79
86
  }
80
87
  }
81
88