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
@@ -29,9 +29,7 @@ class WAFCallback {
29
29
 
30
30
  this.wafTimeout = wafTimeout
31
31
 
32
- const version = this.ddwaf.constructor.version()
33
-
34
- Reporter.metricsQueue.set('_dd.appsec.waf.version', `${version.major}.${version.minor}.${version.patch}`)
32
+ Reporter.metricsQueue.set('_dd.appsec.waf.version', this.ddwaf.constructor.version())
35
33
 
36
34
  const { loaded, failed, errors } = this.ddwaf.rulesInfo
37
35
 
@@ -63,7 +61,7 @@ class WAFCallback {
63
61
 
64
62
  subscribedAddresses.add(address)
65
63
 
66
- Gateway.manager.addSubscription({ addresses: [ address ], callback })
64
+ Gateway.manager.addSubscription({ addresses: [address], callback })
67
65
  }
68
66
  }
69
67
  }
@@ -121,14 +119,18 @@ class WAFCallback {
121
119
  if (result.data && result.data !== '[]') {
122
120
  Reporter.reportAttack(result.data, store)
123
121
  }
122
+
123
+ return result.actions
124
+ }
125
+
126
+ updateRuleData (ruleData) {
127
+ this.ddwaf.updateRuleData(ruleData)
124
128
  }
125
129
 
126
130
  clear () {
127
131
  this.ddwaf.dispose()
128
132
 
129
133
  this.wafContextCache = new WeakMap()
130
-
131
- Gateway.manager.clear()
132
134
  }
133
135
  }
134
136
 
@@ -28,7 +28,7 @@ class SubscriptionManager {
28
28
  const list = this.addressToSubscriptions.get(address)
29
29
 
30
30
  if (list === undefined) {
31
- this.addressToSubscriptions.set(address, [ subscription ])
31
+ this.addressToSubscriptions.set(address, [subscription])
32
32
  } else {
33
33
  list.push(subscription)
34
34
  }
@@ -22,6 +22,10 @@ function getContext () {
22
22
  return store && store.get('context')
23
23
  }
24
24
 
25
+ function needsAddress (address) {
26
+ return manager.addresses.has(address)
27
+ }
28
+
25
29
  function propagate (data, context = getContext()) {
26
30
  if (!context) return
27
31
 
@@ -30,17 +34,18 @@ function propagate (data, context = getContext()) {
30
34
  for (let i = 0; i < keys.length; ++i) {
31
35
  const key = keys[i]
32
36
 
33
- if (manager.addresses.has(key)) {
37
+ if (needsAddress(key)) {
34
38
  context.setValue(key, data[key])
35
39
  }
36
40
  }
37
41
 
38
- context.dispatch()
42
+ return context.dispatch()
39
43
  }
40
44
 
41
45
  module.exports = {
42
46
  manager,
43
47
  startContext,
44
48
  getContext,
49
+ needsAddress,
45
50
  propagate
46
51
  }
@@ -26,7 +26,6 @@ function runSubscriptions (subscriptions, params) {
26
26
  result = subscription.callback.method(params, store)
27
27
  } catch (err) {
28
28
  // TODO: log ?
29
- result = {}
30
29
  }
31
30
 
32
31
  results.push(result)
@@ -1,4 +1,7 @@
1
1
  module.exports = {
2
2
  'WEAK_CIPHER_ANALYZER': require('./weak-cipher-analyzer'),
3
- 'WEAK_HASH_ANALYZER': require('./weak-hash-analyzer')
3
+ 'WEAK_HASH_ANALYZER': require('./weak-hash-analyzer'),
4
+ 'SQL_INJECTION_ANALYZER': require('./sql-injection-analyzer'),
5
+ 'COMMAND_INJECTION_ANALYZER': require('./command-injection-analyzer'),
6
+ 'LDAP_ANALYZER': require('./ldap-injection-analyzer')
4
7
  }
@@ -0,0 +1,11 @@
1
+ 'use strict'
2
+ const InjectionAnalyzer = require('./injection-analyzer')
3
+
4
+ class CommandInjectionAnalyzer extends InjectionAnalyzer {
5
+ constructor () {
6
+ super('COMMAND_INJECTION')
7
+ this.addSub('datadog:child_process:execution:start', ({ command }) => this.analyze(command))
8
+ }
9
+ }
10
+
11
+ module.exports = new CommandInjectionAnalyzer()
@@ -0,0 +1,19 @@
1
+ 'use strict'
2
+ const Analyzer = require('./vulnerability-analyzer')
3
+ const { isTainted, getRanges } = require('../taint-tracking/operations')
4
+
5
+ class InjectionAnalyzer extends Analyzer {
6
+ _isVulnerable (value, iastContext) {
7
+ if (value) {
8
+ return isTainted(iastContext, value)
9
+ }
10
+ return false
11
+ }
12
+
13
+ _getEvidence (value, iastContext) {
14
+ const ranges = getRanges(iastContext, value)
15
+ return { value, ranges }
16
+ }
17
+ }
18
+
19
+ module.exports = InjectionAnalyzer
@@ -0,0 +1,11 @@
1
+ 'use strict'
2
+ const InjectionAnalyzer = require('./injection-analyzer')
3
+
4
+ class LdapInjectionAnalyzer extends InjectionAnalyzer {
5
+ constructor () {
6
+ super('LDAP_INJECTION')
7
+ this.addSub('datadog:ldapjs:client:search', ({ base, filter }) => this.analyzeAll(base, filter))
8
+ }
9
+ }
10
+
11
+ module.exports = new LdapInjectionAnalyzer()
@@ -0,0 +1,13 @@
1
+ 'use strict'
2
+ const InjectionAnalyzer = require('./injection-analyzer')
3
+
4
+ class SqlInjectionAnalyzer extends InjectionAnalyzer {
5
+ constructor () {
6
+ super('SQL_INJECTION')
7
+ this.addSub('apm:mysql:query:start', ({ sql }) => this.analyze(sql))
8
+ this.addSub('apm:mysql2:query:start', ({ sql }) => this.analyze(sql))
9
+ this.addSub('apm:pg:query:start', ({ originalQuery }) => this.analyze(originalQuery))
10
+ }
11
+ }
12
+
13
+ module.exports = new SqlInjectionAnalyzer()
@@ -2,7 +2,8 @@
2
2
 
3
3
  const Plugin = require('../../../../src/plugins/plugin')
4
4
  const { storage } = require('../../../../../datadog-core')
5
- const { getFirstNonDDPathAndLine } = require('./../path-line')
5
+ const log = require('../../../log')
6
+ const { getFirstNonDDPathAndLine } = require('../path-line')
6
7
  const { createVulnerability, addVulnerability } = require('../vulnerability-reporter')
7
8
  const { getIastContext } = require('../iast-context')
8
9
  const overheadController = require('../overhead-controller')
@@ -13,18 +14,40 @@ class Analyzer extends Plugin {
13
14
  this._type = type
14
15
  }
15
16
 
17
+ _wrapHandler (handler) {
18
+ return (message, name) => {
19
+ try {
20
+ handler(message, name)
21
+ } catch (e) {
22
+ log.debug(e)
23
+ }
24
+ }
25
+ }
26
+
27
+ addSub (channelName, handler) {
28
+ super.addSub(channelName, this._wrapHandler(handler))
29
+ }
30
+
16
31
  _isVulnerable (value, context) {
17
32
  return false
18
33
  }
19
34
 
20
35
  _report (value, context) {
21
- const evidence = this._getEvidence(value)
36
+ const evidence = this._getEvidence(value, context)
22
37
  const location = this._getLocation()
23
38
  const spanId = context && context.rootSpan && context.rootSpan.context().toSpanId()
24
39
  const vulnerability = createVulnerability(this._type, evidence, spanId, location)
25
40
  addVulnerability(context, vulnerability)
26
41
  }
27
42
 
43
+ _reportIfVulnerable (value, context) {
44
+ if (this._isVulnerable(value, context) && this._checkOCE(context)) {
45
+ this._report(value, context)
46
+ return true
47
+ }
48
+ return false
49
+ }
50
+
28
51
  _getEvidence (value) {
29
52
  return { value }
30
53
  }
@@ -34,9 +57,24 @@ class Analyzer extends Plugin {
34
57
  }
35
58
 
36
59
  analyze (value) {
37
- const iastContext = getIastContext(storage.getStore())
38
- if (iastContext && this._isVulnerable(value, iastContext) && this._checkOCE(iastContext)) {
39
- this._report(value, iastContext)
60
+ const store = storage.getStore()
61
+ const iastContext = getIastContext(store)
62
+ if (store && !iastContext) return
63
+ this._reportIfVulnerable(value, iastContext)
64
+ }
65
+
66
+ analyzeAll (...values) {
67
+ const store = storage.getStore()
68
+ const iastContext = getIastContext(store)
69
+ if (store && !iastContext) return
70
+ for (let i = 0; i < values.length; i++) {
71
+ const value = values[i]
72
+ if (this._isVulnerable(value, iastContext)) {
73
+ if (this._checkOCE(iastContext)) {
74
+ this._report(value, iastContext)
75
+ }
76
+ break
77
+ }
40
78
  }
41
79
  }
42
80
 
@@ -1,4 +1,5 @@
1
1
  const IAST_CONTEXT_KEY = Symbol('_dd.iast.context')
2
+ const IAST_TRANSACTION_ID = Symbol('_dd.iast.transactionId')
2
3
 
3
4
  function getIastContext (store) {
4
5
  return store && store[IAST_CONTEXT_KEY]
@@ -46,5 +47,6 @@ module.exports = {
46
47
  getIastContext,
47
48
  saveIastContext,
48
49
  cleanIastContext,
49
- IAST_CONTEXT_KEY
50
+ IAST_CONTEXT_KEY,
51
+ IAST_TRANSACTION_ID
50
52
  }
@@ -1,25 +1,33 @@
1
- const { sendVulnerabilities } = require('./vulnerability-reporter')
1
+ const { sendVulnerabilities, setTracer } = require('./vulnerability-reporter')
2
2
  const { enableAllAnalyzers, disableAllAnalyzers } = require('./analyzers')
3
3
  const web = require('../../plugins/util/web')
4
4
  const { storage } = require('../../../../datadog-core')
5
5
  const overheadController = require('./overhead-controller')
6
6
  const dc = require('diagnostics_channel')
7
- const { saveIastContext, getIastContext, cleanIastContext } = require('./iast-context')
7
+ const iastContextFunctions = require('./iast-context')
8
+ const { enableTaintTracking, disableTaintTracking, createTransaction, removeTransaction } = require('./taint-tracking')
9
+
10
+ const IAST_ENABLED_TAG_KEY = '_dd.iast.enabled'
8
11
 
9
12
  // TODO Change to `apm:http:server:request:[start|close]` when the subscription
10
13
  // order of the callbacks can be enforce
11
14
  const requestStart = dc.channel('dd-trace:incomingHttpRequestStart')
12
15
  const requestClose = dc.channel('dd-trace:incomingHttpRequestEnd')
13
16
 
14
- function enable (config) {
17
+ function enable (config, _tracer) {
15
18
  enableAllAnalyzers()
19
+ enableTaintTracking()
16
20
  requestStart.subscribe(onIncomingHttpRequestStart)
17
21
  requestClose.subscribe(onIncomingHttpRequestEnd)
18
22
  overheadController.configure(config.iast)
23
+ overheadController.startGlobalContext()
24
+ setTracer(_tracer)
19
25
  }
20
26
 
21
27
  function disable () {
22
28
  disableAllAnalyzers()
29
+ disableTaintTracking()
30
+ overheadController.finishGlobalContext()
23
31
  if (requestStart.hasSubscribers) requestStart.unsubscribe(onIncomingHttpRequestStart)
24
32
  if (requestClose.hasSubscribers) requestClose.unsubscribe(onIncomingHttpRequestEnd)
25
33
  }
@@ -33,9 +41,15 @@ function onIncomingHttpRequestStart (data) {
33
41
  const rootSpan = topContext.span
34
42
  const isRequestAcquired = overheadController.acquireRequest(rootSpan)
35
43
  if (isRequestAcquired) {
36
- const iastContext = saveIastContext(store, topContext, { rootSpan, req: data.req })
44
+ const iastContext = iastContextFunctions.saveIastContext(store, topContext, { rootSpan, req: data.req })
45
+ createTransaction(rootSpan.context().toSpanId(), iastContext)
37
46
  overheadController.initializeRequestContext(iastContext)
38
47
  }
48
+ if (rootSpan.addTags) {
49
+ rootSpan.addTags({
50
+ [IAST_ENABLED_TAG_KEY]: isRequestAcquired ? '1' : '0'
51
+ })
52
+ }
39
53
  }
40
54
  }
41
55
  }
@@ -44,13 +58,15 @@ function onIncomingHttpRequestStart (data) {
44
58
  function onIncomingHttpRequestEnd (data) {
45
59
  if (data && data.req) {
46
60
  const store = storage.getStore()
47
- const iastContext = getIastContext(storage.getStore())
61
+ const iastContext = iastContextFunctions.getIastContext(storage.getStore())
48
62
  if (iastContext && iastContext.rootSpan) {
49
- overheadController.releaseRequest()
50
- sendVulnerabilities(iastContext, iastContext.rootSpan)
63
+ const vulnerabilities = iastContext.vulnerabilities
64
+ const rootSpan = iastContext.rootSpan
65
+ sendVulnerabilities(vulnerabilities, rootSpan)
66
+ removeTransaction(iastContext)
51
67
  }
52
68
  // TODO web.getContext(data.req) is required when the request is aborted
53
- if (cleanIastContext(store, web.getContext(data.req), iastContext)) {
69
+ if (iastContextFunctions.cleanIastContext(store, web.getContext(data.req), iastContext)) {
54
70
  overheadController.releaseRequest()
55
71
  }
56
72
  }
@@ -2,8 +2,11 @@
2
2
 
3
3
  const OVERHEAD_CONTROLLER_CONTEXT_KEY = 'oce'
4
4
  const REPORT_VULNERABILITY = 'REPORT_VULNERABILITY'
5
+ const INTERVAL_RESET_GLOBAL_CONTEXT = 60 * 1000
5
6
 
6
7
  const GLOBAL_OCE_CONTEXT = {}
8
+
9
+ let resetGlobalContextInterval
7
10
  let config = {}
8
11
  let availableRequest = 0
9
12
  const OPERATIONS = {
@@ -80,11 +83,27 @@ function configure (cfg) {
80
83
  availableRequest = config.maxConcurrentRequests
81
84
  }
82
85
 
83
- _resetGlobalContext()
86
+ function startGlobalContext () {
87
+ if (resetGlobalContextInterval) return
88
+ _resetGlobalContext()
89
+ resetGlobalContextInterval = setInterval(() => {
90
+ _resetGlobalContext()
91
+ }, INTERVAL_RESET_GLOBAL_CONTEXT)
92
+ resetGlobalContextInterval.unref && resetGlobalContextInterval.unref()
93
+ }
94
+
95
+ function finishGlobalContext () {
96
+ if (resetGlobalContextInterval) {
97
+ clearInterval(resetGlobalContextInterval)
98
+ resetGlobalContextInterval = null
99
+ }
100
+ }
84
101
 
85
102
  module.exports = {
86
103
  OVERHEAD_CONTROLLER_CONTEXT_KEY,
87
104
  OPERATIONS,
105
+ startGlobalContext,
106
+ finishGlobalContext,
88
107
  _resetGlobalContext,
89
108
  initializeRequestContext,
90
109
  hasQuota,
@@ -1,4 +1,5 @@
1
1
  const path = require('path')
2
+ const process = require('process')
2
3
  const pathLine = {
3
4
  getFirstNonDDPathAndLine,
4
5
  getFirstNonDDPathAndLineFromCallsites, // Exported only for test purposes
@@ -7,28 +8,35 @@ const pathLine = {
7
8
  }
8
9
 
9
10
  const EXCLUDED_PATHS = [
10
- '/node_modules/diagnostics_channel'
11
+ path.join(path.sep, 'node_modules', 'diagnostics_channel')
11
12
  ]
12
13
  const EXCLUDED_PATH_PREFIXES = [
13
14
  'node:diagnostics_channel',
14
- 'diagnostics_channel'
15
+ 'diagnostics_channel',
16
+ 'node:child_process',
17
+ 'child_process',
18
+ 'node:async_hooks',
19
+ 'async_hooks'
15
20
  ]
16
21
 
17
22
  function calculateDDBasePath (dirname) {
18
23
  const dirSteps = dirname.split(path.sep)
19
- const packagesIndex = dirSteps.indexOf('packages')
24
+ const packagesIndex = dirSteps.lastIndexOf('packages')
20
25
  return dirSteps.slice(0, packagesIndex).join(path.sep) + path.sep
21
26
  }
22
27
 
23
28
  function getCallSiteInfo () {
24
29
  const previousPrepareStackTrace = Error.prepareStackTrace
30
+ const previousStackTraceLimit = Error.stackTraceLimit
25
31
  let callsiteList
32
+ Error.stackTraceLimit = 100
26
33
  Error.prepareStackTrace = function (_, callsites) {
27
34
  callsiteList = callsites
28
35
  }
29
36
  const e = new Error()
30
37
  e.stack
31
38
  Error.prepareStackTrace = previousPrepareStackTrace
39
+ Error.stackTraceLimit = previousStackTraceLimit
32
40
  return callsiteList
33
41
  }
34
42
 
@@ -36,10 +44,10 @@ function getFirstNonDDPathAndLineFromCallsites (callsites) {
36
44
  if (callsites) {
37
45
  for (let i = 0; i < callsites.length; i++) {
38
46
  const callsite = callsites[i]
39
- const path = callsite.getFileName()
40
- if (!isExcluded(callsite) && path.indexOf(pathLine.ddBasePath) === -1) {
47
+ const filepath = callsite.getFileName()
48
+ if (!isExcluded(callsite) && filepath.indexOf(pathLine.ddBasePath) === -1) {
41
49
  return {
42
- path,
50
+ path: path.relative(process.cwd(), filepath),
43
51
  line: callsite.getLineNumber()
44
52
  }
45
53
  }
@@ -51,6 +59,9 @@ function getFirstNonDDPathAndLineFromCallsites (callsites) {
51
59
  function isExcluded (callsite) {
52
60
  if (callsite.isNative()) return true
53
61
  const filename = callsite.getFileName()
62
+ if (!filename) {
63
+ return true
64
+ }
54
65
  for (let i = 0; i < EXCLUDED_PATHS.length; i++) {
55
66
  if (filename.indexOf(EXCLUDED_PATHS[i]) > -1) {
56
67
  return true
@@ -0,0 +1,17 @@
1
+ 'use strict'
2
+
3
+ const csiMethods = [
4
+ { src: 'plusOperator', operator: true },
5
+ { src: 'trim' },
6
+ { src: 'trimStart', dst: 'trim' },
7
+ { src: 'trimEnd' },
8
+ { src: 'concat' },
9
+ { src: 'substring' },
10
+ { src: 'substr' },
11
+ { src: 'slice' },
12
+ { src: 'replace' }
13
+ ]
14
+
15
+ module.exports = {
16
+ csiMethods
17
+ }
@@ -0,0 +1,16 @@
1
+ 'use strict'
2
+
3
+ const NODE_MODULES = 'node_modules'
4
+
5
+ const isPrivateModule = function (file) {
6
+ return file && file.indexOf(NODE_MODULES) === -1
7
+ }
8
+
9
+ const isNotLibraryFile = function (file) {
10
+ return file && file.indexOf('dd-trace-js') === -1 && file.indexOf('dd-trace') === -1
11
+ }
12
+
13
+ module.exports = {
14
+ isPrivateModule,
15
+ isNotLibraryFile
16
+ }
@@ -0,0 +1,18 @@
1
+ const { enableRewriter, disableRewriter } = require('./rewriter')
2
+ const { createTransaction, removeTransaction, enableTaintOperations, disableTaintOperations } = require('./operations')
3
+ const taintTrackingPlugin = require('./plugin')
4
+
5
+ module.exports = {
6
+ enableTaintTracking () {
7
+ enableRewriter()
8
+ enableTaintOperations()
9
+ taintTrackingPlugin.enable()
10
+ },
11
+ disableTaintTracking () {
12
+ disableRewriter()
13
+ disableTaintOperations()
14
+ taintTrackingPlugin.disable()
15
+ },
16
+ createTransaction: createTransaction,
17
+ removeTransaction: removeTransaction
18
+ }
@@ -0,0 +1,98 @@
1
+ const TaintedUtils = require('@datadog/native-iast-taint-tracking')
2
+ const { IAST_TRANSACTION_ID } = require('../iast-context')
3
+ const { TaintTracking, TaintTrackingDummy } = require('./taint-tracking-impl')
4
+
5
+ function createTransaction (id, iastContext) {
6
+ if (id && iastContext) {
7
+ iastContext[IAST_TRANSACTION_ID] = TaintedUtils.createTransaction(id)
8
+ }
9
+ }
10
+
11
+ function removeTransaction (iastContext) {
12
+ if (iastContext && iastContext[IAST_TRANSACTION_ID]) {
13
+ const transactionId = iastContext[IAST_TRANSACTION_ID]
14
+ TaintedUtils.removeTransaction(transactionId)
15
+ delete iastContext[IAST_TRANSACTION_ID]
16
+ }
17
+ }
18
+
19
+ function newTaintedString (iastContext, string, name, type) {
20
+ let result = string
21
+ if (iastContext && iastContext[IAST_TRANSACTION_ID]) {
22
+ const transactionId = iastContext[IAST_TRANSACTION_ID]
23
+ result = TaintedUtils.newTaintedString(transactionId, string, name, type)
24
+ } else {
25
+ result = string
26
+ }
27
+ return result
28
+ }
29
+
30
+ function taintObject (iastContext, object, type) {
31
+ let result = object
32
+ if (iastContext && iastContext[IAST_TRANSACTION_ID]) {
33
+ const transactionId = iastContext[IAST_TRANSACTION_ID]
34
+ const queue = [{ parent: null, property: null, value: object }]
35
+ const visited = new WeakSet()
36
+ while (queue.length > 0) {
37
+ const { parent, property, value } = queue.pop()
38
+ if (typeof value === 'string') {
39
+ const tainted = TaintedUtils.newTaintedString(transactionId, value, property, type)
40
+ if (!parent) {
41
+ result = tainted
42
+ } else {
43
+ parent[property] = tainted
44
+ }
45
+ } else if (typeof value === 'object' && !visited.has(value)) {
46
+ visited.add(value)
47
+ const keys = Object.keys(value)
48
+ for (let i = 0; i < keys.length; i++) {
49
+ const key = keys[i]
50
+ queue.push({ parent: value, property: property ? `${property}.${key}` : key, value: value[key] })
51
+ }
52
+ }
53
+ }
54
+ }
55
+ return result
56
+ }
57
+
58
+ function isTainted (iastContext, string) {
59
+ let result = false
60
+ if (iastContext && iastContext[IAST_TRANSACTION_ID]) {
61
+ const transactionId = iastContext[IAST_TRANSACTION_ID]
62
+ result = TaintedUtils.isTainted(transactionId, string)
63
+ } else {
64
+ result = false
65
+ }
66
+ return result
67
+ }
68
+
69
+ function getRanges (iastContext, string) {
70
+ let result = []
71
+ if (iastContext && iastContext[IAST_TRANSACTION_ID]) {
72
+ const transactionId = iastContext[IAST_TRANSACTION_ID]
73
+ result = TaintedUtils.getRanges(transactionId, string)
74
+ } else {
75
+ result = []
76
+ }
77
+ return result
78
+ }
79
+
80
+ function enableTaintOperations () {
81
+ global._ddiast = TaintTracking
82
+ }
83
+
84
+ function disableTaintOperations () {
85
+ global._ddiast = TaintTrackingDummy
86
+ }
87
+
88
+ module.exports = {
89
+ createTransaction,
90
+ removeTransaction,
91
+ newTaintedString,
92
+ taintObject,
93
+ isTainted,
94
+ getRanges,
95
+ enableTaintOperations,
96
+ disableTaintOperations,
97
+ IAST_TRANSACTION_ID
98
+ }
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ HTTP_REQUEST_BODY: 'http.request.body',
3
+ HTTP_REQUEST_PARAMETER: 'http.request.parameter'
4
+ }
@@ -0,0 +1,38 @@
1
+ const Plugin = require('../../../plugins/plugin')
2
+ const { getIastContext } = require('../iast-context')
3
+ const { storage } = require('../../../../../datadog-core')
4
+ const { HTTP_REQUEST_PARAMETER, HTTP_REQUEST_BODY } = require('./origin-types')
5
+ const { taintObject } = require('./operations')
6
+
7
+ class TaintTrackingPlugin extends Plugin {
8
+ constructor () {
9
+ super()
10
+ this._type = 'taint-tracking'
11
+ this.addSub(
12
+ 'datadog:body-parser:read:finish',
13
+ ({ request }) => this._taintTrackingHandler(HTTP_REQUEST_BODY, request, 'body')
14
+ )
15
+ this.addSub(
16
+ 'datadog:qs:parse:finish',
17
+ ({ qs }) => this._taintTrackingHandler(HTTP_REQUEST_PARAMETER, qs))
18
+ }
19
+
20
+ _taintTrackingHandler (type, target, property) {
21
+ const iastContext = getIastContext(storage.getStore())
22
+ if (!property) {
23
+ target = taintObject(iastContext, target, type)
24
+ } else {
25
+ target[property] = taintObject(iastContext, target[property], type)
26
+ }
27
+ }
28
+
29
+ enable () {
30
+ this.configure(true)
31
+ }
32
+
33
+ disable () {
34
+ this.configure(false)
35
+ }
36
+ }
37
+
38
+ module.exports = new TaintTrackingPlugin()