@contrast/agent 4.5.1 → 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 (209) 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 +16 -33
  7. package/lib/assess/models/call-context.js +1 -1
  8. package/lib/assess/policy/propagators.json +19 -21
  9. package/lib/assess/policy/rules.json +7 -2
  10. package/lib/assess/policy/signatures.json +42 -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/dustjs/escape-html.js +22 -0
  17. package/lib/assess/propagators/dustjs/escape-js.js +22 -0
  18. package/lib/assess/propagators/encode-uri/encode-uri-component.js +22 -0
  19. package/lib/assess/propagators/encode-uri/encode-uri.js +22 -0
  20. package/lib/assess/propagators/handlebars-escape-expresssion.js +1 -1
  21. package/lib/assess/propagators/index.js +0 -2
  22. package/lib/assess/propagators/joi/boolean.js +1 -1
  23. package/lib/assess/propagators/joi/expression.js +1 -1
  24. package/lib/assess/propagators/joi/number.js +1 -1
  25. package/lib/assess/propagators/joi/string-base.js +1 -1
  26. package/lib/assess/propagators/joi/string-schema.js +12 -13
  27. package/lib/assess/propagators/joi/values.js +37 -22
  28. package/lib/assess/propagators/manager.js +12 -10
  29. package/lib/assess/propagators/mongoose/helpers.js +20 -0
  30. package/lib/assess/propagators/mongoose/index.js +18 -0
  31. package/lib/assess/propagators/mongoose/map.js +74 -0
  32. package/lib/assess/propagators/mongoose/string.js +104 -0
  33. package/lib/assess/propagators/mustache/escape.js +22 -0
  34. package/lib/assess/propagators/number.js +54 -0
  35. package/lib/assess/propagators/object.js +6 -7
  36. package/lib/assess/propagators/path/basename.js +14 -13
  37. package/lib/assess/propagators/path/common.js +156 -47
  38. package/lib/assess/propagators/path/dirname.js +14 -13
  39. package/lib/assess/propagators/path/extname.js +14 -13
  40. package/lib/assess/propagators/path/join.js +5 -1
  41. package/lib/assess/propagators/path/normalize.js +1 -2
  42. package/lib/assess/propagators/path/parse.js +1 -1
  43. package/lib/assess/propagators/path/relative.js +7 -5
  44. package/lib/assess/propagators/path/resolve.js +11 -2
  45. package/lib/assess/propagators/querystring/escape.js +20 -18
  46. package/lib/assess/propagators/querystring/parse.js +7 -5
  47. package/lib/assess/propagators/querystring/stringify.js +25 -24
  48. package/lib/assess/propagators/querystring/unescape.js +20 -18
  49. package/lib/assess/propagators/sequelize/sql-string-escape.js +1 -1
  50. package/lib/assess/propagators/sequelize/sql-string-format-named-parameters.js +1 -1
  51. package/lib/assess/propagators/sequelize/sql-string-format.js +3 -3
  52. package/lib/assess/propagators/sequelize/utils.js +2 -2
  53. package/lib/assess/propagators/string-prototype-replace.js +30 -28
  54. package/lib/assess/propagators/string-prototype-split.js +36 -36
  55. package/lib/assess/propagators/string-prototype-trim.js +15 -17
  56. package/lib/assess/propagators/string.js +12 -16
  57. package/lib/assess/propagators/template-escape.js +87 -0
  58. package/lib/assess/propagators/templates.js +10 -11
  59. package/lib/assess/propagators/url/url-prototype-parse.js +5 -6
  60. package/lib/assess/propagators/url/url-url.js +51 -43
  61. package/lib/assess/propagators/util/format.js +1 -1
  62. package/lib/assess/propagators/v8/init-hooks.js +3 -3
  63. package/lib/assess/propagators/validator/init-hooks.js +22 -22
  64. package/lib/assess/sinks/common.js +10 -5
  65. package/lib/assess/sinks/dustjs-linkedin-xss.js +131 -0
  66. package/lib/assess/sinks/libxmljs-xxe.js +1 -1
  67. package/lib/assess/sinks/mongodb.js +2 -1
  68. package/lib/assess/sinks/ssrf-url.js +1 -1
  69. package/lib/constants.js +4 -1
  70. package/lib/core/arch-components/dynamodb.js +1 -2
  71. package/lib/core/arch-components/dynamodbv3.js +44 -0
  72. package/lib/core/arch-components/index.js +1 -0
  73. package/lib/core/arch-components/rethinkdb.js +53 -0
  74. package/lib/core/config/options.js +3 -2
  75. package/lib/core/rewrite/injections.js +8 -0
  76. package/lib/core/stacktrace.js +2 -1
  77. package/lib/feature-set.js +1 -1
  78. package/lib/hooks/frameworks/base.js +8 -2
  79. package/lib/hooks/frameworks/http.js +23 -16
  80. package/lib/hooks/frameworks/http2.js +73 -0
  81. package/lib/hooks/frameworks/index.js +8 -3
  82. package/lib/hooks/http.js +112 -128
  83. package/lib/hooks/object-to-primitive.js +6 -7
  84. package/lib/hooks/patcher.js +75 -44
  85. package/lib/hooks/require.js +16 -22
  86. package/lib/instrumentation.js +0 -3
  87. package/lib/protect/rules/nosqli/nosql-injection-rule.js +228 -0
  88. package/lib/protect/rules/rule-factory.js +2 -2
  89. package/lib/protect/service.js +23 -11
  90. package/lib/protect/sinks/mongodb.js +56 -55
  91. package/lib/reporter/translations/to-protobuf/dtm/index.js +1 -1
  92. package/lib/reporter/translations/to-protobuf/dtm/ip-denylist-details.js +1 -1
  93. package/lib/reporter/translations/to-protobuf/dtm/rasp-rule-sample.js +1 -1
  94. package/lib/reporter/translations/to-protobuf/settings/defend-features.js +8 -6
  95. package/lib/reporter/translations/to-protobuf/settings/exclusions.js +5 -4
  96. package/lib/tracker.js +13 -65
  97. package/lib/util/some.js +27 -0
  98. package/lib/util/source-map.js +1 -1
  99. package/package.json +15 -16
  100. package/lib/hooks/frameworks/https.js +0 -42
  101. package/lib/protect/rules/nosqli/no-sql-injection-rule.js +0 -109
  102. package/node_modules/bindings/LICENSE.md +0 -22
  103. package/node_modules/bindings/README.md +0 -98
  104. package/node_modules/bindings/bindings.js +0 -221
  105. package/node_modules/bindings/package.json +0 -32
  106. package/node_modules/file-uri-to-path/.npmignore +0 -1
  107. package/node_modules/file-uri-to-path/.travis.yml +0 -30
  108. package/node_modules/file-uri-to-path/History.md +0 -21
  109. package/node_modules/file-uri-to-path/LICENSE +0 -20
  110. package/node_modules/file-uri-to-path/README.md +0 -74
  111. package/node_modules/file-uri-to-path/index.d.ts +0 -2
  112. package/node_modules/file-uri-to-path/index.js +0 -66
  113. package/node_modules/file-uri-to-path/package.json +0 -36
  114. package/node_modules/file-uri-to-path/test/test.js +0 -24
  115. package/node_modules/file-uri-to-path/test/tests.json +0 -13
  116. package/node_modules/glossy/LICENSE +0 -19
  117. package/node_modules/glossy/README.md +0 -129
  118. package/node_modules/glossy/index.js +0 -12
  119. package/node_modules/glossy/lib/glossy/parse.js +0 -520
  120. package/node_modules/glossy/lib/glossy/produce.js +0 -459
  121. package/node_modules/glossy/package.json +0 -47
  122. package/node_modules/glossy/test/decide.js +0 -7
  123. package/node_modules/glossy/test/decode_pri.js +0 -24
  124. package/node_modules/glossy/test/parse_3164.js +0 -104
  125. package/node_modules/glossy/test/parse_5424.js +0 -106
  126. package/node_modules/glossy/test/parse_5848.js +0 -40
  127. package/node_modules/glossy/test/parse_8601.js +0 -14
  128. package/node_modules/glossy/test/parse_rfc3339.js +0 -9
  129. package/node_modules/glossy/test/produce.js +0 -162
  130. package/node_modules/glossy/test/runner.js +0 -40
  131. package/node_modules/glossy/test/structure_data.js +0 -24
  132. package/node_modules/nan/CHANGELOG.md +0 -537
  133. package/node_modules/nan/LICENSE.md +0 -13
  134. package/node_modules/nan/README.md +0 -455
  135. package/node_modules/nan/doc/asyncworker.md +0 -146
  136. package/node_modules/nan/doc/buffers.md +0 -54
  137. package/node_modules/nan/doc/callback.md +0 -76
  138. package/node_modules/nan/doc/converters.md +0 -41
  139. package/node_modules/nan/doc/errors.md +0 -226
  140. package/node_modules/nan/doc/json.md +0 -62
  141. package/node_modules/nan/doc/maybe_types.md +0 -583
  142. package/node_modules/nan/doc/methods.md +0 -664
  143. package/node_modules/nan/doc/new.md +0 -147
  144. package/node_modules/nan/doc/node_misc.md +0 -123
  145. package/node_modules/nan/doc/object_wrappers.md +0 -263
  146. package/node_modules/nan/doc/persistent.md +0 -296
  147. package/node_modules/nan/doc/scopes.md +0 -73
  148. package/node_modules/nan/doc/script.md +0 -38
  149. package/node_modules/nan/doc/string_bytes.md +0 -62
  150. package/node_modules/nan/doc/v8_internals.md +0 -199
  151. package/node_modules/nan/doc/v8_misc.md +0 -85
  152. package/node_modules/nan/include_dirs.js +0 -1
  153. package/node_modules/nan/nan.h +0 -2898
  154. package/node_modules/nan/nan_callbacks.h +0 -88
  155. package/node_modules/nan/nan_callbacks_12_inl.h +0 -514
  156. package/node_modules/nan/nan_callbacks_pre_12_inl.h +0 -520
  157. package/node_modules/nan/nan_converters.h +0 -72
  158. package/node_modules/nan/nan_converters_43_inl.h +0 -68
  159. package/node_modules/nan/nan_converters_pre_43_inl.h +0 -42
  160. package/node_modules/nan/nan_define_own_property_helper.h +0 -29
  161. package/node_modules/nan/nan_implementation_12_inl.h +0 -430
  162. package/node_modules/nan/nan_implementation_pre_12_inl.h +0 -263
  163. package/node_modules/nan/nan_json.h +0 -166
  164. package/node_modules/nan/nan_maybe_43_inl.h +0 -356
  165. package/node_modules/nan/nan_maybe_pre_43_inl.h +0 -268
  166. package/node_modules/nan/nan_new.h +0 -340
  167. package/node_modules/nan/nan_object_wrap.h +0 -156
  168. package/node_modules/nan/nan_persistent_12_inl.h +0 -132
  169. package/node_modules/nan/nan_persistent_pre_12_inl.h +0 -242
  170. package/node_modules/nan/nan_private.h +0 -73
  171. package/node_modules/nan/nan_string_bytes.h +0 -305
  172. package/node_modules/nan/nan_typedarray_contents.h +0 -96
  173. package/node_modules/nan/nan_weak.h +0 -437
  174. package/node_modules/nan/package.json +0 -41
  175. package/node_modules/nan/tools/1to2.js +0 -412
  176. package/node_modules/nan/tools/README.md +0 -14
  177. package/node_modules/nan/tools/package.json +0 -19
  178. package/node_modules/unix-dgram/LICENSE +0 -13
  179. package/node_modules/unix-dgram/README.md +0 -107
  180. package/node_modules/unix-dgram/binding.gyp +0 -20
  181. package/node_modules/unix-dgram/build/Makefile +0 -324
  182. package/node_modules/unix-dgram/build/Release/.deps/Release/obj.target/unix_dgram/src/unix_dgram.o.d +0 -58
  183. package/node_modules/unix-dgram/build/Release/.deps/Release/obj.target/unix_dgram.node.d +0 -1
  184. package/node_modules/unix-dgram/build/Release/.deps/Release/unix_dgram.node.d +0 -1
  185. package/node_modules/unix-dgram/build/Release/obj.target/unix_dgram/src/unix_dgram.o +0 -0
  186. package/node_modules/unix-dgram/build/Release/obj.target/unix_dgram.node +0 -0
  187. package/node_modules/unix-dgram/build/Release/unix_dgram.node +0 -0
  188. package/node_modules/unix-dgram/build/binding.Makefile +0 -6
  189. package/node_modules/unix-dgram/build/config.gypi +0 -213
  190. package/node_modules/unix-dgram/build/unix_dgram.target.mk +0 -159
  191. package/node_modules/unix-dgram/lib/unix_dgram.js +0 -168
  192. package/node_modules/unix-dgram/package.json +0 -36
  193. package/node_modules/unix-dgram/src/unix_dgram.cc +0 -404
  194. package/node_modules/unix-dgram/src/win_dummy.cc +0 -7
  195. package/node_modules/unix-dgram/test/test-connect-callback.js +0 -68
  196. package/node_modules/unix-dgram/test/test-connect.js +0 -53
  197. package/node_modules/unix-dgram/test/test-dgram-unix.js +0 -58
  198. package/node_modules/unix-dgram/test/test-send-error.js +0 -26
  199. package/node_modules/winston-syslog/.eslintrc +0 -7
  200. package/node_modules/winston-syslog/.travis.yml +0 -14
  201. package/node_modules/winston-syslog/CHANGELOG.md +0 -9
  202. package/node_modules/winston-syslog/LICENSE +0 -20
  203. package/node_modules/winston-syslog/README.md +0 -135
  204. package/node_modules/winston-syslog/lib/utils.js +0 -26
  205. package/node_modules/winston-syslog/lib/winston-syslog.js +0 -385
  206. package/node_modules/winston-syslog/package.json +0 -56
  207. package/node_modules/winston-syslog/test/format-test.js +0 -122
  208. package/node_modules/winston-syslog/test/syslog-test.js +0 -95
  209. package/node_modules/winston-syslog/test/unix-connect-test.js +0 -133
@@ -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)
@@ -63,7 +63,7 @@ module.exports.handle = function handle() {
63
63
  if (tracked) {
64
64
  // it was behind a membrane
65
65
  data.result[TRACKED] = tracked;
66
- } else if (tracker.getData2(data.args[0])) {
66
+ } else if (tracker.getData(data.args[0])) {
67
67
  // it was a tracked string
68
68
  data.result[TRACKED] = data.args[0];
69
69
  }
@@ -80,13 +80,13 @@ module.exports.handle = function handle() {
80
80
  return;
81
81
  }
82
82
 
83
- const sTracking = tracker.getData2(tracked);
83
+ const sTracking = tracker.getData(tracked);
84
84
  // if the argument was a tracked string then the result should
85
85
  // have the same tags. i don't know that the length of a string
86
86
  // can change as a result of deserialize(serialize()) but best
87
87
  // to be safe.
88
88
  if (sTracking) {
89
- const resultTracking = tracker.track2(data.result);
89
+ const resultTracking = tracker.track(data.result);
90
90
  if (!resultTracking) {
91
91
  // there's nothing to do if tracking failed on the result. it should
92
92
  // only do so on a zero-length string, but node works in mysterious ways.
@@ -90,7 +90,7 @@ module.exports.handle = function() {
90
90
  function post(data) {
91
91
  if (data.result) {
92
92
  const trackingData = tracker.getData(data.args[0]);
93
- if (trackingData.tracked) {
93
+ if (trackingData) {
94
94
  tagRangeUtil.addInPlace(
95
95
  trackingData.tagRanges,
96
96
  new TagRange(0, data.args[0].length - 1, validators[validator])
@@ -131,7 +131,7 @@ module.exports.handle = function() {
131
131
  function post(data) {
132
132
  if (data.result) {
133
133
  const trackingData = tracker.getData(data.args[0]);
134
- if (trackingData.tracked) {
134
+ if (trackingData) {
135
135
  trackingData.tagRanges = [];
136
136
  trackingData.tracked = false;
137
137
  }
@@ -154,36 +154,36 @@ module.exports.handle = function() {
154
154
  function post(data) {
155
155
  // if not tracked then it can't be untrusted, so don't start
156
156
  // tracking this string.
157
- if (!tracker.getData(data.args[0]).tracked) return;
157
+ const inputData = tracker.getData(data.args[0]);
158
+ if (!inputData) return;
158
159
 
159
160
  const tag = sanitizers[sanitizer];
160
- // get existing tracker data
161
- const inputData = tracker.getData(data.args[0]);
162
161
 
163
162
  // are the input and output of the sanitizer the same?
164
163
  if (data.args[0] !== data.result) {
165
164
  let needsTag = true;
166
165
  // the input and output strings are not the same. start tracking the output.
167
- const result = tracker.track(data.result);
168
- const resultData = tracker.getData(result);
166
+ const tracked = tracker.track(data.result);
169
167
  // copy the input tags to the result, adjusting the range.
170
- const stop = data.result.length - 1;
171
- for (let i = 0; i < inputData.tagRanges.length; i++) {
172
- resultData.tagRanges[i] = new TagRange(
173
- 0,
174
- stop,
175
- inputData.tagRanges[i].tag
176
- );
177
- if (inputData.tagRanges[i].tag === tag) {
178
- needsTag = false;
168
+ if (tracked) {
169
+ const stop = data.result.length - 1;
170
+ for (let i = 0; i < inputData.tagRanges.length; i++) {
171
+ tracked.props.tagRanges[i] = new TagRange(
172
+ 0,
173
+ stop,
174
+ inputData.tagRanges[i].tag
175
+ );
176
+ if (inputData.tagRanges[i].tag === tag) {
177
+ needsTag = false;
178
+ }
179
179
  }
180
+ if (needsTag) {
181
+ tracked.props.tagRanges.push(
182
+ new TagRange(0, stop, sanitizers[sanitizer])
183
+ );
184
+ }
185
+ data.result = tracked.str;
180
186
  }
181
- if (needsTag) {
182
- resultData.tagRanges.push(
183
- new TagRange(0, stop, sanitizers[sanitizer])
184
- );
185
- }
186
- data.result = result;
187
187
  } else {
188
188
  // the input and output strings are the same, so the output is already being
189
189
  // tracked. if the tag is already present adjust the bounds otherwise add the
@@ -78,7 +78,9 @@ module.exports = (agent) => {
78
78
  // get the tags, event from dataflow actions ONLY
79
79
  if (sinkType === DATAFLOW) {
80
80
  const contrastProps = tracker.getData(input);
81
- ({ tagRanges, event } = contrastProps);
81
+ if (contrastProps) {
82
+ ({ tagRanges, event } = contrastProps);
83
+ }
82
84
  }
83
85
 
84
86
  const sink = new SinkEvent({
@@ -189,7 +191,7 @@ module.exports = (agent) => {
189
191
  ) {
190
192
  const trackData = tracker.getData(value);
191
193
 
192
- if (!trackData.tracked) {
194
+ if (!trackData) {
193
195
  return;
194
196
  }
195
197
 
@@ -415,9 +417,12 @@ module.exports = (agent) => {
415
417
  : common.nonDataflowVulnerableCheck({ sink });
416
418
 
417
419
  const checkedArgs = isVulnCheck(stack, result, args);
418
- return Array.isArray(checkedArgs)
419
- ? reduceConditions(checkedArgs, conditions.mode)
420
- : checkedArgs;
420
+ if (Array.isArray(checkedArgs) && checkedArgs.length > 0) {
421
+ const mode = conditions ? conditions.mode : 'and';
422
+ return reduceConditions(checkedArgs, mode);
423
+ } else {
424
+ return checkedArgs;
425
+ }
421
426
  };
422
427
 
423
428
  /**
@@ -0,0 +1,131 @@
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
+
17
+ const patcher = require('../../hooks/patcher');
18
+ const { PATCH_TYPES } = require('../../constants');
19
+ const moduleHook = require('../../hooks/require');
20
+ const { CallContext, Signature } = require('../models');
21
+ const {
22
+ RULES: { REFLECTED_XSS }
23
+ } = require('../../constants');
24
+
25
+ const signature = new Signature({
26
+ moduleName: 'http.ClientResponse',
27
+ methodName: 'write',
28
+ isModule: true
29
+ });
30
+ const moduleName = 'dustjs-linkedin';
31
+
32
+ class Handler {
33
+ constructor({ report, isVulnerable, requiredTags, xss: { disallowedTags } }) {
34
+ this._isVulnerable = isVulnerable;
35
+ this.report = report;
36
+ this.requiredTags = requiredTags;
37
+ this.disallowedTags = disallowedTags;
38
+ }
39
+
40
+ isVulnerable(input) {
41
+ const { requiredTags, disallowedTags } = this;
42
+ const ret = this._isVulnerable({
43
+ input,
44
+ ruleId: REFLECTED_XSS,
45
+ disallowedTags,
46
+ requiredTags
47
+ });
48
+ return ret;
49
+ }
50
+
51
+ handle() {
52
+ moduleHook.resolve({ name: moduleName }, (dust) =>
53
+ this.handleRequire(dust)
54
+ );
55
+ }
56
+
57
+ getType(obj) {
58
+ return obj && obj.constructor && obj.constructor.name;
59
+ }
60
+
61
+ isResponseType(typeName) {
62
+ return typeName === 'ServerResponse';
63
+ }
64
+
65
+ patchDust(dust) {
66
+ const self = this;
67
+ patcher.patch(dust, 'stream', {
68
+ name: moduleName,
69
+ patchType: PATCH_TYPES.ASSESS_SINK,
70
+ alwaysRun: true,
71
+ post(data) {
72
+ self.patchStream(data.result);
73
+ }
74
+ });
75
+ }
76
+
77
+ /**
78
+ * Patch the pipe method and check wether the stream is targeting a
79
+ * response instance. If so, add the instance to the set of streams
80
+ * we should follow.
81
+ */
82
+ patchStream(streamInstance) {
83
+ const self = this;
84
+ patcher.patch(streamInstance, 'pipe', {
85
+ name: 'dust.Stream.prototype',
86
+ patchType: PATCH_TYPES.ASSESS_SINK,
87
+ alwaysRun: true,
88
+ pre({ args: [maybeRes] }) {
89
+ self.patchStreamTarget(maybeRes);
90
+ }
91
+ });
92
+ }
93
+
94
+ patchStreamTarget(target) {
95
+ const self = this;
96
+ const typeName = self.getType(target);
97
+
98
+ if (self.isResponseType(typeName)) {
99
+ patcher.patch(target, 'write', {
100
+ name: 'dust.pipeTarget',
101
+ patchType: PATCH_TYPES.ASSESS_SINK,
102
+ alwaysRun: true,
103
+ post({ args: [str], hooked }) {
104
+ if (self.isVulnerable(str)) {
105
+ self.report({
106
+ ruleId: REFLECTED_XSS,
107
+ signature,
108
+ input: str,
109
+ ctxt: new CallContext({
110
+ obj: typeName,
111
+ args: [str],
112
+ result: str,
113
+ stackOpts: {
114
+ constructorOpt: hooked
115
+ }
116
+ })
117
+ });
118
+ }
119
+ }
120
+ });
121
+ }
122
+ }
123
+
124
+ handleRequire(dust) {
125
+ this.patchDust(dust);
126
+ return dust;
127
+ }
128
+ }
129
+
130
+ module.exports = ({ common }) => new Handler(common);
131
+ module.exports.Handler = Handler;
@@ -34,7 +34,7 @@ module.exports = ({ common, signature }) => ({
34
34
  }
35
35
 
36
36
  const contrastProps = tracker.getData(xmlString);
37
- if (!contrastProps.tracked) {
37
+ if (!contrastProps) {
38
38
  return;
39
39
  }
40
40
 
@@ -39,7 +39,8 @@ const ruleId = 'nosql-injection';
39
39
  const disallowedTags = [
40
40
  'limited-chars',
41
41
  'alphanum-space-hyphen',
42
- 'string-type-checked'
42
+ 'string-type-checked',
43
+ 'custom-validated-nosql-injection'
43
44
  ];
44
45
  const requiredTags = ['untrusted'];
45
46
 
@@ -64,7 +64,7 @@ module.exports = ({ common: { isVulnerable } }) => {
64
64
 
65
65
  const contrastProps = tracker.getData(input);
66
66
 
67
- if (!contrastProps.tracked) {
67
+ if (!contrastProps) {
68
68
  return false;
69
69
  }
70
70
 
package/lib/constants.js CHANGED
@@ -108,8 +108,11 @@ const RULES = {
108
108
  'cmd-injection-semantic-chained-commands',
109
109
  CMD_INJECTION_SEMANTIC_DANGEROUS_PATHS:
110
110
  'cmd-injection-semantic-dangerous-paths',
111
- IP_DENYLIST: 'ip-blacklist',
111
+ IP_DENYLIST: 'ip-denylist',
112
112
  METHOD_TAMPERING: 'method-tampering',
113
+ // The following is not a known rule in TS and is only used by SR when
114
+ // reporting certain analysis results. We convert to nosqli before reporting
115
+ NOSQL_EXPANSION: 'nosql-expansion',
113
116
  NOSQL_INJECTION: 'nosql-injection',
114
117
  PATH_TRAVERSAL: 'path-traversal',
115
118
  REFLECTED_XSS: 'reflected-xss',
@@ -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');
@@ -0,0 +1,53 @@
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
+
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
+ const logger = require('../logger')('contrast:arch-component');
22
+
23
+ ModuleHook.resolve({ name: 'rethinkdb' }, (rethinkdb) => {
24
+ patcher.patch(rethinkdb, 'connect', {
25
+ name: 'rethinkdb.arch_component',
26
+ patchType: PATCH_TYPES.ARCH_COMPONENT,
27
+ alwaysRun: true,
28
+ post(ctx) {
29
+ ctx.result
30
+ .then((res) => {
31
+ if (res.open) {
32
+ const url =
33
+ res.host == 'localhost'
34
+ ? 'http://localhost'
35
+ : new URL(res.host).toString();
36
+ agentEmitter.emit('architectureComponent', {
37
+ vendor: 'RethinkDB',
38
+ url,
39
+ remotePort: res.port
40
+ });
41
+ } else {
42
+ logger.warn('unable to open RethinkDB connection');
43
+ }
44
+ })
45
+ .catch((err) => {
46
+ logger.warn(
47
+ 'unable to report RethinkDB architecture component\n%o',
48
+ err
49
+ );
50
+ });
51
+ }
52
+ });
53
+ });
@@ -284,7 +284,7 @@ const agent = [
284
284
  {
285
285
  name: 'agent.node.array_request_sampling.enable',
286
286
  arg: '[false]',
287
- default: true,
287
+ default: false,
288
288
  fn: castBoolean,
289
289
  desc: 'enable sampling of array members for dataflow'
290
290
  },
@@ -483,7 +483,8 @@ const agent = [
483
483
  arg: '<limit>',
484
484
  default: 10,
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',
@@ -131,6 +131,14 @@ const injections = {
131
131
  name: 'String',
132
132
  patchType: PATCH_TYPES.INJECTION
133
133
  })
134
+ ),
135
+ Number: new Injection(
136
+ 'Number',
137
+ 'ContrastNumber',
138
+ patcher.patch(global.Number, {
139
+ name: 'Number',
140
+ patchType: PATCH_TYPES.INJECTION
141
+ })
134
142
  )
135
143
  };
136
144
 
@@ -15,11 +15,12 @@ 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
25
  const EVENTS_FILE = semver.gte(process.version, '16.0.0')
25
26
  ? 'node:events'