dd-trace 4.18.0 → 5.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/CONTRIBUTING.md +98 -0
  2. package/LICENSE-3rdparty.csv +4 -5
  3. package/MIGRATING.md +15 -0
  4. package/README.md +20 -140
  5. package/ci/cypress/after-run.js +1 -0
  6. package/ci/cypress/after-spec.js +1 -0
  7. package/ci/init.js +1 -4
  8. package/ext/kinds.d.ts +1 -0
  9. package/ext/kinds.js +2 -1
  10. package/ext/tags.d.ts +2 -1
  11. package/ext/tags.js +6 -1
  12. package/index.d.ts +1523 -1460
  13. package/package.json +19 -19
  14. package/packages/datadog-core/src/storage/async_resource.js +1 -1
  15. package/packages/datadog-core/src/utils/src/get.js +11 -0
  16. package/packages/datadog-core/src/utils/src/has.js +14 -0
  17. package/packages/datadog-core/src/utils/src/kebabcase.js +16 -0
  18. package/packages/datadog-core/src/utils/src/pick.js +11 -0
  19. package/packages/datadog-core/src/utils/src/set.js +16 -0
  20. package/packages/datadog-core/src/utils/src/uniq.js +5 -0
  21. package/packages/datadog-esbuild/index.js +1 -20
  22. package/packages/datadog-instrumentations/src/aerospike.js +47 -0
  23. package/packages/datadog-instrumentations/src/amqplib.js +2 -2
  24. package/packages/datadog-instrumentations/src/apollo-server-core.js +41 -0
  25. package/packages/datadog-instrumentations/src/apollo-server.js +83 -0
  26. package/packages/datadog-instrumentations/src/child_process.js +150 -0
  27. package/packages/datadog-instrumentations/src/couchbase.js +5 -4
  28. package/packages/datadog-instrumentations/src/crypto.js +2 -1
  29. package/packages/datadog-instrumentations/src/cucumber.js +163 -46
  30. package/packages/datadog-instrumentations/src/dns.js +2 -1
  31. package/packages/datadog-instrumentations/src/express.js +20 -0
  32. package/packages/datadog-instrumentations/src/graphql.js +18 -4
  33. package/packages/datadog-instrumentations/src/grpc/client.js +56 -36
  34. package/packages/datadog-instrumentations/src/grpc/server.js +3 -1
  35. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +1 -2
  36. package/packages/datadog-instrumentations/src/helpers/hooks.js +12 -3
  37. package/packages/datadog-instrumentations/src/helpers/instrument.js +9 -4
  38. package/packages/datadog-instrumentations/src/helpers/register.js +19 -3
  39. package/packages/datadog-instrumentations/src/http/client.js +12 -2
  40. package/packages/datadog-instrumentations/src/http/server.js +7 -4
  41. package/packages/datadog-instrumentations/src/http2/client.js +3 -1
  42. package/packages/datadog-instrumentations/src/http2/server.js +3 -1
  43. package/packages/datadog-instrumentations/src/jest.js +239 -52
  44. package/packages/datadog-instrumentations/src/kafkajs.js +27 -0
  45. package/packages/datadog-instrumentations/src/mocha.js +154 -18
  46. package/packages/datadog-instrumentations/src/mongodb-core.js +34 -3
  47. package/packages/datadog-instrumentations/src/mongoose.js +23 -10
  48. package/packages/datadog-instrumentations/src/mquery.js +65 -0
  49. package/packages/datadog-instrumentations/src/net.js +10 -2
  50. package/packages/datadog-instrumentations/src/next.js +35 -9
  51. package/packages/datadog-instrumentations/src/playwright.js +110 -16
  52. package/packages/datadog-instrumentations/src/restify.js +14 -1
  53. package/packages/datadog-instrumentations/src/rhea.js +15 -9
  54. package/packages/datadog-plugin-aerospike/src/index.js +113 -0
  55. package/packages/datadog-plugin-amqplib/src/consumer.js +14 -1
  56. package/packages/datadog-plugin-amqplib/src/producer.js +13 -1
  57. package/packages/datadog-plugin-aws-sdk/src/base.js +3 -2
  58. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +163 -27
  59. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +46 -8
  60. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +129 -22
  61. package/packages/datadog-plugin-child_process/src/index.js +91 -0
  62. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +125 -0
  63. package/packages/datadog-plugin-cucumber/src/index.js +70 -13
  64. package/packages/datadog-plugin-cypress/src/after-run.js +3 -0
  65. package/packages/datadog-plugin-cypress/src/after-spec.js +3 -0
  66. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +625 -0
  67. package/packages/datadog-plugin-cypress/src/plugin.js +6 -454
  68. package/packages/datadog-plugin-cypress/src/support.js +50 -3
  69. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -0
  70. package/packages/datadog-plugin-graphql/src/index.js +1 -6
  71. package/packages/datadog-plugin-graphql/src/resolve.js +28 -18
  72. package/packages/datadog-plugin-grpc/src/client.js +16 -2
  73. package/packages/datadog-plugin-grpc/src/util.js +1 -1
  74. package/packages/datadog-plugin-http/src/client.js +19 -2
  75. package/packages/datadog-plugin-jest/src/index.js +118 -12
  76. package/packages/datadog-plugin-jest/src/util.js +38 -16
  77. package/packages/datadog-plugin-kafkajs/src/consumer.js +76 -6
  78. package/packages/datadog-plugin-kafkajs/src/producer.js +64 -8
  79. package/packages/datadog-plugin-mocha/src/index.js +87 -17
  80. package/packages/datadog-plugin-next/src/index.js +40 -14
  81. package/packages/datadog-plugin-playwright/src/index.js +71 -8
  82. package/packages/datadog-plugin-rhea/src/consumer.js +16 -1
  83. package/packages/datadog-plugin-rhea/src/producer.js +10 -0
  84. package/packages/dd-trace/src/appsec/activation.js +29 -0
  85. package/packages/dd-trace/src/appsec/addresses.js +5 -1
  86. package/packages/dd-trace/src/appsec/api_security_sampler.js +61 -0
  87. package/packages/dd-trace/src/appsec/blocked_templates.js +4 -1
  88. package/packages/dd-trace/src/appsec/blocking.js +95 -43
  89. package/packages/dd-trace/src/appsec/channels.js +7 -3
  90. package/packages/dd-trace/src/appsec/graphql.js +146 -0
  91. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +2 -0
  92. package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +1 -1
  93. package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +105 -0
  94. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +22 -17
  95. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +7 -28
  96. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +10 -6
  97. package/packages/dd-trace/src/appsec/iast/analyzers/weak-randomness-analyzer.js +19 -0
  98. package/packages/dd-trace/src/appsec/iast/context/context-plugin.js +90 -0
  99. package/packages/dd-trace/src/appsec/iast/context/kafka-ctx-plugin.js +14 -0
  100. package/packages/dd-trace/src/appsec/iast/iast-log.js +1 -1
  101. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +13 -2
  102. package/packages/dd-trace/src/appsec/iast/index.js +15 -5
  103. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +1 -1
  104. package/packages/dd-trace/src/appsec/iast/path-line.js +1 -1
  105. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +2 -0
  106. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +10 -0
  107. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +53 -0
  108. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +10 -46
  109. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +13 -9
  110. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +47 -0
  111. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +19 -6
  112. package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +3 -1
  113. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +41 -3
  114. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/constants.js +7 -0
  115. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +12 -19
  116. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/header-sensitive-analyzer.js +20 -0
  117. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +6 -10
  118. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +18 -25
  119. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +79 -85
  120. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +27 -36
  121. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +14 -11
  122. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
  123. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +2 -0
  124. package/packages/dd-trace/src/appsec/index.js +49 -33
  125. package/packages/dd-trace/src/appsec/recommended.json +1763 -106
  126. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +7 -1
  127. package/packages/dd-trace/src/appsec/remote_config/index.js +42 -16
  128. package/packages/dd-trace/src/appsec/remote_config/manager.js +9 -8
  129. package/packages/dd-trace/src/appsec/reporter.js +51 -34
  130. package/packages/dd-trace/src/appsec/rule_manager.js +11 -8
  131. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  132. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +28 -13
  133. package/packages/dd-trace/src/appsec/waf/waf_manager.js +0 -1
  134. package/packages/dd-trace/src/ci-visibility/{intelligent-test-runner/get-itr-configuration.js → early-flake-detection/get-known-tests.js} +17 -22
  135. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +25 -6
  136. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +30 -1
  137. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +2 -0
  138. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +30 -1
  139. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +95 -37
  140. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +134 -61
  141. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +37 -4
  142. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +131 -0
  143. package/packages/dd-trace/src/ci-visibility/telemetry.js +130 -0
  144. package/packages/dd-trace/src/config.js +561 -470
  145. package/packages/dd-trace/src/data_streams_context.js +1 -1
  146. package/packages/dd-trace/src/datastreams/pathway.js +58 -1
  147. package/packages/dd-trace/src/datastreams/processor.js +196 -27
  148. package/packages/dd-trace/src/datastreams/writer.js +11 -5
  149. package/packages/dd-trace/src/dogstatsd.js +3 -5
  150. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +44 -6
  151. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +14 -0
  152. package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +4 -0
  153. package/packages/dd-trace/src/exporters/common/form-data.js +4 -0
  154. package/packages/dd-trace/src/exporters/common/request.js +21 -3
  155. package/packages/dd-trace/src/format.js +30 -2
  156. package/packages/dd-trace/src/id.js +12 -0
  157. package/packages/dd-trace/src/iitm.js +1 -1
  158. package/packages/dd-trace/src/log/channels.js +1 -1
  159. package/packages/dd-trace/src/noop/proxy.js +4 -0
  160. package/packages/dd-trace/src/noop/span.js +1 -0
  161. package/packages/dd-trace/src/opentelemetry/span.js +104 -4
  162. package/packages/dd-trace/src/opentelemetry/tracer.js +9 -10
  163. package/packages/dd-trace/src/opentracing/propagation/text_map.js +16 -7
  164. package/packages/dd-trace/src/opentracing/span.js +48 -4
  165. package/packages/dd-trace/src/opentracing/span_context.js +15 -6
  166. package/packages/dd-trace/src/opentracing/tracer.js +4 -3
  167. package/packages/dd-trace/src/plugin_manager.js +1 -1
  168. package/packages/dd-trace/src/plugins/ci_plugin.js +78 -19
  169. package/packages/dd-trace/src/plugins/database.js +1 -1
  170. package/packages/dd-trace/src/plugins/index.js +7 -0
  171. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  172. package/packages/dd-trace/src/plugins/util/ci.js +6 -19
  173. package/packages/dd-trace/src/plugins/util/git.js +104 -22
  174. package/packages/dd-trace/src/plugins/util/ip_extractor.js +7 -6
  175. package/packages/dd-trace/src/plugins/util/test.js +60 -10
  176. package/packages/dd-trace/src/plugins/util/url.js +26 -0
  177. package/packages/dd-trace/src/plugins/util/user-provided-git.js +4 -16
  178. package/packages/dd-trace/src/plugins/util/web.js +1 -1
  179. package/packages/dd-trace/src/priority_sampler.js +30 -38
  180. package/packages/dd-trace/src/profiler.js +5 -3
  181. package/packages/dd-trace/src/profiling/config.js +77 -24
  182. package/packages/dd-trace/src/profiling/exporters/agent.js +77 -31
  183. package/packages/dd-trace/src/profiling/exporters/file.js +2 -1
  184. package/packages/dd-trace/src/profiling/profiler.js +33 -22
  185. package/packages/dd-trace/src/profiling/profilers/events.js +270 -0
  186. package/packages/dd-trace/src/profiling/profilers/shared.js +45 -0
  187. package/packages/dd-trace/src/profiling/profilers/space.js +18 -2
  188. package/packages/dd-trace/src/profiling/profilers/wall.js +146 -70
  189. package/packages/dd-trace/src/proxy.js +56 -24
  190. package/packages/dd-trace/src/ritm.js +1 -1
  191. package/packages/dd-trace/src/sampling_rule.js +130 -0
  192. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +5 -0
  193. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
  194. package/packages/dd-trace/src/span_processor.js +9 -1
  195. package/packages/dd-trace/src/span_sampler.js +6 -64
  196. package/packages/dd-trace/src/spanleak.js +98 -0
  197. package/packages/dd-trace/src/startup-log.js +7 -1
  198. package/packages/dd-trace/src/telemetry/dependencies.js +56 -10
  199. package/packages/dd-trace/src/telemetry/index.js +182 -53
  200. package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
  201. package/packages/dd-trace/src/telemetry/send-data.js +65 -7
  202. package/packages/dd-trace/src/tracer.js +12 -5
  203. package/register.js +4 -0
  204. package/scripts/install_plugin_modules.js +11 -3
  205. package/scripts/st.js +105 -0
  206. package/packages/datadog-instrumentations/src/child-process.js +0 -30
  207. package/packages/dd-trace/src/plugins/util/exec.js +0 -13
  208. package/packages/diagnostics_channel/index.js +0 -3
  209. package/packages/diagnostics_channel/src/index.js +0 -121
@@ -9,5 +9,11 @@ module.exports = {
9
9
  ASM_USER_BLOCKING: 1n << 7n,
10
10
  ASM_CUSTOM_RULES: 1n << 8n,
11
11
  ASM_CUSTOM_BLOCKING_RESPONSE: 1n << 9n,
12
- ASM_TRUSTED_IPS: 1n << 10n
12
+ ASM_TRUSTED_IPS: 1n << 10n,
13
+ ASM_API_SECURITY_SAMPLE_RATE: 1n << 11n,
14
+ APM_TRACING_SAMPLE_RATE: 1n << 12n,
15
+ APM_TRACING_LOGS_INJECTION: 1n << 13n,
16
+ APM_TRACING_HTTP_HEADER_TAGS: 1n << 14n,
17
+ APM_TRACING_CUSTOM_TAGS: 1n << 15n,
18
+ APM_TRACING_ENABLED: 1n << 19n
13
19
  }
@@ -1,40 +1,66 @@
1
1
  'use strict'
2
2
 
3
+ const Activation = require('../activation')
4
+
3
5
  const RemoteConfigManager = require('./manager')
4
6
  const RemoteConfigCapabilities = require('./capabilities')
7
+ const apiSecuritySampler = require('../api_security_sampler')
5
8
 
6
9
  let rc
7
10
 
8
11
  function enable (config) {
9
12
  rc = new RemoteConfigManager(config)
13
+ rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_CUSTOM_TAGS, true)
14
+ rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_HTTP_HEADER_TAGS, true)
15
+ rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_LOGS_INJECTION, true)
16
+ rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_SAMPLE_RATE, true)
17
+ rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_ENABLED, true)
18
+
19
+ const activation = Activation.fromConfig(config)
10
20
 
11
- if (config.appsec.enabled === undefined) { // only activate ASM_FEATURES when conf is not set locally
12
- rc.updateCapabilities(RemoteConfigCapabilities.ASM_ACTIVATION, true)
21
+ if (activation !== Activation.DISABLED) {
22
+ if (activation === Activation.ONECLICK) {
23
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_ACTIVATION, true)
24
+ }
13
25
 
14
- rc.on('ASM_FEATURES', (action, conf) => {
15
- if (conf && conf.asm && typeof conf.asm.enabled === 'boolean') {
16
- let shouldEnable
26
+ if (config.appsec.apiSecurity?.enabled) {
27
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_API_SECURITY_SAMPLE_RATE, true)
28
+ }
17
29
 
18
- if (action === 'apply' || action === 'modify') {
19
- shouldEnable = conf.asm.enabled // take control
20
- } else {
21
- shouldEnable = config.appsec.enabled // give back control to local config
22
- }
30
+ rc.on('ASM_FEATURES', (action, rcConfig) => {
31
+ if (!rcConfig) return
23
32
 
24
- if (shouldEnable) {
25
- require('..').enable(config)
26
- } else {
27
- require('..').disable()
28
- }
33
+ if (activation === Activation.ONECLICK) {
34
+ enableOrDisableAppsec(action, rcConfig, config)
29
35
  }
36
+
37
+ apiSecuritySampler.setRequestSampling(rcConfig.api_security?.request_sample_rate)
30
38
  })
31
39
  }
32
40
 
33
41
  return rc
34
42
  }
35
43
 
44
+ function enableOrDisableAppsec (action, rcConfig, config) {
45
+ if (typeof rcConfig.asm?.enabled === 'boolean') {
46
+ let shouldEnable
47
+
48
+ if (action === 'apply' || action === 'modify') {
49
+ shouldEnable = rcConfig.asm.enabled // take control
50
+ } else {
51
+ shouldEnable = config.appsec.enabled // give back control to local config
52
+ }
53
+
54
+ if (shouldEnable) {
55
+ require('..').enable(config)
56
+ } else {
57
+ require('..').disable()
58
+ }
59
+ }
60
+ }
61
+
36
62
  function enableWafUpdate (appsecConfig) {
37
- if (rc && appsecConfig && !appsecConfig.customRulesProvided) {
63
+ if (rc && appsecConfig && !appsecConfig.rules) {
38
64
  // dirty require to make startup faster for serverless
39
65
  const RuleManager = require('../rule_manager')
40
66
 
@@ -25,7 +25,8 @@ class RemoteConfigManager extends EventEmitter {
25
25
  super()
26
26
 
27
27
  const pollInterval = Math.floor(config.remoteConfig.pollInterval * 1000)
28
- const url = config.url || new URL(format({
28
+
29
+ this.url = config.url || new URL(format({
29
30
  protocol: 'http:',
30
31
  hostname: config.hostname || 'localhost',
31
32
  port: config.port
@@ -33,12 +34,6 @@ class RemoteConfigManager extends EventEmitter {
33
34
 
34
35
  this.scheduler = new Scheduler((cb) => this.poll(cb), pollInterval)
35
36
 
36
- this.requestOptions = {
37
- url,
38
- method: 'POST',
39
- path: '/v0.7/config'
40
- }
41
-
42
37
  this.state = {
43
38
  client: {
44
39
  state: { // updated by `parseConfig()`
@@ -122,7 +117,13 @@ class RemoteConfigManager extends EventEmitter {
122
117
  }
123
118
 
124
119
  poll (cb) {
125
- request(this.getPayload(), this.requestOptions, (err, data, statusCode) => {
120
+ const options = {
121
+ url: this.url,
122
+ method: 'POST',
123
+ path: '/v0.7/config'
124
+ }
125
+
126
+ request(this.getPayload(), options, (err, data, statusCode) => {
126
127
  // 404 means RC is disabled, ignore it
127
128
  if (statusCode === 404) return cb()
128
129
 
@@ -3,64 +3,62 @@
3
3
  const Limiter = require('../rate_limiter')
4
4
  const { storage } = require('../../../datadog-core')
5
5
  const web = require('../plugins/util/web')
6
+ const { ipHeaderList } = require('../plugins/util/ip_extractor')
6
7
  const {
7
8
  incrementWafInitMetric,
8
9
  updateWafRequestsMetricTags,
9
10
  incrementWafUpdatesMetric,
10
11
  incrementWafRequestsMetric
11
12
  } = require('./telemetry')
13
+ const zlib = require('zlib')
12
14
 
13
15
  // default limiter, configurable with setRateLimit()
14
16
  let limiter = new Limiter(100)
15
17
 
16
- // TODO: use precomputed maps instead
17
- const REQUEST_HEADERS_PASSLIST = [
18
- 'accept',
19
- 'accept-encoding',
20
- 'accept-language',
18
+ const metricsQueue = new Map()
19
+
20
+ const contentHeaderList = [
21
21
  'content-encoding',
22
22
  'content-language',
23
23
  'content-length',
24
- 'content-type',
25
- 'forwarded',
26
- 'forwarded-for',
24
+ 'content-type'
25
+ ]
26
+
27
+ const REQUEST_HEADERS_MAP = mapHeaderAndTags([
28
+ 'accept',
29
+ 'accept-encoding',
30
+ 'accept-language',
27
31
  'host',
28
- 'true-client-ip',
32
+ 'forwarded',
29
33
  'user-agent',
30
34
  'via',
31
- 'x-client-ip',
32
- 'x-cluster-client-ip',
33
- 'x-forwarded',
34
- 'x-forwarded-for',
35
- 'x-real-ip'
36
- ]
35
+ 'x-amzn-trace-id',
37
36
 
38
- const RESPONSE_HEADERS_PASSLIST = [
39
- 'content-encoding',
40
- 'content-language',
41
- 'content-length',
42
- 'content-type'
43
- ]
37
+ ...ipHeaderList,
38
+ ...contentHeaderList
39
+ ], 'http.request.headers.')
44
40
 
45
- const metricsQueue = new Map()
41
+ const RESPONSE_HEADERS_MAP = mapHeaderAndTags(contentHeaderList, 'http.response.headers.')
46
42
 
47
- function filterHeaders (headers, passlist, prefix) {
43
+ function mapHeaderAndTags (headerList, tagPrefix) {
44
+ return new Map(headerList.map(headerName => [headerName, `${tagPrefix}${formatHeaderName(headerName)}`]))
45
+ }
46
+
47
+ function filterHeaders (headers, map) {
48
48
  const result = {}
49
49
 
50
50
  if (!headers) return result
51
51
 
52
- for (let i = 0; i < passlist.length; ++i) {
53
- const headerName = passlist[i]
54
-
55
- if (headers[headerName]) {
56
- result[`${prefix}${formatHeaderName(headerName)}`] = '' + headers[headerName]
52
+ for (const [headerName, tagName] of map) {
53
+ const headerValue = headers[headerName]
54
+ if (headerValue) {
55
+ result[tagName] = '' + headerValue
57
56
  }
58
57
  }
59
58
 
60
59
  return result
61
60
  }
62
61
 
63
- // TODO: this can be precomputed at start time
64
62
  function formatHeaderName (name) {
65
63
  return name
66
64
  .trim()
@@ -86,7 +84,7 @@ function reportWafInit (wafVersion, rulesVersion, diagnosticsRules = {}) {
86
84
  function reportMetrics (metrics) {
87
85
  // TODO: metrics should be incremental, there already is an RFC to report metrics
88
86
  const store = storage.getStore()
89
- const rootSpan = store && store.req && web.root(store.req)
87
+ const rootSpan = store?.req && web.root(store.req)
90
88
  if (!rootSpan) return
91
89
 
92
90
  if (metrics.duration) {
@@ -106,13 +104,13 @@ function reportMetrics (metrics) {
106
104
 
107
105
  function reportAttack (attackData) {
108
106
  const store = storage.getStore()
109
- const req = store && store.req
107
+ const req = store?.req
110
108
  const rootSpan = web.root(req)
111
109
  if (!rootSpan) return
112
110
 
113
111
  const currentTags = rootSpan.context()._tags
114
112
 
115
- const newTags = filterHeaders(req.headers, REQUEST_HEADERS_PASSLIST, 'http.request.headers.')
113
+ const newTags = filterHeaders(req.headers, REQUEST_HEADERS_MAP)
116
114
 
117
115
  newTags['appsec.event'] = 'true'
118
116
 
@@ -144,6 +142,23 @@ function reportAttack (attackData) {
144
142
  rootSpan.addTags(newTags)
145
143
  }
146
144
 
145
+ function reportSchemas (derivatives) {
146
+ if (!derivatives) return
147
+
148
+ const req = storage.getStore()?.req
149
+ const rootSpan = web.root(req)
150
+
151
+ if (!rootSpan) return
152
+
153
+ const tags = {}
154
+ for (const [address, value] of Object.entries(derivatives)) {
155
+ const gzippedValue = zlib.gzipSync(JSON.stringify(value))
156
+ tags[address] = gzippedValue.toString('base64')
157
+ }
158
+
159
+ rootSpan.addTags(tags)
160
+ }
161
+
147
162
  function finishRequest (req, res) {
148
163
  const rootSpan = web.root(req)
149
164
  if (!rootSpan) return
@@ -158,7 +173,7 @@ function finishRequest (req, res) {
158
173
 
159
174
  if (!rootSpan.context()._tags['appsec.event']) return
160
175
 
161
- const newTags = filterHeaders(res.getHeaders(), RESPONSE_HEADERS_PASSLIST, 'http.response.headers.')
176
+ const newTags = filterHeaders(res.getHeaders(), RESPONSE_HEADERS_MAP)
162
177
 
163
178
  if (req.route && typeof req.route.path === 'string') {
164
179
  newTags['http.endpoint'] = req.route.path
@@ -179,6 +194,8 @@ module.exports = {
179
194
  reportMetrics,
180
195
  reportAttack,
181
196
  reportWafUpdate: incrementWafUpdatesMetric,
197
+ reportSchemas,
182
198
  finishRequest,
183
- setRateLimit
199
+ setRateLimit,
200
+ mapHeaderAndTags
184
201
  }
@@ -1,5 +1,6 @@
1
1
  'use strict'
2
2
 
3
+ const fs = require('fs')
3
4
  const waf = require('./waf')
4
5
  const { ACKNOWLEDGED, ERROR } = require('./remote_config/apply_states')
5
6
  const blocking = require('./blocking')
@@ -13,13 +14,15 @@ let appliedExclusions = new Map()
13
14
  let appliedCustomRules = new Map()
14
15
  let appliedActions = new Map()
15
16
 
16
- function applyRules (rules, config) {
17
- defaultRules = rules
17
+ function loadRules (config) {
18
+ defaultRules = config.rules
19
+ ? JSON.parse(fs.readFileSync(config.rules))
20
+ : require('./recommended.json')
18
21
 
19
- waf.init(rules, config)
22
+ waf.init(defaultRules, config)
20
23
 
21
- if (rules.actions) {
22
- blocking.updateBlockingConfiguration(rules.actions.find(action => action.id === 'block'))
24
+ if (defaultRules.actions) {
25
+ blocking.updateBlockingConfiguration(defaultRules.actions.find(action => action.id === 'block'))
23
26
  }
24
27
  }
25
28
 
@@ -66,9 +69,9 @@ function updateWafFromRC ({ toUnapply, toApply, toModify }) {
66
69
  item.apply_error = 'Multiple ruleset received in ASM_DD'
67
70
  } else {
68
71
  if (file && file.rules && file.rules.length) {
69
- const { version, metadata, rules } = file
72
+ const { version, metadata, rules, processors, scanners } = file
70
73
 
71
- newRuleset = { version, metadata, rules }
74
+ newRuleset = { version, metadata, rules, processors, scanners }
72
75
  newRulesetId = id
73
76
  }
74
77
 
@@ -252,7 +255,7 @@ function clearAllRules () {
252
255
  }
253
256
 
254
257
  module.exports = {
255
- applyRules,
258
+ loadRules,
256
259
  updateWafFromRC,
257
260
  clearAllRules
258
261
  }
@@ -9,7 +9,7 @@ const { setUserTags } = require('./set_user')
9
9
  const log = require('../../log')
10
10
 
11
11
  function isUserBlocked (user) {
12
- const actions = waf.run({ [USER_ID]: user.id })
12
+ const actions = waf.run({ persistent: { [USER_ID]: user.id } })
13
13
 
14
14
  if (!actions) return false
15
15
 
@@ -10,37 +10,50 @@ const preventDuplicateAddresses = new Set([
10
10
  ])
11
11
 
12
12
  class WAFContextWrapper {
13
- constructor (ddwafContext, requiredAddresses, wafTimeout, wafVersion, rulesVersion) {
13
+ constructor (ddwafContext, wafTimeout, wafVersion, rulesVersion) {
14
14
  this.ddwafContext = ddwafContext
15
- this.requiredAddresses = requiredAddresses
16
15
  this.wafTimeout = wafTimeout
17
16
  this.wafVersion = wafVersion
18
17
  this.rulesVersion = rulesVersion
19
18
  this.addressesToSkip = new Set()
20
19
  }
21
20
 
22
- run (params) {
21
+ run ({ persistent, ephemeral }) {
22
+ const payload = {}
23
+ let payloadHasData = false
23
24
  const inputs = {}
24
- let someInputAdded = false
25
25
  const newAddressesToSkip = new Set(this.addressesToSkip)
26
26
 
27
- // TODO: possible optimizaion: only send params that haven't already been sent with same value to this wafContext
28
- for (const key of Object.keys(params)) {
29
- if (this.requiredAddresses.has(key) && !this.addressesToSkip.has(key)) {
30
- inputs[key] = params[key]
31
- if (preventDuplicateAddresses.has(key)) {
32
- newAddressesToSkip.add(key)
27
+ if (persistent && typeof persistent === 'object') {
28
+ // TODO: possible optimization: only send params that haven't already been sent with same value to this wafContext
29
+ for (const key of Object.keys(persistent)) {
30
+ // TODO: requiredAddresses is no longer used due to processor addresses are not included in the list. Check on
31
+ // future versions when the actual addresses are included in the 'loaded' section inside diagnostics.
32
+ if (!this.addressesToSkip.has(key)) {
33
+ inputs[key] = persistent[key]
34
+ if (preventDuplicateAddresses.has(key)) {
35
+ newAddressesToSkip.add(key)
36
+ }
33
37
  }
34
- someInputAdded = true
35
38
  }
36
39
  }
37
40
 
38
- if (!someInputAdded) return
41
+ if (Object.keys(inputs).length) {
42
+ payload['persistent'] = inputs
43
+ payloadHasData = true
44
+ }
45
+
46
+ if (ephemeral && Object.keys(ephemeral).length) {
47
+ payload['ephemeral'] = ephemeral
48
+ payloadHasData = true
49
+ }
50
+
51
+ if (!payloadHasData) return
39
52
 
40
53
  try {
41
54
  const start = process.hrtime.bigint()
42
55
 
43
- const result = this.ddwafContext.run(inputs, this.wafTimeout)
56
+ const result = this.ddwafContext.run(payload, this.wafTimeout)
44
57
 
45
58
  const end = process.hrtime.bigint()
46
59
 
@@ -63,6 +76,8 @@ class WAFContextWrapper {
63
76
  Reporter.reportAttack(JSON.stringify(result.events))
64
77
  }
65
78
 
79
+ Reporter.reportSchemas(result.derivatives)
80
+
66
81
  return result.actions
67
82
  } catch (err) {
68
83
  log.error('Error while running the AppSec WAF')
@@ -37,7 +37,6 @@ class WAFManager {
37
37
  if (!wafContext) {
38
38
  wafContext = new WAFContextWrapper(
39
39
  this.ddwaf.createContext(),
40
- this.ddwaf.requiredAddresses,
41
40
  this.wafTimeout,
42
41
  this.ddwafVersion,
43
42
  this.rulesVersion
@@ -1,10 +1,11 @@
1
1
  const request = require('../../exporters/common/request')
2
2
  const id = require('../../id')
3
- const log = require('../../log')
4
3
 
5
- function getItrConfiguration ({
4
+ function getKnownTests ({
6
5
  url,
7
6
  isEvpProxy,
7
+ evpProxyPrefix,
8
+ isGzipCompatible,
8
9
  env,
9
10
  service,
10
11
  repositoryUrl,
@@ -14,35 +15,39 @@ function getItrConfiguration ({
14
15
  osArchitecture,
15
16
  runtimeName,
16
17
  runtimeVersion,
17
- branch,
18
18
  custom
19
19
  }, done) {
20
20
  const options = {
21
- path: '/api/v2/libraries/tests/services/setting',
21
+ path: '/api/v2/ci/libraries/tests',
22
22
  method: 'POST',
23
23
  headers: {
24
24
  'Content-Type': 'application/json'
25
25
  },
26
+ timeout: 20000,
26
27
  url
27
28
  }
28
29
 
30
+ if (isGzipCompatible) {
31
+ options.headers['accept-encoding'] = 'gzip'
32
+ }
33
+
29
34
  if (isEvpProxy) {
30
- options.path = '/evp_proxy/v2/api/v2/libraries/tests/services/setting'
35
+ options.path = `${evpProxyPrefix}/api/v2/ci/libraries/tests`
31
36
  options.headers['X-Datadog-EVP-Subdomain'] = 'api'
32
37
  } else {
33
38
  const apiKey = process.env.DATADOG_API_KEY || process.env.DD_API_KEY
34
39
  if (!apiKey) {
35
- return done(new Error('Request to settings endpoint was not done because Datadog API key is not defined.'))
40
+ return done(new Error('Known tests were not fetched because Datadog API key is not defined.'))
36
41
  }
42
+
37
43
  options.headers['dd-api-key'] = apiKey
38
44
  }
39
45
 
40
46
  const data = JSON.stringify({
41
47
  data: {
42
48
  id: id().toString(10),
43
- type: 'ci_app_test_service_libraries_settings',
49
+ type: 'ci_app_libraries_tests_request',
44
50
  attributes: {
45
- test_level: 'suite',
46
51
  configurations: {
47
52
  'os.platform': osPlatform,
48
53
  'os.version': osVersion,
@@ -54,8 +59,7 @@ function getItrConfiguration ({
54
59
  service,
55
60
  env,
56
61
  repository_url: repositoryUrl,
57
- sha,
58
- branch
62
+ sha
59
63
  }
60
64
  }
61
65
  })
@@ -65,17 +69,8 @@ function getItrConfiguration ({
65
69
  done(err)
66
70
  } else {
67
71
  try {
68
- const {
69
- data: {
70
- attributes: {
71
- code_coverage: isCodeCoverageEnabled,
72
- tests_skipping: isSuitesSkippingEnabled
73
- }
74
- }
75
- } = JSON.parse(res)
76
- const config = { isCodeCoverageEnabled, isSuitesSkippingEnabled }
77
- log.debug(() => `Received settings: ${config}`)
78
- done(null, config)
72
+ const { data: { attributes: { tests: knownTests } } } = JSON.parse(res)
73
+ done(null, knownTests)
79
74
  } catch (err) {
80
75
  done(err)
81
76
  }
@@ -83,4 +78,4 @@ function getItrConfiguration ({
83
78
  })
84
79
  }
85
80
 
86
- module.exports = { getItrConfiguration }
81
+ module.exports = { getKnownTests }
@@ -5,10 +5,23 @@ const AgentlessWriter = require('../agentless/writer')
5
5
  const CoverageWriter = require('../agentless/coverage-writer')
6
6
  const CiVisibilityExporter = require('../ci-visibility-exporter')
7
7
 
8
- const AGENT_EVP_PROXY_PATH = '/evp_proxy/v2'
8
+ const AGENT_EVP_PROXY_PATH_PREFIX = '/evp_proxy/v'
9
+ const AGENT_EVP_PROXY_PATH_REGEX = /\/evp_proxy\/v(\d+)\/?/
9
10
 
10
- function getIsEvpCompatible (err, agentInfo) {
11
- return !err && agentInfo.endpoints.some(url => url.includes(AGENT_EVP_PROXY_PATH))
11
+ function getLatestEvpProxyVersion (err, agentInfo) {
12
+ if (err) {
13
+ return 0
14
+ }
15
+ return agentInfo.endpoints.reduce((acc, endpoint) => {
16
+ if (endpoint.includes(AGENT_EVP_PROXY_PATH_PREFIX)) {
17
+ const version = Number(endpoint.replace(AGENT_EVP_PROXY_PATH_REGEX, '$1'))
18
+ if (isNaN(version)) {
19
+ return acc
20
+ }
21
+ return version > acc ? version : acc
22
+ }
23
+ return acc
24
+ }, 0)
12
25
  }
13
26
 
14
27
  class AgentProxyCiVisibilityExporter extends CiVisibilityExporter {
@@ -25,17 +38,22 @@ class AgentProxyCiVisibilityExporter extends CiVisibilityExporter {
25
38
 
26
39
  this.getAgentInfo((err, agentInfo) => {
27
40
  this._isInitialized = true
28
- const isEvpCompatible = getIsEvpCompatible(err, agentInfo)
41
+ const latestEvpProxyVersion = getLatestEvpProxyVersion(err, agentInfo)
42
+ const isEvpCompatible = latestEvpProxyVersion >= 2
43
+ const isGzipCompatible = latestEvpProxyVersion >= 4
44
+
45
+ const evpProxyPrefix = `${AGENT_EVP_PROXY_PATH_PREFIX}${latestEvpProxyVersion}`
29
46
  if (isEvpCompatible) {
30
47
  this._isUsingEvpProxy = true
48
+ this.evpProxyPrefix = evpProxyPrefix
31
49
  this._writer = new AgentlessWriter({
32
50
  url: this._url,
33
51
  tags,
34
- evpProxyPrefix: AGENT_EVP_PROXY_PATH
52
+ evpProxyPrefix
35
53
  })
36
54
  this._coverageWriter = new CoverageWriter({
37
55
  url: this._url,
38
- evpProxyPrefix: AGENT_EVP_PROXY_PATH
56
+ evpProxyPrefix
39
57
  })
40
58
  } else {
41
59
  this._writer = new AgentWriter({
@@ -51,6 +69,7 @@ class AgentProxyCiVisibilityExporter extends CiVisibilityExporter {
51
69
  this._resolveCanUseCiVisProtocol(isEvpCompatible)
52
70
  this.exportUncodedTraces()
53
71
  this.exportUncodedCoverages()
72
+ this._isGzipCompatible = isGzipCompatible
54
73
  })
55
74
  }
56
75
 
@@ -5,6 +5,16 @@ const { safeJSONStringify } = require('../../../exporters/common/util')
5
5
 
6
6
  const { CoverageCIVisibilityEncoder } = require('../../../encode/coverage-ci-visibility')
7
7
  const BaseWriter = require('../../../exporters/common/writer')
8
+ const {
9
+ incrementCountMetric,
10
+ distributionMetric,
11
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS,
12
+ TELEMETRY_ENDPOINT_PAYLOAD_BYTES,
13
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_MS,
14
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_ERRORS,
15
+ TELEMETRY_ENDPOINT_PAYLOAD_DROPPED,
16
+ getErrorTypeFromStatusCode
17
+ } = require('../../../ci-visibility/telemetry')
8
18
 
9
19
  class Writer extends BaseWriter {
10
20
  constructor ({ url, evpProxyPrefix = '' }) {
@@ -34,8 +44,27 @@ class Writer extends BaseWriter {
34
44
 
35
45
  log.debug(() => `Request to the intake: ${safeJSONStringify(options)}`)
36
46
 
37
- request(form, options, (err, res) => {
47
+ const startRequestTime = Date.now()
48
+
49
+ incrementCountMetric(TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS, { endpoint: 'code_coverage' })
50
+ distributionMetric(TELEMETRY_ENDPOINT_PAYLOAD_BYTES, { endpoint: 'code_coverage' }, form.size())
51
+
52
+ request(form, options, (err, res, statusCode) => {
53
+ distributionMetric(
54
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_MS,
55
+ { endpoint: 'code_coverage' },
56
+ Date.now() - startRequestTime
57
+ )
38
58
  if (err) {
59
+ const errorType = getErrorTypeFromStatusCode(statusCode)
60
+ incrementCountMetric(
61
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_ERRORS,
62
+ { endpoint: 'code_coverage', errorType }
63
+ )
64
+ incrementCountMetric(
65
+ TELEMETRY_ENDPOINT_PAYLOAD_DROPPED,
66
+ { endpoint: 'code_coverage' }
67
+ )
39
68
  log.error(err)
40
69
  done()
41
70
  return
@@ -21,6 +21,8 @@ class AgentlessCiVisibilityExporter extends CiVisibilityExporter {
21
21
  this._coverageWriter = new CoverageWriter({ url: this._coverageUrl })
22
22
 
23
23
  this._apiUrl = url || new URL(`https://api.${site}`)
24
+ // Agentless is always gzip compatible
25
+ this._isGzipCompatible = true
24
26
  }
25
27
 
26
28
  setUrl (url, coverageUrl = url, apiUrl = url) {