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
@@ -13,9 +13,26 @@ const {
13
13
  addIntelligentTestRunnerSpanTags,
14
14
  TEST_SOURCE_START,
15
15
  TEST_ITR_UNSKIPPABLE,
16
- TEST_ITR_FORCED_RUN
16
+ TEST_ITR_FORCED_RUN,
17
+ TEST_CODE_OWNERS,
18
+ ITR_CORRELATION_ID,
19
+ TEST_SOURCE_FILE,
20
+ removeEfdStringFromTestName,
21
+ TEST_IS_NEW,
22
+ TEST_EARLY_FLAKE_IS_RETRY,
23
+ TEST_EARLY_FLAKE_IS_ENABLED
17
24
  } = require('../../dd-trace/src/plugins/util/test')
18
25
  const { COMPONENT } = require('../../dd-trace/src/constants')
26
+ const {
27
+ TELEMETRY_EVENT_CREATED,
28
+ TELEMETRY_EVENT_FINISHED,
29
+ TELEMETRY_CODE_COVERAGE_STARTED,
30
+ TELEMETRY_CODE_COVERAGE_FINISHED,
31
+ TELEMETRY_ITR_FORCED_TO_RUN,
32
+ TELEMETRY_CODE_COVERAGE_EMPTY,
33
+ TELEMETRY_ITR_UNSKIPPABLE,
34
+ TELEMETRY_CODE_COVERAGE_NUM_FILES
35
+ } = require('../../dd-trace/src/ci-visibility/telemetry')
19
36
 
20
37
  class MochaPlugin extends CiPlugin {
21
38
  static get id () {
@@ -26,15 +43,19 @@ class MochaPlugin extends CiPlugin {
26
43
  super(...args)
27
44
 
28
45
  this._testSuites = new Map()
29
- this._testNameToParams = {}
46
+ this._testTitleToParams = {}
30
47
  this.sourceRoot = process.cwd()
31
48
 
32
49
  this.addSub('ci:mocha:test-suite:code-coverage', ({ coverageFiles, suiteFile }) => {
33
- if (!this.itrConfig || !this.itrConfig.isCodeCoverageEnabled) {
50
+ if (!this.libraryConfig?.isCodeCoverageEnabled) {
34
51
  return
35
52
  }
36
53
  const testSuiteSpan = this._testSuites.get(suiteFile)
37
54
 
55
+ if (!coverageFiles.length) {
56
+ this.telemetry.count(TELEMETRY_CODE_COVERAGE_EMPTY)
57
+ }
58
+
38
59
  const relativeCoverageFiles = [...coverageFiles, suiteFile]
39
60
  .map(filename => getTestSuitePath(filename, this.sourceRoot))
40
61
 
@@ -47,9 +68,16 @@ class MochaPlugin extends CiPlugin {
47
68
  }
48
69
 
49
70
  this.tracer._exporter.exportCoverage(formattedCoverage)
71
+ this.telemetry.ciVisEvent(TELEMETRY_CODE_COVERAGE_FINISHED, 'suite', { library: 'istanbul' })
72
+ this.telemetry.distribution(TELEMETRY_CODE_COVERAGE_NUM_FILES, {}, relativeCoverageFiles.length)
50
73
  })
51
74
 
52
- this.addSub('ci:mocha:test-suite:start', ({ testSuite, isUnskippable, isForcedToRun }) => {
75
+ this.addSub('ci:mocha:test-suite:start', ({
76
+ testSuite,
77
+ isUnskippable,
78
+ isForcedToRun,
79
+ itrCorrelationId
80
+ }) => {
53
81
  const store = storage.getStore()
54
82
  const testSuiteMetadata = getTestSuiteCommonTags(
55
83
  this.command,
@@ -59,9 +87,11 @@ class MochaPlugin extends CiPlugin {
59
87
  )
60
88
  if (isUnskippable) {
61
89
  testSuiteMetadata[TEST_ITR_UNSKIPPABLE] = 'true'
90
+ this.telemetry.count(TELEMETRY_ITR_UNSKIPPABLE, { testLevel: 'suite' })
62
91
  }
63
92
  if (isForcedToRun) {
64
93
  testSuiteMetadata[TEST_ITR_FORCED_RUN] = 'true'
94
+ this.telemetry.count(TELEMETRY_ITR_FORCED_TO_RUN, { testLevel: 'suite' })
65
95
  }
66
96
 
67
97
  const testSuiteSpan = this.tracer.startSpan('mocha.test_suite', {
@@ -72,6 +102,13 @@ class MochaPlugin extends CiPlugin {
72
102
  ...testSuiteMetadata
73
103
  }
74
104
  })
105
+ this.telemetry.ciVisEvent(TELEMETRY_EVENT_CREATED, 'suite')
106
+ if (this.libraryConfig?.isCodeCoverageEnabled) {
107
+ this.telemetry.ciVisEvent(TELEMETRY_CODE_COVERAGE_STARTED, 'suite', { library: 'istanbul' })
108
+ }
109
+ if (itrCorrelationId) {
110
+ testSuiteSpan.setTag(ITR_CORRELATION_ID, itrCorrelationId)
111
+ }
75
112
  this.enter(testSuiteSpan, store)
76
113
  this._testSuites.set(testSuite, testSuiteSpan)
77
114
  })
@@ -85,6 +122,7 @@ class MochaPlugin extends CiPlugin {
85
122
  span.setTag(TEST_STATUS, status)
86
123
  }
87
124
  span.finish()
125
+ this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
88
126
  }
89
127
  })
90
128
 
@@ -97,9 +135,9 @@ class MochaPlugin extends CiPlugin {
97
135
  }
98
136
  })
99
137
 
100
- this.addSub('ci:mocha:test:start', ({ test, testStartLine }) => {
138
+ this.addSub('ci:mocha:test:start', (testInfo) => {
101
139
  const store = storage.getStore()
102
- const span = this.startTestSpan(test, testStartLine)
140
+ const span = this.startTestSpan(testInfo)
103
141
 
104
142
  this.enter(span, store)
105
143
  })
@@ -113,16 +151,21 @@ class MochaPlugin extends CiPlugin {
113
151
  span.setTag(TEST_STATUS, status)
114
152
 
115
153
  span.finish()
154
+ this.telemetry.ciVisEvent(
155
+ TELEMETRY_EVENT_FINISHED,
156
+ 'test',
157
+ { hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS] }
158
+ )
116
159
  finishAllTraceSpans(span)
117
160
  }
118
161
  })
119
162
 
120
- this.addSub('ci:mocha:test:skip', (test) => {
163
+ this.addSub('ci:mocha:test:skip', (testInfo) => {
121
164
  const store = storage.getStore()
122
165
  // skipped through it.skip, so the span is not created yet
123
166
  // for this test
124
167
  if (!store) {
125
- const testSpan = this.startTestSpan(test)
168
+ const testSpan = this.startTestSpan(testInfo)
126
169
  this.enter(testSpan, store)
127
170
  }
128
171
  })
@@ -140,8 +183,8 @@ class MochaPlugin extends CiPlugin {
140
183
  }
141
184
  })
142
185
 
143
- this.addSub('ci:mocha:test:parameterize', ({ name, params }) => {
144
- this._testNameToParams[name] = params
186
+ this.addSub('ci:mocha:test:parameterize', ({ title, params }) => {
187
+ this._testTitleToParams[title] = params
145
188
  })
146
189
 
147
190
  this.addSub('ci:mocha:session:finish', ({
@@ -151,10 +194,11 @@ class MochaPlugin extends CiPlugin {
151
194
  numSkippedSuites,
152
195
  hasForcedToRunSuites,
153
196
  hasUnskippableSuites,
154
- error
197
+ error,
198
+ isEarlyFlakeDetectionEnabled
155
199
  }) => {
156
200
  if (this.testSessionSpan) {
157
- const { isSuitesSkippingEnabled, isCodeCoverageEnabled } = this.itrConfig || {}
201
+ const { isSuitesSkippingEnabled, isCodeCoverageEnabled } = this.libraryConfig || {}
158
202
  this.testSessionSpan.setTag(TEST_STATUS, status)
159
203
  this.testModuleSpan.setTag(TEST_STATUS, status)
160
204
 
@@ -178,21 +222,34 @@ class MochaPlugin extends CiPlugin {
178
222
  }
179
223
  )
180
224
 
225
+ if (isEarlyFlakeDetectionEnabled) {
226
+ this.testSessionSpan.setTag(TEST_EARLY_FLAKE_IS_ENABLED, 'true')
227
+ }
228
+
181
229
  this.testModuleSpan.finish()
230
+ this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
182
231
  this.testSessionSpan.finish()
232
+ this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session')
183
233
  finishAllTraceSpans(this.testSessionSpan)
184
234
  }
185
- this.itrConfig = null
235
+ this.libraryConfig = null
186
236
  this.tracer._exporter.flush()
187
237
  })
188
238
  }
189
239
 
190
- startTestSpan (test, testStartLine) {
191
- const testName = test.fullTitle()
192
- const { file: testSuiteAbsolutePath, title } = test
240
+ startTestSpan (testInfo) {
241
+ const {
242
+ testSuiteAbsolutePath,
243
+ title,
244
+ isNew,
245
+ isEfdRetry,
246
+ testStartLine
247
+ } = testInfo
248
+
249
+ const testName = removeEfdStringFromTestName(testInfo.testName)
193
250
 
194
251
  const extraTags = {}
195
- const testParametersString = getTestParametersString(this._testNameToParams, title)
252
+ const testParametersString = getTestParametersString(this._testTitleToParams, title)
196
253
  if (testParametersString) {
197
254
  extraTags[TEST_PARAMETERS] = testParametersString
198
255
  }
@@ -204,6 +261,19 @@ class MochaPlugin extends CiPlugin {
204
261
  const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.sourceRoot)
205
262
  const testSuiteSpan = this._testSuites.get(testSuiteAbsolutePath)
206
263
 
264
+ if (this.repositoryRoot !== this.sourceRoot && !!this.repositoryRoot) {
265
+ extraTags[TEST_SOURCE_FILE] = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
266
+ } else {
267
+ extraTags[TEST_SOURCE_FILE] = testSuite
268
+ }
269
+
270
+ if (isNew) {
271
+ extraTags[TEST_IS_NEW] = 'true'
272
+ if (isEfdRetry) {
273
+ extraTags[TEST_EARLY_FLAKE_IS_RETRY] = 'true'
274
+ }
275
+ }
276
+
207
277
  return super.startTestSpan(testName, testSuite, testSuiteSpan, extraTags)
208
278
  }
209
279
  }
@@ -6,6 +6,8 @@ const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
6
6
  const { COMPONENT } = require('../../dd-trace/src/constants')
7
7
  const web = require('../../dd-trace/src/plugins/util/web')
8
8
 
9
+ const errorPages = ['/404', '/500', '/_error', '/_not-found']
10
+
9
11
  class NextPlugin extends ServerPlugin {
10
12
  static get id () {
11
13
  return 'next'
@@ -40,6 +42,13 @@ class NextPlugin extends ServerPlugin {
40
42
  }
41
43
 
42
44
  error ({ span, error }) {
45
+ if (!span) {
46
+ const store = storage.getStore()
47
+ if (!store) return
48
+
49
+ span = store.span
50
+ }
51
+
43
52
  this.addError(error, span)
44
53
  }
45
54
 
@@ -50,10 +59,20 @@ class NextPlugin extends ServerPlugin {
50
59
 
51
60
  const span = store.span
52
61
  const error = span.context()._tags['error']
53
-
54
- if (!this.config.validateStatus(res.statusCode) && !error) {
55
- span.setTag('error', req.error || nextRequest.error || true)
56
- web.addError(req, req.error || nextRequest.error || true)
62
+ const requestError = req.error || nextRequest.error
63
+
64
+ if (requestError) {
65
+ // prioritize user-set errors from API routes
66
+ span.setTag('error', requestError)
67
+ web.addError(req, requestError)
68
+ } else if (error) {
69
+ // general error handling
70
+ span.setTag('error', error)
71
+ web.addError(req, requestError || error)
72
+ } else if (!this.config.validateStatus(res.statusCode)) {
73
+ // where there's no error, we still need to validate status
74
+ span.setTag('error', true)
75
+ web.addError(req, true)
57
76
  }
58
77
 
59
78
  span.addTags({
@@ -65,7 +84,7 @@ class NextPlugin extends ServerPlugin {
65
84
  span.finish()
66
85
  }
67
86
 
68
- pageLoad ({ page, isAppPath = false }) {
87
+ pageLoad ({ page, isAppPath = false, isStatic = false }) {
69
88
  const store = storage.getStore()
70
89
 
71
90
  if (!store) return
@@ -73,21 +92,28 @@ class NextPlugin extends ServerPlugin {
73
92
  const span = store.span
74
93
  const req = this._requests.get(span)
75
94
 
95
+ // safeguard against missing req in complicated timeout scenarios
96
+ if (!req) return
97
+
76
98
  // Only use error page names if there's not already a name
77
99
  const current = span.context()._tags['next.page']
78
- if (current && ['/404', '/500', '/_error', '/_not-found'].includes(page)) {
100
+ const isErrorPage = errorPages.includes(page)
101
+
102
+ if (current && isErrorPage) {
79
103
  return
80
104
  }
81
105
 
82
106
  // remove ending /route or /page for appDir projects
83
- if (isAppPath) page = page.substring(0, page.lastIndexOf('/'))
84
-
85
- // This is for static files whose 'page' includes the whole file path
86
- // For normal page matches, like /api/hello/[name] and a req.url like /api/hello/world,
87
- // nothing should happen
88
- // For page matches like /User/something/public/text.txt and req.url like /text.txt,
89
- // it should disregard the extra absolute path Next.js sometimes sets
90
- if (page.includes(req.url)) page = req.url
107
+ // need to check if not an error page too, as those are marked as app directory
108
+ // in newer versions
109
+ if (isAppPath && !isErrorPage) page = page.substring(0, page.lastIndexOf('/'))
110
+
111
+ // handle static resource
112
+ if (isStatic) {
113
+ page = req.url.includes('_next/static')
114
+ ? '/_next/static/*'
115
+ : '/public/*'
116
+ }
91
117
 
92
118
  span.addTags({
93
119
  [COMPONENT]: this.constructor.id,
@@ -8,10 +8,18 @@ const {
8
8
  finishAllTraceSpans,
9
9
  getTestSuitePath,
10
10
  getTestSuiteCommonTags,
11
- TEST_SOURCE_START
11
+ TEST_SOURCE_START,
12
+ TEST_CODE_OWNERS,
13
+ TEST_SOURCE_FILE,
14
+ TEST_CONFIGURATION_BROWSER_NAME
12
15
  } = require('../../dd-trace/src/plugins/util/test')
13
16
  const { RESOURCE_NAME } = require('../../../ext/tags')
14
17
  const { COMPONENT } = require('../../dd-trace/src/constants')
18
+ const {
19
+ TELEMETRY_EVENT_CREATED,
20
+ TELEMETRY_EVENT_FINISHED
21
+ } = require('../../dd-trace/src/ci-visibility/telemetry')
22
+ const { appClosing: appClosingTelemetry } = require('../../dd-trace/src/telemetry')
15
23
 
16
24
  class PlaywrightPlugin extends CiPlugin {
17
25
  static get id () {
@@ -22,15 +30,31 @@ class PlaywrightPlugin extends CiPlugin {
22
30
  super(...args)
23
31
 
24
32
  this._testSuites = new Map()
33
+ this.numFailedTests = 0
34
+ this.numFailedSuites = 0
25
35
 
26
36
  this.addSub('ci:playwright:session:finish', ({ status, onDone }) => {
27
37
  this.testModuleSpan.setTag(TEST_STATUS, status)
28
38
  this.testSessionSpan.setTag(TEST_STATUS, status)
29
39
 
40
+ if (this.numFailedSuites > 0) {
41
+ let errorMessage = `Test suites failed: ${this.numFailedSuites}.`
42
+ if (this.numFailedTests > 0) {
43
+ errorMessage += ` Tests failed: ${this.numFailedTests}`
44
+ }
45
+ const error = new Error(errorMessage)
46
+ this.testModuleSpan.setTag('error', error)
47
+ this.testSessionSpan.setTag('error', error)
48
+ }
49
+
30
50
  this.testModuleSpan.finish()
51
+ this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
31
52
  this.testSessionSpan.finish()
53
+ this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session')
32
54
  finishAllTraceSpans(this.testSessionSpan)
55
+ appClosingTelemetry()
33
56
  this.tracer._exporter.flush(onDone)
57
+ this.numFailedTests = 0
34
58
  })
35
59
 
36
60
  this.addSub('ci:playwright:test-suite:start', (testSuiteAbsolutePath) => {
@@ -52,23 +76,36 @@ class PlaywrightPlugin extends CiPlugin {
52
76
  ...testSuiteMetadata
53
77
  }
54
78
  })
79
+ this.telemetry.ciVisEvent(TELEMETRY_EVENT_CREATED, 'suite')
55
80
  this.enter(testSuiteSpan, store)
56
81
 
57
82
  this._testSuites.set(testSuite, testSuiteSpan)
58
83
  })
59
84
 
60
- this.addSub('ci:playwright:test-suite:finish', (status) => {
85
+ this.addSub('ci:playwright:test-suite:finish', ({ status, error }) => {
61
86
  const store = storage.getStore()
62
87
  const span = store && store.span
63
88
  if (!span) return
64
- span.setTag(TEST_STATUS, status)
89
+ if (error) {
90
+ span.setTag('error', error)
91
+ span.setTag(TEST_STATUS, 'fail')
92
+ } else {
93
+ span.setTag(TEST_STATUS, status)
94
+ }
95
+
96
+ if (status === 'fail' || error) {
97
+ this.numFailedSuites++
98
+ }
99
+
65
100
  span.finish()
101
+ this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
66
102
  })
67
103
 
68
- this.addSub('ci:playwright:test:start', ({ testName, testSuiteAbsolutePath, testSourceLine }) => {
104
+ this.addSub('ci:playwright:test:start', ({ testName, testSuiteAbsolutePath, testSourceLine, browserName }) => {
69
105
  const store = storage.getStore()
70
106
  const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.rootDir)
71
- const span = this.startTestSpan(testName, testSuite, testSourceLine)
107
+ const testSourceFile = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
108
+ const span = this.startTestSpan(testName, testSuite, testSourceFile, testSourceLine, browserName)
72
109
 
73
110
  this.enter(span, store)
74
111
  })
@@ -100,17 +137,43 @@ class PlaywrightPlugin extends CiPlugin {
100
137
  if (step.error) {
101
138
  stepSpan.setTag('error', step.error)
102
139
  }
103
- stepSpan.finish(stepStartTime + step.duration)
140
+ let stepDuration = step.duration
141
+ if (stepDuration <= 0 || isNaN(stepDuration)) {
142
+ stepDuration = 0
143
+ }
144
+ stepSpan.finish(stepStartTime + stepDuration)
104
145
  })
105
146
 
106
147
  span.finish()
148
+
149
+ if (testStatus === 'fail') {
150
+ this.numFailedTests++
151
+ }
152
+
153
+ this.telemetry.ciVisEvent(
154
+ TELEMETRY_EVENT_FINISHED,
155
+ 'test',
156
+ { hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS] }
157
+ )
158
+
107
159
  finishAllTraceSpans(span)
108
160
  })
109
161
  }
110
162
 
111
- startTestSpan (testName, testSuite, testSourceLine) {
163
+ startTestSpan (testName, testSuite, testSourceFile, testSourceLine, browserName) {
112
164
  const testSuiteSpan = this._testSuites.get(testSuite)
113
- return super.startTestSpan(testName, testSuite, testSuiteSpan, { [TEST_SOURCE_START]: testSourceLine })
165
+
166
+ const extraTags = {
167
+ [TEST_SOURCE_START]: testSourceLine
168
+ }
169
+ if (testSourceFile) {
170
+ extraTags[TEST_SOURCE_FILE] = testSourceFile || testSuite
171
+ }
172
+ if (browserName) {
173
+ extraTags[TEST_CONFIGURATION_BROWSER_NAME] = browserName
174
+ }
175
+
176
+ return super.startTestSpan(testName, testSuite, testSuiteSpan, extraTags)
114
177
  }
115
178
  }
116
179
 
@@ -2,6 +2,8 @@
2
2
 
3
3
  const ConsumerPlugin = require('../../dd-trace/src/plugins/consumer')
4
4
  const { storage } = require('../../datadog-core')
5
+ const { getAmqpMessageSize } = require('../../dd-trace/src/datastreams/processor')
6
+ const { DsmPathwayCodec } = require('../../dd-trace/src/datastreams/pathway')
5
7
 
6
8
  class RheaConsumerPlugin extends ConsumerPlugin {
7
9
  static get id () { return 'rhea' }
@@ -19,7 +21,7 @@ class RheaConsumerPlugin extends ConsumerPlugin {
19
21
  const name = getResourceNameFromMessage(msgObj)
20
22
  const childOf = extractTextMap(msgObj, this.tracer)
21
23
 
22
- this.startSpan({
24
+ const span = this.startSpan({
23
25
  childOf,
24
26
  resource: name,
25
27
  type: 'worker',
@@ -29,6 +31,19 @@ class RheaConsumerPlugin extends ConsumerPlugin {
29
31
  'amqp.link.role': 'receiver'
30
32
  }
31
33
  })
34
+
35
+ if (
36
+ this.config.dsmEnabled &&
37
+ msgObj?.message?.delivery_annotations &&
38
+ DsmPathwayCodec.contextExists(msgObj.message.delivery_annotations)
39
+ ) {
40
+ const payloadSize = getAmqpMessageSize(
41
+ { headers: msgObj.message.delivery_annotations, content: msgObj.message.body }
42
+ )
43
+ this.tracer.decodeDataStreamsContext(msgObj.message.delivery_annotations)
44
+ this.tracer
45
+ .setCheckpoint(['direction:in', `topic:${name}`, 'type:rabbitmq'], span, payloadSize)
46
+ }
32
47
  }
33
48
  }
34
49
 
@@ -2,6 +2,8 @@
2
2
 
3
3
  const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
4
4
  const ProducerPlugin = require('../../dd-trace/src/plugins/producer')
5
+ const { DsmPathwayCodec } = require('../../dd-trace/src/datastreams/pathway')
6
+ const { getAmqpMessageSize } = require('../../dd-trace/src/datastreams/processor')
5
7
 
6
8
  class RheaProducerPlugin extends ProducerPlugin {
7
9
  static get id () { return 'rhea' }
@@ -36,6 +38,14 @@ function addDeliveryAnnotations (msg, tracer, span) {
36
38
  msg.delivery_annotations = msg.delivery_annotations || {}
37
39
 
38
40
  tracer.inject(span, 'text_map', msg.delivery_annotations)
41
+
42
+ if (tracer._config.dsmEnabled) {
43
+ const targetName = span.context()._tags['amqp.link.target.address']
44
+ const payloadSize = getAmqpMessageSize({ content: msg.body, headers: msg.delivery_annotations })
45
+ const dataStreamsContext = tracer
46
+ .setCheckpoint(['direction:out', `exchange:${targetName}`, 'type:rabbitmq'], span, payloadSize)
47
+ DsmPathwayCodec.encode(dataStreamsContext, msg.delivery_annotations)
48
+ }
39
49
  }
40
50
  }
41
51
 
@@ -0,0 +1,29 @@
1
+ 'use strict'
2
+
3
+ const Activation = {
4
+ ONECLICK: 'OneClick',
5
+ ENABLED: 'Enabled',
6
+ DISABLED: 'Disabled',
7
+
8
+ fromConfig (config) {
9
+ switch (config.appsec.enabled) {
10
+ // ASM is activated by an env var DD_APPSEC_ENABLED=true
11
+ case true:
12
+ return Activation.ENABLED
13
+
14
+ // ASM is disabled by an env var DD_APPSEC_ENABLED=false
15
+ case false:
16
+ return Activation.DISABLED
17
+
18
+ // ASM is activated by one click remote config
19
+ case undefined:
20
+ return Activation.ONECLICK
21
+
22
+ // Any other value should never occur
23
+ default:
24
+ return Activation.DISABLED
25
+ }
26
+ }
27
+ }
28
+
29
+ module.exports = Activation
@@ -13,8 +13,12 @@ module.exports = {
13
13
  HTTP_INCOMING_RESPONSE_HEADERS: 'server.response.headers.no_cookies',
14
14
  // TODO: 'server.response.trailers',
15
15
  HTTP_INCOMING_GRAPHQL_RESOLVERS: 'graphql.server.all_resolvers',
16
+ HTTP_INCOMING_GRAPHQL_RESOLVER: 'graphql.server.resolver',
17
+
18
+ HTTP_OUTGOING_BODY: 'server.response.body',
16
19
 
17
20
  HTTP_CLIENT_IP: 'http.client_ip',
18
21
 
19
- USER_ID: 'usr.id'
22
+ USER_ID: 'usr.id',
23
+ WAF_CONTEXT_PROCESSOR: 'waf.context.processor'
20
24
  }
@@ -0,0 +1,61 @@
1
+ 'use strict'
2
+
3
+ const log = require('../log')
4
+
5
+ let enabled
6
+ let requestSampling
7
+
8
+ const sampledRequests = new WeakSet()
9
+
10
+ function configure ({ apiSecurity }) {
11
+ enabled = apiSecurity.enabled
12
+ setRequestSampling(apiSecurity.requestSampling)
13
+ }
14
+
15
+ function disable () {
16
+ enabled = false
17
+ }
18
+
19
+ function setRequestSampling (sampling) {
20
+ requestSampling = parseRequestSampling(sampling)
21
+ }
22
+
23
+ function parseRequestSampling (requestSampling) {
24
+ let parsed = parseFloat(requestSampling)
25
+
26
+ if (isNaN(parsed)) {
27
+ log.warn(`Incorrect API Security request sampling value: ${requestSampling}`)
28
+
29
+ parsed = 0
30
+ } else {
31
+ parsed = Math.min(1, Math.max(0, parsed))
32
+ }
33
+
34
+ return parsed
35
+ }
36
+
37
+ function sampleRequest (req) {
38
+ if (!enabled || !requestSampling) {
39
+ return false
40
+ }
41
+
42
+ const shouldSample = Math.random() <= requestSampling
43
+
44
+ if (shouldSample) {
45
+ sampledRequests.add(req)
46
+ }
47
+
48
+ return shouldSample
49
+ }
50
+
51
+ function isSampled (req) {
52
+ return sampledRequests.has(req)
53
+ }
54
+
55
+ module.exports = {
56
+ configure,
57
+ disable,
58
+ setRequestSampling,
59
+ sampleRequest,
60
+ isSampled
61
+ }
@@ -5,7 +5,10 @@ const html = `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta n
5
5
 
6
6
  const json = `{"errors":[{"title":"You've been blocked","detail":"Sorry, you cannot access this page. Please contact the customer service team. Security provided by Datadog."}]}`
7
7
 
8
+ const graphqlJson = `{"errors":[{"message":"You've been blocked","extensions":{"detail":"Sorry, you cannot perform this operation. Please contact the customer service team. Security provided by Datadog."}}]}`
9
+
8
10
  module.exports = {
9
11
  html,
10
- json
12
+ json,
13
+ graphqlJson
11
14
  }