dd-trace 3.3.1 → 3.12.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 (198) hide show
  1. package/LICENSE-3rdparty.csv +8 -0
  2. package/README.md +108 -43
  3. package/ci/init.js +6 -1
  4. package/ext/exporters.d.ts +2 -1
  5. package/ext/exporters.js +2 -1
  6. package/index.d.ts +129 -36
  7. package/package.json +18 -9
  8. package/packages/datadog-instrumentations/src/body-parser.js +26 -0
  9. package/packages/datadog-instrumentations/src/cassandra-driver.js +7 -7
  10. package/packages/datadog-instrumentations/src/child-process.js +30 -0
  11. package/packages/datadog-instrumentations/src/connect.js +15 -15
  12. package/packages/datadog-instrumentations/src/cucumber.js +1 -3
  13. package/packages/datadog-instrumentations/src/elasticsearch.js +51 -47
  14. package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +1 -1
  15. package/packages/datadog-instrumentations/src/helpers/hooks.js +9 -0
  16. package/packages/datadog-instrumentations/src/helpers/register.js +5 -0
  17. package/packages/datadog-instrumentations/src/http/server.js +20 -12
  18. package/packages/datadog-instrumentations/src/http2/server.js +67 -1
  19. package/packages/datadog-instrumentations/src/jest.js +182 -25
  20. package/packages/datadog-instrumentations/src/koa.js +32 -32
  21. package/packages/datadog-instrumentations/src/ldapjs.js +91 -0
  22. package/packages/datadog-instrumentations/src/mariadb.js +63 -0
  23. package/packages/datadog-instrumentations/src/memcached.js +1 -4
  24. package/packages/datadog-instrumentations/src/mocha.js +135 -24
  25. package/packages/datadog-instrumentations/src/next.js +10 -2
  26. package/packages/datadog-instrumentations/src/opensearch.js +10 -0
  27. package/packages/datadog-instrumentations/src/oracledb.js +8 -8
  28. package/packages/datadog-instrumentations/src/pg.js +7 -3
  29. package/packages/datadog-instrumentations/src/qs.js +24 -0
  30. package/packages/datadog-instrumentations/src/redis.js +12 -3
  31. package/packages/datadog-instrumentations/src/restify.js +5 -1
  32. package/packages/datadog-instrumentations/src/rhea.js +29 -15
  33. package/packages/datadog-instrumentations/src/router.js +23 -23
  34. package/packages/datadog-plugin-amqp10/src/consumer.js +32 -0
  35. package/packages/datadog-plugin-amqp10/src/index.js +11 -101
  36. package/packages/datadog-plugin-amqp10/src/producer.js +34 -0
  37. package/packages/datadog-plugin-amqp10/src/util.js +15 -0
  38. package/packages/datadog-plugin-amqplib/src/client.js +38 -0
  39. package/packages/datadog-plugin-amqplib/src/consumer.js +40 -0
  40. package/packages/datadog-plugin-amqplib/src/index.js +14 -102
  41. package/packages/datadog-plugin-amqplib/src/producer.js +37 -0
  42. package/packages/datadog-plugin-amqplib/src/util.js +14 -0
  43. package/packages/datadog-plugin-cassandra-driver/src/index.js +22 -60
  44. package/packages/datadog-plugin-cucumber/src/index.js +14 -33
  45. package/packages/datadog-plugin-cypress/src/plugin.js +2 -1
  46. package/packages/datadog-plugin-dns/src/index.js +16 -91
  47. package/packages/datadog-plugin-dns/src/lookup.js +40 -0
  48. package/packages/datadog-plugin-dns/src/lookup_service.js +24 -0
  49. package/packages/datadog-plugin-dns/src/resolve.js +24 -0
  50. package/packages/datadog-plugin-dns/src/reverse.js +21 -0
  51. package/packages/datadog-plugin-elasticsearch/src/index.js +24 -60
  52. package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +24 -0
  53. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +41 -0
  54. package/packages/datadog-plugin-google-cloud-pubsub/src/index.js +14 -99
  55. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +33 -0
  56. package/packages/datadog-plugin-graphql/src/execute.js +73 -0
  57. package/packages/datadog-plugin-graphql/src/index.js +14 -176
  58. package/packages/datadog-plugin-graphql/src/parse.js +32 -0
  59. package/packages/datadog-plugin-graphql/src/resolve.js +70 -76
  60. package/packages/datadog-plugin-graphql/src/validate.js +28 -0
  61. package/packages/datadog-plugin-grpc/src/client.js +46 -55
  62. package/packages/datadog-plugin-grpc/src/index.js +7 -24
  63. package/packages/datadog-plugin-grpc/src/server.js +50 -52
  64. package/packages/datadog-plugin-grpc/src/util.js +15 -14
  65. package/packages/datadog-plugin-http/src/client.js +15 -5
  66. package/packages/datadog-plugin-http/src/index.js +7 -22
  67. package/packages/datadog-plugin-http/src/server.js +19 -3
  68. package/packages/datadog-plugin-http2/src/client.js +20 -1
  69. package/packages/datadog-plugin-http2/src/index.js +8 -26
  70. package/packages/datadog-plugin-http2/src/server.js +44 -0
  71. package/packages/datadog-plugin-jest/src/index.js +41 -65
  72. package/packages/datadog-plugin-kafkajs/src/consumer.js +42 -0
  73. package/packages/datadog-plugin-kafkajs/src/index.js +11 -87
  74. package/packages/datadog-plugin-kafkajs/src/producer.js +31 -0
  75. package/packages/datadog-plugin-mariadb/src/index.js +10 -0
  76. package/packages/datadog-plugin-memcached/src/index.js +17 -52
  77. package/packages/datadog-plugin-mocha/src/index.js +40 -60
  78. package/packages/datadog-plugin-moleculer/src/client.js +22 -36
  79. package/packages/datadog-plugin-moleculer/src/index.js +8 -26
  80. package/packages/datadog-plugin-moleculer/src/server.js +18 -30
  81. package/packages/datadog-plugin-mongodb-core/src/index.js +23 -51
  82. package/packages/datadog-plugin-mysql/src/index.js +23 -52
  83. package/packages/datadog-plugin-mysql2/src/index.js +1 -3
  84. package/packages/datadog-plugin-net/src/index.js +4 -0
  85. package/packages/datadog-plugin-net/src/ipc.js +21 -0
  86. package/packages/datadog-plugin-net/src/tcp.js +46 -0
  87. package/packages/datadog-plugin-next/src/index.js +3 -0
  88. package/packages/datadog-plugin-opensearch/src/index.js +11 -0
  89. package/packages/datadog-plugin-oracledb/src/index.js +29 -55
  90. package/packages/datadog-plugin-pg/src/index.js +27 -51
  91. package/packages/datadog-plugin-redis/src/index.js +29 -60
  92. package/packages/datadog-plugin-rhea/src/consumer.js +55 -0
  93. package/packages/datadog-plugin-rhea/src/index.js +11 -96
  94. package/packages/datadog-plugin-rhea/src/producer.js +45 -0
  95. package/packages/datadog-plugin-router/src/index.js +13 -2
  96. package/packages/datadog-plugin-sharedb/src/index.js +22 -39
  97. package/packages/datadog-plugin-tedious/src/index.js +20 -41
  98. package/packages/dd-trace/src/appsec/addresses.js +3 -1
  99. package/packages/dd-trace/src/appsec/blocking.js +44 -0
  100. package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +8 -6
  101. package/packages/dd-trace/src/appsec/gateway/engine/engine.js +1 -1
  102. package/packages/dd-trace/src/appsec/gateway/engine/index.js +7 -2
  103. package/packages/dd-trace/src/appsec/gateway/engine/runner.js +0 -1
  104. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +4 -1
  105. package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +11 -0
  106. package/packages/dd-trace/src/appsec/iast/analyzers/injection-analyzer.js +19 -0
  107. package/packages/dd-trace/src/appsec/iast/analyzers/ldap-injection-analyzer.js +11 -0
  108. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +13 -0
  109. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +43 -5
  110. package/packages/dd-trace/src/appsec/iast/iast-context.js +3 -1
  111. package/packages/dd-trace/src/appsec/iast/index.js +24 -8
  112. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +20 -1
  113. package/packages/dd-trace/src/appsec/iast/path-line.js +17 -6
  114. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +17 -0
  115. package/packages/dd-trace/src/appsec/iast/taint-tracking/filter.js +16 -0
  116. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +18 -0
  117. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +98 -0
  118. package/packages/dd-trace/src/appsec/iast/taint-tracking/origin-types.js +4 -0
  119. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +38 -0
  120. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +67 -0
  121. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +103 -0
  122. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +98 -30
  123. package/packages/dd-trace/src/appsec/index.js +79 -25
  124. package/packages/dd-trace/src/{plugins/util → appsec}/ip_blocklist.js +0 -0
  125. package/packages/dd-trace/src/appsec/ip_extractor.js +98 -0
  126. package/packages/dd-trace/src/appsec/recommended.json +134 -53
  127. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +7 -0
  128. package/packages/dd-trace/src/appsec/remote_config/index.js +56 -0
  129. package/packages/dd-trace/src/appsec/remote_config/manager.js +264 -0
  130. package/packages/dd-trace/src/{exporters → appsec/remote_config}/scheduler.js +9 -9
  131. package/packages/dd-trace/src/appsec/rule_manager.js +61 -1
  132. package/packages/dd-trace/src/appsec/templates/blocked.html +99 -0
  133. package/packages/dd-trace/src/appsec/templates/blocked.json +8 -0
  134. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +66 -0
  135. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +9 -5
  136. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +19 -51
  137. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +10 -5
  138. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +202 -0
  139. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +51 -62
  140. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +89 -0
  141. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +82 -0
  142. package/packages/dd-trace/src/config.js +119 -35
  143. package/packages/dd-trace/src/constants.js +9 -1
  144. package/packages/dd-trace/src/dogstatsd.js +42 -10
  145. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +10 -2
  146. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
  147. package/packages/dd-trace/src/exporter.js +3 -0
  148. package/packages/dd-trace/src/exporters/agent/index.js +10 -2
  149. package/packages/dd-trace/src/exporters/agent/writer.js +2 -9
  150. package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +82 -0
  151. package/packages/dd-trace/src/exporters/common/request.js +51 -1
  152. package/packages/dd-trace/src/exporters/span-stats/index.js +6 -2
  153. package/packages/dd-trace/src/format.js +29 -10
  154. package/packages/dd-trace/src/lambda/handler.js +72 -0
  155. package/packages/dd-trace/src/lambda/index.js +5 -0
  156. package/packages/dd-trace/src/lambda/runtime/errors.js +20 -0
  157. package/packages/dd-trace/src/lambda/runtime/patch.js +74 -0
  158. package/packages/dd-trace/src/lambda/runtime/ritm.js +138 -0
  159. package/packages/dd-trace/src/metrics.js +15 -2
  160. package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -5
  161. package/packages/dd-trace/src/opentracing/span.js +2 -1
  162. package/packages/dd-trace/src/opentracing/span_context.js +9 -0
  163. package/packages/dd-trace/src/plugin_manager.js +11 -17
  164. package/packages/dd-trace/src/plugins/cache.js +9 -0
  165. package/packages/dd-trace/src/plugins/ci_plugin.js +97 -0
  166. package/packages/dd-trace/src/plugins/client.js +9 -0
  167. package/packages/dd-trace/src/plugins/composite.js +26 -0
  168. package/packages/dd-trace/src/plugins/consumer.js +9 -0
  169. package/packages/dd-trace/src/plugins/database.js +55 -0
  170. package/packages/dd-trace/src/plugins/incoming.js +7 -0
  171. package/packages/dd-trace/src/plugins/index.js +3 -0
  172. package/packages/dd-trace/src/plugins/log_plugin.js +2 -2
  173. package/packages/dd-trace/src/plugins/outgoing.js +31 -0
  174. package/packages/dd-trace/src/plugins/plugin.js +3 -0
  175. package/packages/dd-trace/src/plugins/producer.js +9 -0
  176. package/packages/dd-trace/src/plugins/server.js +9 -0
  177. package/packages/dd-trace/src/plugins/storage.js +21 -0
  178. package/packages/dd-trace/src/plugins/tracing.js +94 -0
  179. package/packages/dd-trace/src/plugins/util/ci.js +40 -4
  180. package/packages/dd-trace/src/plugins/util/git.js +58 -18
  181. package/packages/dd-trace/src/plugins/util/test.js +71 -8
  182. package/packages/dd-trace/src/plugins/util/user-provided-git.js +14 -1
  183. package/packages/dd-trace/src/plugins/util/web.js +11 -114
  184. package/packages/dd-trace/src/priority_sampler.js +6 -2
  185. package/packages/dd-trace/src/profiling/config.js +11 -6
  186. package/packages/dd-trace/src/profiling/exporters/agent.js +4 -0
  187. package/packages/dd-trace/src/profiling/index.js +2 -2
  188. package/packages/dd-trace/src/profiling/profiler.js +43 -7
  189. package/packages/dd-trace/src/proxy.js +8 -16
  190. package/packages/dd-trace/src/ritm.js +25 -14
  191. package/packages/dd-trace/src/span_processor.js +17 -0
  192. package/packages/dd-trace/src/span_sampler.js +77 -0
  193. package/packages/dd-trace/src/span_stats.js +2 -2
  194. package/packages/dd-trace/src/telemetry/dependencies.js +21 -7
  195. package/packages/dd-trace/src/telemetry/index.js +7 -1
  196. package/packages/dd-trace/src/telemetry/send-data.js +3 -1
  197. package/packages/dd-trace/src/tracer.js +10 -5
  198. package/packages/dd-trace/src/util.js +43 -1
@@ -0,0 +1,67 @@
1
+ 'use strict'
2
+
3
+ const Module = require('module')
4
+ const shimmer = require('../../../../../datadog-shimmer')
5
+ const log = require('../../../log')
6
+ const { isPrivateModule, isNotLibraryFile } = require('./filter')
7
+ const { csiMethods } = require('./csi-methods')
8
+
9
+ let rewriter
10
+ let getPrepareStackTrace
11
+ function getRewriter () {
12
+ if (!rewriter) {
13
+ try {
14
+ const iastRewriter = require('@datadog/native-iast-rewriter')
15
+ const Rewriter = iastRewriter.Rewriter
16
+ getPrepareStackTrace = iastRewriter.getPrepareStackTrace
17
+ rewriter = new Rewriter({ csiMethods })
18
+ } catch (e) {
19
+ log.warn(`Unable to initialize TaintTracking Rewriter: ${e.message}`)
20
+ }
21
+ }
22
+ return rewriter
23
+ }
24
+
25
+ let originalPrepareStackTrace = Error.prepareStackTrace
26
+ function getPrepareStackTraceAccessor () {
27
+ let actual = getPrepareStackTrace(originalPrepareStackTrace)
28
+ return {
29
+ get () {
30
+ return actual
31
+ },
32
+ set (value) {
33
+ actual = getPrepareStackTrace(value)
34
+ originalPrepareStackTrace = value
35
+ }
36
+ }
37
+ }
38
+
39
+ function getCompileMethodFn (compileMethod) {
40
+ return function (content, filename) {
41
+ try {
42
+ if (isPrivateModule(filename) && isNotLibraryFile(filename)) {
43
+ content = rewriter.rewrite(content, filename)
44
+ }
45
+ } catch (e) {
46
+ log.debug(e)
47
+ }
48
+ return compileMethod.apply(this, [content, filename])
49
+ }
50
+ }
51
+
52
+ function enableRewriter () {
53
+ const rewriter = getRewriter()
54
+ if (rewriter) {
55
+ Object.defineProperty(global.Error, 'prepareStackTrace', getPrepareStackTraceAccessor())
56
+ shimmer.wrap(Module.prototype, '_compile', compileMethod => getCompileMethodFn(compileMethod))
57
+ }
58
+ }
59
+
60
+ function disableRewriter () {
61
+ shimmer.unwrap(Module.prototype, '_compile')
62
+ Error.prepareStackTrace = originalPrepareStackTrace
63
+ }
64
+
65
+ module.exports = {
66
+ enableRewriter, disableRewriter
67
+ }
@@ -0,0 +1,103 @@
1
+ 'use strict'
2
+
3
+ const TaintedUtils = require('@datadog/native-iast-taint-tracking')
4
+ const { storage } = require('../../../../../datadog-core')
5
+ const iastContextFunctions = require('../iast-context')
6
+ const log = require('../../../log')
7
+
8
+ function noop (res) { return res }
9
+ const TaintTrackingDummy = {
10
+ plusOperator: noop,
11
+ trim: noop,
12
+ trimEnd: noop,
13
+ concat: noop,
14
+ substring: noop,
15
+ substr: noop,
16
+ slice: noop,
17
+ replace: noop
18
+ }
19
+
20
+ function getTransactionId () {
21
+ const store = storage.getStore()
22
+ const iastContext = iastContextFunctions.getIastContext(store)
23
+ return iastContext && iastContext[iastContextFunctions.IAST_TRANSACTION_ID]
24
+ }
25
+
26
+ function getFilteredCsiFn (cb, filter) {
27
+ return function csiCall (res, fn, target, ...rest) {
28
+ try {
29
+ if (filter(res, fn, target)) { return res }
30
+ const transactionId = getTransactionId()
31
+ if (transactionId) {
32
+ return cb(transactionId, res, target, ...rest)
33
+ }
34
+ } catch (e) {
35
+ log.debug(e)
36
+ }
37
+ return res
38
+ }
39
+ }
40
+
41
+ function notString () {
42
+ return Array.prototype.some.call(arguments, (p) => typeof p !== 'string')
43
+ }
44
+
45
+ function isValidCsiMethod (fn, protos) {
46
+ return protos.some(proto => fn === proto)
47
+ }
48
+
49
+ function getCsiFn (cb, ...protos) {
50
+ let filter
51
+ if (!protos || protos.length === 0) {
52
+ filter = (res, fn, target) => notString(res, target)
53
+ } else if (protos.length === 1) {
54
+ const protoFn = protos[0]
55
+ filter = (res, fn, target) => notString(res, target) || fn !== protoFn
56
+ } else {
57
+ filter = (res, fn, target) => notString(res, target) || !isValidCsiMethod(fn, protos)
58
+ }
59
+ return getFilteredCsiFn(cb, filter)
60
+ }
61
+
62
+ function csiMethodsDefaults (names, excluded) {
63
+ const impl = {}
64
+ names.forEach(name => {
65
+ if (excluded.indexOf(name) !== -1) return
66
+ impl[name] = getCsiFn(
67
+ (transactionId, res, target, ...rest) => TaintedUtils[name](transactionId, res, target, ...rest),
68
+ String.prototype[name]
69
+ )
70
+ })
71
+ return impl
72
+ }
73
+
74
+ const csiMethodsOverrides = {
75
+ plusOperator: function (res, op1, op2) {
76
+ try {
77
+ if (notString(res) || (notString(op1) && notString(op2))) { return res }
78
+ const transactionId = getTransactionId()
79
+ if (transactionId) {
80
+ return TaintedUtils.concat(transactionId, res, op1, op2)
81
+ }
82
+ } catch (e) {
83
+ log.debug(e)
84
+ }
85
+ return res
86
+ },
87
+
88
+ trim: getCsiFn(
89
+ (transactionId, res, target) => TaintedUtils.trim(transactionId, res, target),
90
+ String.prototype.trim,
91
+ String.prototype.trimStart
92
+ )
93
+ }
94
+
95
+ const TaintTracking = {
96
+ ...csiMethodsDefaults(Object.keys(TaintTrackingDummy), Object.keys(csiMethodsOverrides)),
97
+ ...csiMethodsOverrides
98
+ }
99
+
100
+ module.exports = {
101
+ TaintTracking,
102
+ TaintTrackingDummy
103
+ }
@@ -5,13 +5,16 @@ const IAST_JSON_TAG_KEY = '_dd.iast.json'
5
5
  const VULNERABILITY_HASHES_MAX_SIZE = 1000
6
6
  const VULNERABILITY_HASHES = new LRU({ max: VULNERABILITY_HASHES_MAX_SIZE })
7
7
 
8
+ let tracer
9
+
8
10
  function createVulnerability (type, evidence, spanId, location) {
9
- if (type && evidence && spanId) {
11
+ if (type && evidence) {
12
+ const _spanId = spanId || 0
10
13
  return {
11
14
  type,
12
15
  evidence,
13
16
  location: {
14
- spanId,
17
+ spanId: _spanId,
15
18
  ...location
16
19
  },
17
20
  hash: createHash(type, location)
@@ -37,10 +40,14 @@ function createHash (type, location) {
37
40
  }
38
41
 
39
42
  function addVulnerability (iastContext, vulnerability) {
40
- if (iastContext && vulnerability && vulnerability.evidence && vulnerability.type &&
41
- vulnerability.location && vulnerability.location.spanId) {
42
- iastContext[VULNERABILITIES_KEY] = iastContext[VULNERABILITIES_KEY] || []
43
- iastContext[VULNERABILITIES_KEY].push(vulnerability)
43
+ if (vulnerability && vulnerability.evidence && vulnerability.type &&
44
+ vulnerability.location) {
45
+ if (iastContext && iastContext.rootSpan) {
46
+ iastContext[VULNERABILITIES_KEY] = iastContext[VULNERABILITIES_KEY] || []
47
+ iastContext[VULNERABILITIES_KEY].push(vulnerability)
48
+ } else {
49
+ sendVulnerabilities([vulnerability])
50
+ }
44
51
  }
45
52
  }
46
53
 
@@ -50,13 +57,44 @@ function isValidVulnerability (vulnerability) {
50
57
  vulnerability.location && vulnerability.location.spanId
51
58
  }
52
59
 
53
- function jsonVulnerabilityFromVulnerability (vulnerability) {
60
+ function formatEvidence (evidence, sourcesIndexes) {
61
+ if (!evidence.ranges) {
62
+ return { value: evidence.value }
63
+ }
64
+
65
+ const valueParts = []
66
+ let fromIndex = 0
67
+ evidence.ranges.forEach((range, rangeIndex) => {
68
+ if (fromIndex < range.start) {
69
+ valueParts.push({ value: evidence.value.substring(fromIndex, range.start) })
70
+ }
71
+ valueParts.push({ value: evidence.value.substring(range.start, range.end), source: sourcesIndexes[rangeIndex] })
72
+ fromIndex = range.end
73
+ })
74
+ if (fromIndex < evidence.value.length) {
75
+ valueParts.push({ value: evidence.value.substring(fromIndex) })
76
+ }
77
+ return { valueParts }
78
+ }
79
+
80
+ function extractSourcesFromVulnerability (vulnerability) {
81
+ if (!vulnerability.evidence.ranges) {
82
+ return []
83
+ }
84
+ return vulnerability.evidence.ranges.map(range => (
85
+ {
86
+ origin: range.iinfo.type,
87
+ name: range.iinfo.parameterName,
88
+ value: range.iinfo.parameterValue
89
+ }
90
+ ))
91
+ }
92
+
93
+ function jsonVulnerabilityFromVulnerability (vulnerability, sourcesIndexes) {
54
94
  const jsonVulnerability = {
55
95
  type: vulnerability.type,
56
96
  hash: vulnerability.hash,
57
- evidence: {
58
- value: vulnerability.evidence.value
59
- },
97
+ evidence: formatEvidence(vulnerability.evidence, sourcesIndexes),
60
98
  location: {
61
99
  spanId: vulnerability.location.spanId
62
100
  }
@@ -70,28 +108,53 @@ function jsonVulnerabilityFromVulnerability (vulnerability) {
70
108
  return jsonVulnerability
71
109
  }
72
110
 
73
- function sendVulnerabilities (iastContext) {
74
- if (iastContext && iastContext.rootSpan && iastContext[VULNERABILITIES_KEY] &&
75
- iastContext[VULNERABILITIES_KEY].length && iastContext.rootSpan.addTags) {
76
- const span = iastContext.rootSpan
77
- const allVulnerabilities = iastContext[VULNERABILITIES_KEY]
78
- // TODO support sources and ranges
79
- const jsonToSend = {
80
- vulnerabilities: []
111
+ function sendVulnerabilities (vulnerabilities, rootSpan) {
112
+ if (vulnerabilities && vulnerabilities.length) {
113
+ let span = rootSpan
114
+ if (!span && tracer) {
115
+ span = tracer.startSpan('vulnerability', {
116
+ type: 'vulnerability'
117
+ })
118
+ vulnerabilities.forEach((vulnerability) => {
119
+ vulnerability.location.spanId = span.context().toSpanId()
120
+ })
81
121
  }
82
122
 
83
- deduplicateVulnerabilities(allVulnerabilities).forEach((vulnerability) => {
84
- if (isValidVulnerability(vulnerability)) {
85
- jsonToSend.vulnerabilities.push(jsonVulnerabilityFromVulnerability(vulnerability))
123
+ if (span && span.addTags) {
124
+ const jsonToSend = {
125
+ sources: [],
126
+ vulnerabilities: []
127
+ }
128
+
129
+ deduplicateVulnerabilities(vulnerabilities).forEach((vulnerability) => {
130
+ if (isValidVulnerability(vulnerability)) {
131
+ const sourcesIndexes = []
132
+ const vulnerabilitySources = extractSourcesFromVulnerability(vulnerability)
133
+ vulnerabilitySources.forEach((source) => {
134
+ let sourceIndex = jsonToSend.sources.findIndex(
135
+ existingSource =>
136
+ existingSource.origin === source.origin &&
137
+ existingSource.name === source.name &&
138
+ existingSource.value === source.value
139
+ )
140
+ if (sourceIndex === -1) {
141
+ sourceIndex = jsonToSend.sources.length
142
+ jsonToSend.sources.push(source)
143
+ }
144
+ sourcesIndexes.push(sourceIndex)
145
+ })
146
+ jsonToSend.vulnerabilities.push(jsonVulnerabilityFromVulnerability(vulnerability, sourcesIndexes))
147
+ }
148
+ })
149
+
150
+ if (jsonToSend.vulnerabilities.length > 0) {
151
+ const tags = {}
152
+ // TODO: Store this outside of the span and set the tag in the exporter.
153
+ tags[IAST_JSON_TAG_KEY] = JSON.stringify(jsonToSend)
154
+ tags[MANUAL_KEEP] = 'true'
155
+ span.addTags(tags)
156
+ if (!rootSpan) span.finish()
86
157
  }
87
- })
88
-
89
- if (jsonToSend.vulnerabilities.length > 0) {
90
- const tags = {}
91
- // TODO: Store this outside of the span and set the tag in the exporter.
92
- tags[IAST_JSON_TAG_KEY] = JSON.stringify(jsonToSend)
93
- tags[MANUAL_KEEP] = 'true'
94
- span.addTags(tags)
95
158
  }
96
159
  }
97
160
  return IAST_JSON_TAG_KEY
@@ -113,9 +176,14 @@ function deduplicateVulnerabilities (vulnerabilities) {
113
176
  return deduplicated
114
177
  }
115
178
 
179
+ function setTracer (_tracer) {
180
+ tracer = _tracer
181
+ }
182
+
116
183
  module.exports = {
117
184
  createVulnerability,
118
185
  addVulnerability,
119
186
  sendVulnerabilities,
120
- clearCache
187
+ clearCache,
188
+ setTracer
121
189
  }
@@ -1,32 +1,51 @@
1
1
  'use strict'
2
2
 
3
3
  const fs = require('fs')
4
+ const path = require('path')
4
5
  const log = require('../log')
5
6
  const RuleManager = require('./rule_manager')
7
+ const remoteConfig = require('./remote_config')
6
8
  const { incomingHttpRequestStart, incomingHttpRequestEnd } = require('./gateway/channels')
7
9
  const Gateway = require('./gateway/engine')
8
10
  const addresses = require('./addresses')
9
11
  const Reporter = require('./reporter')
10
12
  const web = require('../plugins/util/web')
13
+ const { extractIp } = require('./ip_extractor')
14
+ const { HTTP_CLIENT_IP } = require('../../../../ext/tags')
15
+ const { block, loadTemplates, loadTemplatesAsync } = require('./blocking')
11
16
 
12
- function enable (config) {
13
- try {
14
- // TODO: enable dc_blocking: config.appsec.blocking === true
17
+ let isEnabled = false
18
+ let config
15
19
 
16
- let rules = fs.readFileSync(config.appsec.rules)
17
- rules = JSON.parse(rules)
20
+ function enable (_config) {
21
+ if (isEnabled) return
18
22
 
19
- RuleManager.applyRules(rules, config.appsec)
23
+ try {
24
+ loadTemplates(_config)
25
+ const rules = fs.readFileSync(_config.appsec.rules || path.join(__dirname, 'recommended.json'))
26
+ enableFromRules(_config, JSON.parse(rules))
20
27
  } catch (err) {
21
- log.error('Unable to start AppSec')
22
- log.error(err)
28
+ abortEnable(err)
29
+ }
30
+ }
23
31
 
24
- // abort AppSec start
25
- RuleManager.clearAllRules()
26
- return
32
+ async function enableAsync (_config) {
33
+ if (isEnabled) return
34
+
35
+ try {
36
+ await loadTemplatesAsync(_config)
37
+ const rules = await fs.promises.readFile(_config.appsec.rules || path.join(__dirname, 'recommended.json'))
38
+ enableFromRules(_config, JSON.parse(rules))
39
+ } catch (err) {
40
+ abortEnable(err)
27
41
  }
42
+ }
43
+
44
+ function enableFromRules (_config, rules) {
45
+ RuleManager.applyRules(rules, _config.appsec)
46
+ remoteConfig.enableAsmData(_config.appsec)
28
47
 
29
- Reporter.setRateLimit(config.appsec.rateLimit)
48
+ Reporter.setRateLimit(_config.appsec.rateLimit)
30
49
 
31
50
  incomingHttpRequestStart.subscribe(incomingHttpStartTranslator)
32
51
  incomingHttpRequestEnd.subscribe(incomingHttpEndTranslator)
@@ -36,27 +55,57 @@ function enable (config) {
36
55
  Gateway.manager.addresses.add(addresses.HTTP_INCOMING_ENDPOINT)
37
56
  Gateway.manager.addresses.add(addresses.HTTP_INCOMING_RESPONSE_HEADERS)
38
57
  Gateway.manager.addresses.add(addresses.HTTP_INCOMING_REMOTE_IP)
58
+
59
+ isEnabled = true
60
+ config = _config
39
61
  }
40
62
 
41
- function incomingHttpStartTranslator (data) {
42
- // TODO: get span from datadog-core storage instead
43
- const topSpan = web.root(data.req)
44
- if (topSpan) {
45
- topSpan.addTags({
46
- '_dd.appsec.enabled': 1,
47
- '_dd.runtime_family': 'nodejs'
48
- })
49
- }
63
+ function abortEnable (err) {
64
+ log.error('Unable to start AppSec')
65
+ log.error(err)
66
+
67
+ // abort AppSec start
68
+ RuleManager.clearAllRules()
69
+ remoteConfig.disableAsmData()
50
70
  }
51
71
 
52
- function incomingHttpEndTranslator (data) {
72
+ function incomingHttpStartTranslator ({ req, res, abortController }) {
73
+ const topSpan = web.root(req)
74
+ if (!topSpan) return
75
+
76
+ const clientIp = extractIp(config, req)
77
+
78
+ topSpan.addTags({
79
+ '_dd.appsec.enabled': 1,
80
+ '_dd.runtime_family': 'nodejs',
81
+ [HTTP_CLIENT_IP]: clientIp
82
+ })
83
+
53
84
  const store = Gateway.startContext()
54
85
 
55
- store.set('req', data.req)
56
- store.set('res', data.res)
86
+ store.set('req', req)
87
+ store.set('res', res)
57
88
 
58
89
  const context = store.get('context')
59
90
 
91
+ if (clientIp) {
92
+ const results = Gateway.propagate({
93
+ [addresses.HTTP_CLIENT_IP]: clientIp
94
+ }, context)
95
+
96
+ if (!results || !abortController) return
97
+
98
+ for (const entry of results) {
99
+ if (entry && entry.includes('block')) {
100
+ block(req, res, topSpan, abortController)
101
+ break
102
+ }
103
+ }
104
+ }
105
+ }
106
+
107
+ function incomingHttpEndTranslator (data) {
108
+ const context = Gateway.getContext()
60
109
  if (!context) return
61
110
 
62
111
  const requestHeaders = Object.assign({}, data.req.headers)
@@ -97,7 +146,7 @@ function incomingHttpEndTranslator (data) {
97
146
  payload[addresses.HTTP_INCOMING_COOKIES] = {}
98
147
 
99
148
  for (const k of Object.keys(data.req.cookies)) {
100
- payload[addresses.HTTP_INCOMING_COOKIES][k] = [ data.req.cookies[k] ]
149
+ payload[addresses.HTTP_INCOMING_COOKIES][k] = [data.req.cookies[k]]
101
150
  }
102
151
  }
103
152
 
@@ -107,7 +156,11 @@ function incomingHttpEndTranslator (data) {
107
156
  }
108
157
 
109
158
  function disable () {
159
+ isEnabled = false
160
+ config = null
161
+
110
162
  RuleManager.clearAllRules()
163
+ remoteConfig.disableAsmData()
111
164
 
112
165
  // Channel#unsubscribe() is undefined for non active channels
113
166
  if (incomingHttpRequestStart.hasSubscribers) incomingHttpRequestStart.unsubscribe(incomingHttpStartTranslator)
@@ -116,6 +169,7 @@ function disable () {
116
169
 
117
170
  module.exports = {
118
171
  enable,
172
+ enableAsync,
119
173
  disable,
120
174
  incomingHttpStartTranslator,
121
175
  incomingHttpEndTranslator
@@ -0,0 +1,98 @@
1
+ 'use strict'
2
+
3
+ const BlockList = require('./ip_blocklist')
4
+ const net = require('net')
5
+ const log = require('../log')
6
+
7
+ const ipHeaderList = [
8
+ 'x-forwarded-for',
9
+ 'x-real-ip',
10
+ 'client-ip',
11
+ 'x-forwarded',
12
+ 'x-cluster-client-ip',
13
+ 'forwarded-for',
14
+ 'forwarded',
15
+ 'via',
16
+ 'true-client-ip'
17
+ ]
18
+
19
+ const privateCIDRs = [
20
+ '127.0.0.0/8',
21
+ '10.0.0.0/8',
22
+ '172.16.0.0/12',
23
+ '192.168.0.0/16',
24
+ '169.254.0.0/16',
25
+ '::1/128',
26
+ 'fec0::/10',
27
+ 'fe80::/10',
28
+ 'fc00::/7',
29
+ 'fd00::/8'
30
+ ]
31
+
32
+ const privateIPMatcher = new BlockList()
33
+
34
+ for (const cidr of privateCIDRs) {
35
+ const [address, prefix] = cidr.split('/')
36
+
37
+ privateIPMatcher.addSubnet(address, parseInt(prefix), net.isIPv6(address) ? 'ipv6' : 'ipv4')
38
+ }
39
+
40
+ function extractIp (config, req) {
41
+ const headers = req.headers
42
+ if (config.clientIpHeader) {
43
+ if (!headers) return
44
+ const header = headers[config.clientIpHeader]
45
+ if (!header) return
46
+
47
+ return findFirstIp(header)
48
+ }
49
+
50
+ const foundHeaders = []
51
+ if (headers) {
52
+ for (let i = 0; i < ipHeaderList.length; i++) {
53
+ if (headers[ipHeaderList[i]]) {
54
+ foundHeaders.push(ipHeaderList[i])
55
+ }
56
+ }
57
+ }
58
+
59
+ if (foundHeaders.length === 1) {
60
+ const header = headers[foundHeaders[0]]
61
+ const firstIp = findFirstIp(header)
62
+
63
+ if (firstIp) return firstIp
64
+ } else if (foundHeaders.length > 1) {
65
+ log.error(`Cannot find client IP: multiple IP headers detected ${foundHeaders}`)
66
+ return
67
+ }
68
+
69
+ return req.socket && req.socket.remoteAddress
70
+ }
71
+
72
+ function findFirstIp (str) {
73
+ let firstPrivateIp
74
+ const splitted = str.split(',')
75
+
76
+ for (let i = 0; i < splitted.length; i++) {
77
+ const chunk = splitted[i].trim()
78
+
79
+ // TODO: strip port and interface data ?
80
+
81
+ const type = net.isIP(chunk)
82
+ if (!type) continue
83
+
84
+ if (!privateIPMatcher.check(chunk, type === 6 ? 'ipv6' : 'ipv4')) {
85
+ // it's public, return it immediately
86
+ return chunk
87
+ }
88
+
89
+ // it's private, only save the first one found
90
+ if (!firstPrivateIp) firstPrivateIp = chunk
91
+ }
92
+
93
+ return firstPrivateIp
94
+ }
95
+
96
+ module.exports = {
97
+ extractIp
98
+ }