dd-trace 3.7.1 → 3.9.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 (67) hide show
  1. package/LICENSE-3rdparty.csv +2 -0
  2. package/index.d.ts +11 -0
  3. package/package.json +4 -2
  4. package/packages/datadog-instrumentations/src/body-parser.js +26 -0
  5. package/packages/datadog-instrumentations/src/child-process.js +30 -0
  6. package/packages/datadog-instrumentations/src/cucumber.js +1 -1
  7. package/packages/datadog-instrumentations/src/helpers/hooks.js +5 -0
  8. package/packages/datadog-instrumentations/src/jest.js +110 -40
  9. package/packages/datadog-instrumentations/src/mocha.js +87 -6
  10. package/packages/datadog-instrumentations/src/pg.js +1 -2
  11. package/packages/datadog-instrumentations/src/qs.js +24 -0
  12. package/packages/datadog-plugin-cucumber/src/index.js +13 -32
  13. package/packages/datadog-plugin-cypress/src/plugin.js +2 -1
  14. package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +0 -1
  15. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +0 -1
  16. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +0 -1
  17. package/packages/datadog-plugin-http/src/client.js +14 -4
  18. package/packages/datadog-plugin-http/src/server.js +3 -0
  19. package/packages/datadog-plugin-http2/src/client.js +20 -1
  20. package/packages/datadog-plugin-http2/src/server.js +3 -0
  21. package/packages/datadog-plugin-jest/src/index.js +11 -129
  22. package/packages/datadog-plugin-mocha/src/index.js +33 -46
  23. package/packages/datadog-plugin-net/src/index.js +4 -0
  24. package/packages/datadog-plugin-next/src/index.js +3 -0
  25. package/packages/datadog-plugin-oracledb/src/index.js +0 -1
  26. package/packages/datadog-plugin-pg/src/index.js +5 -2
  27. package/packages/datadog-plugin-router/src/index.js +2 -0
  28. package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +3 -5
  29. package/packages/dd-trace/src/appsec/gateway/engine/index.js +1 -1
  30. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +3 -1
  31. package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +11 -0
  32. package/packages/dd-trace/src/appsec/iast/analyzers/injection-analyzer.js +19 -0
  33. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +13 -0
  34. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +1 -1
  35. package/packages/dd-trace/src/appsec/iast/index.js +6 -0
  36. package/packages/dd-trace/src/appsec/iast/path-line.js +8 -1
  37. package/packages/dd-trace/src/appsec/iast/taint-tracking/filter.js +16 -0
  38. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +18 -0
  39. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +125 -0
  40. package/packages/dd-trace/src/appsec/iast/taint-tracking/origin-types.js +4 -0
  41. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +38 -0
  42. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +66 -0
  43. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +52 -6
  44. package/packages/dd-trace/src/appsec/index.js +8 -0
  45. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +7 -0
  46. package/packages/dd-trace/src/appsec/remote_config/index.js +34 -0
  47. package/packages/dd-trace/src/appsec/remote_config/manager.js +264 -0
  48. package/packages/dd-trace/src/{exporters → appsec/remote_config}/scheduler.js +9 -9
  49. package/packages/dd-trace/src/appsec/rule_manager.js +3 -0
  50. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +5 -7
  51. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +3 -4
  52. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +3 -4
  53. package/packages/dd-trace/src/config.js +41 -20
  54. package/packages/dd-trace/src/constants.js +6 -1
  55. package/packages/dd-trace/src/exporters/common/request.js +7 -1
  56. package/packages/dd-trace/src/format.js +12 -10
  57. package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -5
  58. package/packages/dd-trace/src/opentracing/span_context.js +9 -0
  59. package/packages/dd-trace/src/plugin_manager.js +6 -1
  60. package/packages/dd-trace/src/plugins/ci_plugin.js +132 -0
  61. package/packages/dd-trace/src/plugins/database.js +46 -0
  62. package/packages/dd-trace/src/plugins/tracing.js +2 -0
  63. package/packages/dd-trace/src/plugins/util/test.js +61 -8
  64. package/packages/dd-trace/src/plugins/util/web.js +9 -8
  65. package/packages/dd-trace/src/proxy.js +4 -3
  66. package/packages/dd-trace/src/span_processor.js +1 -0
  67. package/packages/dd-trace/src/tracer.js +4 -3
@@ -0,0 +1,132 @@
1
+ const { channel } = require('diagnostics_channel')
2
+
3
+ const {
4
+ getTestEnvironmentMetadata,
5
+ getCodeOwnersFileEntries,
6
+ getTestParentSpan,
7
+ getTestCommonTags,
8
+ getCodeOwnersForFilename,
9
+ TEST_CODE_OWNERS,
10
+ CI_APP_ORIGIN
11
+ } = require('./util/test')
12
+ const { getItrConfiguration } = require('../ci-visibility/intelligent-test-runner/get-itr-configuration')
13
+ const { getSkippableSuites } = require('../ci-visibility/intelligent-test-runner/get-skippable-suites')
14
+ const { COMPONENT } = require('../constants')
15
+
16
+ const Plugin = require('./plugin')
17
+
18
+ module.exports = class CiPlugin extends Plugin {
19
+ constructor (...args) {
20
+ super(...args)
21
+
22
+ const gitMetadataUploadFinishCh = channel('ci:git-metadata-upload:finish')
23
+ // `gitMetadataPromise` is used to wait until git metadata is uploaded to
24
+ // proceed with calculating the suites to skip
25
+ // TODO: add timeout after which the promise is resolved
26
+ const gitMetadataPromise = new Promise(resolve => {
27
+ gitMetadataUploadFinishCh.subscribe(err => {
28
+ resolve(err)
29
+ })
30
+ })
31
+
32
+ this.codeOwnersEntries = getCodeOwnersFileEntries()
33
+
34
+ this.addSub(`ci:${this.constructor.name}:configuration`, ({ onDone }) => {
35
+ if (!this.config.isAgentlessEnabled || !this.config.isIntelligentTestRunnerEnabled) {
36
+ onDone({ config: {} })
37
+ return
38
+ }
39
+ getItrConfiguration(this.testConfiguration, (err, config) => {
40
+ if (err) {
41
+ onDone({ err })
42
+ } else {
43
+ this.itrConfig = config
44
+ onDone({ config })
45
+ }
46
+ })
47
+ })
48
+
49
+ this.addSub(`ci:${this.constructor.name}:test-suite:skippable`, ({ onDone }) => {
50
+ if (!this.config.isAgentlessEnabled || !this.config.isIntelligentTestRunnerEnabled) {
51
+ return onDone({ skippableSuites: [] })
52
+ }
53
+ // we only request after git upload has happened, if it didn't fail
54
+ gitMetadataPromise.then((gitUploadError) => {
55
+ if (gitUploadError) {
56
+ return onDone({ err: gitUploadError })
57
+ }
58
+ if (!this.itrConfig || !this.itrConfig.isSuitesSkippingEnabled) {
59
+ return onDone({ skippableSuites: [] })
60
+ }
61
+ getSkippableSuites(this.testConfiguration, (err, skippableSuites) => {
62
+ if (err) {
63
+ onDone({ err })
64
+ } else {
65
+ onDone({ skippableSuites })
66
+ }
67
+ })
68
+ })
69
+ })
70
+ }
71
+
72
+ configure (config) {
73
+ super.configure(config)
74
+ this.testEnvironmentMetadata = getTestEnvironmentMetadata(this.constructor.name, this.config)
75
+
76
+ const {
77
+ 'git.repository_url': repositoryUrl,
78
+ 'git.commit.sha': sha,
79
+ 'os.version': osVersion,
80
+ 'os.platform': osPlatform,
81
+ 'os.architecture': osArchitecture,
82
+ 'runtime.name': runtimeName,
83
+ 'runtime.version': runtimeVersion,
84
+ 'git.branch': branch
85
+ } = this.testEnvironmentMetadata
86
+
87
+ this.testConfiguration = {
88
+ repositoryUrl,
89
+ sha,
90
+ osVersion,
91
+ osPlatform,
92
+ osArchitecture,
93
+ runtimeName,
94
+ runtimeVersion,
95
+ branch,
96
+ url: this.config.url,
97
+ site: this.config.site,
98
+ env: this.tracer._env,
99
+ service: this.config.service || this.tracer._service
100
+ }
101
+ }
102
+
103
+ startTestSpan (name, suite, extraTags, childOf) {
104
+ const parent = childOf || getTestParentSpan(this.tracer)
105
+ const testCommonTags = getTestCommonTags(name, suite, this.tracer._version)
106
+
107
+ const testTags = {
108
+ ...testCommonTags,
109
+ [COMPONENT]: this.constructor.name,
110
+ ...extraTags
111
+ }
112
+
113
+ const codeOwners = getCodeOwnersForFilename(suite, this.codeOwnersEntries)
114
+
115
+ if (codeOwners) {
116
+ testTags[TEST_CODE_OWNERS] = codeOwners
117
+ }
118
+
119
+ const testSpan = this.tracer
120
+ .startSpan(`${this.constructor.name}.test`, {
121
+ childOf: parent,
122
+ tags: {
123
+ ...this.testEnvironmentMetadata,
124
+ ...testTags
125
+ }
126
+ })
127
+
128
+ testSpan.context()._trace.origin = CI_APP_ORIGIN
129
+
130
+ return testSpan
131
+ }
132
+ }
@@ -4,6 +4,52 @@ const StoragePlugin = require('./storage')
4
4
 
5
5
  class DatabasePlugin extends StoragePlugin {
6
6
  static get operation () { return 'query' }
7
+
8
+ constructor (...args) {
9
+ super(...args)
10
+ this.serviceTags = {
11
+ dddbs: '',
12
+ encodedDddbs: '',
13
+ dde: '',
14
+ encodedDde: '',
15
+ ddps: '',
16
+ encodedDdps: '',
17
+ ddpv: '',
18
+ encodedDdpv: ''
19
+ }
20
+ }
21
+ encodingServiceTags (serviceTag, encodeATag, spanConfig) {
22
+ if (serviceTag !== spanConfig) {
23
+ this.serviceTags[serviceTag] = spanConfig
24
+ this.serviceTags[encodeATag] = encodeURIComponent(spanConfig)
25
+ }
26
+ }
27
+
28
+ createDBMPropagationCommentService () {
29
+ this.encodingServiceTags('dddbs', 'encodedDddbs', this.config.service)
30
+ this.encodingServiceTags('dde', 'encodedDde', this.tracer._env)
31
+ this.encodingServiceTags('ddps', 'encodedDdps', this.tracer._service)
32
+ this.encodingServiceTags('ddpv', 'encodedDdpv', this.tracer._version)
33
+
34
+ const { encodedDddbs, encodedDde, encodedDdps, encodedDdpv } = this.serviceTags
35
+
36
+ return `dddbs='${encodedDddbs}',dde='${encodedDde}',` +
37
+ `ddps='${encodedDdps}',ddpv='${encodedDdpv}'`
38
+ }
39
+
40
+ injectDbmQuery (query) {
41
+ if (this.config.dbmPropagationMode === 'disabled') {
42
+ return query
43
+ }
44
+ const servicePropagation = this.createDBMPropagationCommentService()
45
+ if (this.config.dbmPropagationMode === 'service') {
46
+ return `/*${servicePropagation}*/ ${query}`
47
+ } else if (this.config.dbmPropagationMode === 'full') {
48
+ this.activeSpan.setTag('_dd.dbm_trace_injected', 'true')
49
+ const traceparent = this.activeSpan._spanContext.toTraceparent()
50
+ return `/*${servicePropagation},traceparent='${traceparent}'*/ ${query}`
51
+ }
52
+ }
7
53
  }
8
54
 
9
55
  module.exports = DatabasePlugin
@@ -3,6 +3,7 @@
3
3
  const Plugin = require('./plugin')
4
4
  const { storage } = require('../../../datadog-core')
5
5
  const analyticsSampler = require('../analytics_sampler')
6
+ const { COMPONENT } = require('../constants')
6
7
 
7
8
  class TracingPlugin extends Plugin {
8
9
  constructor (...args) {
@@ -72,6 +73,7 @@ class TracingPlugin extends Plugin {
72
73
  const span = this.tracer.startSpan(name, {
73
74
  childOf,
74
75
  tags: {
76
+ [COMPONENT]: this.component,
75
77
  'service.name': service || this.tracer._service,
76
78
  'resource.name': resource,
77
79
  'span.kind': kind,
@@ -1,6 +1,7 @@
1
1
  const path = require('path')
2
2
  const fs = require('fs')
3
3
 
4
+ const istanbul = require('istanbul-lib-coverage')
4
5
  const ignore = require('ignore')
5
6
 
6
7
  const { getGitMetadata } = require('./git')
@@ -40,10 +41,6 @@ const TEST_COMMAND = 'test.command'
40
41
  const TEST_SESSION_ID = 'test_session_id'
41
42
  const TEST_SUITE_ID = 'test_suite_id'
42
43
 
43
- const ERROR_TYPE = 'error.type'
44
- const ERROR_MESSAGE = 'error.msg'
45
- const ERROR_STACK = 'error.stack'
46
-
47
44
  const CI_APP_ORIGIN = 'ciapp-test'
48
45
 
49
46
  const JEST_TEST_RUNNER = 'test.jest.test_runner'
@@ -65,9 +62,6 @@ module.exports = {
65
62
  TEST_SKIP_REASON,
66
63
  TEST_IS_RUM_ACTIVE,
67
64
  TEST_SOURCE_FILE,
68
- ERROR_TYPE,
69
- ERROR_MESSAGE,
70
- ERROR_STACK,
71
65
  CI_APP_ORIGIN,
72
66
  LIBRARY_VERSION,
73
67
  getTestEnvironmentMetadata,
@@ -84,7 +78,11 @@ module.exports = {
84
78
  TEST_SESSION_ID,
85
79
  TEST_SUITE_ID,
86
80
  TEST_ITR_TESTS_SKIPPED,
87
- TEST_CODE_COVERAGE_LINES_TOTAL
81
+ TEST_CODE_COVERAGE_LINES_TOTAL,
82
+ getCoveredFilenamesFromCoverage,
83
+ resetCoverage,
84
+ mergeCoverage,
85
+ fromCoverageMapToCoverage
88
86
  }
89
87
 
90
88
  function getTestEnvironmentMetadata (testFramework, config) {
@@ -262,3 +260,58 @@ function getTestSuiteCommonTags (command, version, testSuite) {
262
260
  [TEST_COMMAND]: command
263
261
  }
264
262
  }
263
+
264
+ function getCoveredFilenamesFromCoverage (coverage) {
265
+ const coverageMap = istanbul.createCoverageMap(coverage)
266
+
267
+ return coverageMap
268
+ .files()
269
+ .filter(filename => {
270
+ const fileCoverage = coverageMap.fileCoverageFor(filename)
271
+ const lineCoverage = fileCoverage.getLineCoverage()
272
+ const isAnyLineExecuted = Object.entries(lineCoverage).some(([, numExecutions]) => !!numExecutions)
273
+
274
+ return isAnyLineExecuted
275
+ })
276
+ }
277
+
278
+ function resetCoverage (coverage) {
279
+ const coverageMap = istanbul.createCoverageMap(coverage)
280
+
281
+ return coverageMap
282
+ .files()
283
+ .forEach(filename => {
284
+ const fileCoverage = coverageMap.fileCoverageFor(filename)
285
+ fileCoverage.resetHits()
286
+ })
287
+ }
288
+
289
+ function mergeCoverage (coverage, targetCoverage) {
290
+ const coverageMap = istanbul.createCoverageMap(coverage)
291
+ return coverageMap
292
+ .files()
293
+ .forEach(filename => {
294
+ const fileCoverage = coverageMap.fileCoverageFor(filename)
295
+
296
+ // If the fileCoverage is not there for this filename,
297
+ // we create it to force a merge between the fileCoverages
298
+ // instead of a reference assignment (which would not work if the coverage is reset later on)
299
+ if (!targetCoverage.data[filename]) {
300
+ targetCoverage.addFileCoverage(istanbul.createFileCoverage(filename))
301
+ }
302
+ targetCoverage.addFileCoverage(fileCoverage)
303
+ const targetFileCoverage = targetCoverage.fileCoverageFor(filename)
304
+
305
+ // branches (.b) are copied by reference, so `resetHits` affects the copy, so we need to copy it manually
306
+ Object.entries(targetFileCoverage.data.b).forEach(([key, value]) => {
307
+ targetFileCoverage.data.b[key] = [...value]
308
+ })
309
+ })
310
+ }
311
+
312
+ function fromCoverageMapToCoverage (coverageMap) {
313
+ return Object.entries(coverageMap.data).reduce((acc, [filename, fileCoverage]) => {
314
+ acc[filename] = fileCoverage.data
315
+ return acc
316
+ }, {})
317
+ }
@@ -11,6 +11,7 @@ const kinds = require('../../../../../ext/kinds')
11
11
  const urlFilter = require('./urlfilter')
12
12
  const BlockList = require('./ip_blocklist')
13
13
  const { incomingHttpRequestEnd } = require('../../appsec/gateway/channels')
14
+ const { ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK } = require('../../constants')
14
15
 
15
16
  const WEB = types.WEB
16
17
  const SERVER = kinds.SERVER
@@ -51,8 +52,6 @@ const ends = new WeakMap()
51
52
  const web = {
52
53
  // Ensure the configuration has the correct structure and defaults.
53
54
  normalizeConfig (config) {
54
- config = config.server || config
55
-
56
55
  const headers = getHeadersToRecord(config)
57
56
  const validateStatus = getStatusValidator(config)
58
57
  const hooks = getHooks(config)
@@ -60,14 +59,15 @@ const web = {
60
59
  const middleware = getMiddlewareSetting(config)
61
60
  const queryStringObfuscation = getQsObfuscator(config)
62
61
 
63
- return Object.assign({}, config, {
62
+ return {
63
+ ...config,
64
64
  headers,
65
65
  validateStatus,
66
66
  hooks,
67
67
  filter,
68
68
  middleware,
69
69
  queryStringObfuscation
70
- })
70
+ }
71
71
  },
72
72
 
73
73
  setFramework (req, name, config) {
@@ -77,6 +77,7 @@ const web = {
77
77
  if (!span) return
78
78
 
79
79
  span.context()._name = `${name}.request`
80
+ span.context()._tags['component'] = name
80
81
 
81
82
  web.setConfig(req, config)
82
83
  },
@@ -204,9 +205,9 @@ const web = {
204
205
  if (span) {
205
206
  if (error) {
206
207
  span.addTags({
207
- 'error.type': error.name,
208
- 'error.msg': error.message,
209
- 'error.stack': error.stack
208
+ [ERROR_TYPE]: error.name,
209
+ [ERROR_MESSAGE]: error.message,
210
+ [ERROR_STACK]: error.stack
210
211
  })
211
212
  }
212
213
 
@@ -276,7 +277,7 @@ const web = {
276
277
  const context = contexts.get(req)
277
278
  const span = context.span
278
279
  const error = context.error
279
- const hasExistingError = span.context()._tags['error'] || span.context()._tags['error.msg']
280
+ const hasExistingError = span.context()._tags['error'] || span.context()._tags[ERROR_MESSAGE]
280
281
 
281
282
  if (!hasExistingError && !context.config.validateStatus(statusCode)) {
282
283
  span.setTag(ERROR, error || true)
@@ -10,6 +10,7 @@ const { setStartupLogPluginManager } = require('./startup-log')
10
10
  const telemetry = require('./telemetry')
11
11
  const PluginManager = require('./plugin_manager')
12
12
  const { sendGitMetadata } = require('./ci-visibility/exporters/git/git_metadata')
13
+ const remoteConfig = require('./appsec/remote_config')
13
14
 
14
15
  const gitMetadataUploadFinishCh = channel('ci:git-metadata-upload:finish')
15
16
 
@@ -29,8 +30,7 @@ class Tracer extends NoopProxy {
29
30
  try {
30
31
  const config = new Config(options) // TODO: support dynamic config
31
32
 
32
- log.use(config.logger)
33
- log.toggle(config.debug, config.logLevel, this)
33
+ remoteConfig.enable(config)
34
34
 
35
35
  if (config.profiling.enabled) {
36
36
  // do not stop tracer initialization if the profiler fails to be imported
@@ -51,6 +51,7 @@ class Tracer extends NoopProxy {
51
51
  if (config.appsec.enabled) {
52
52
  require('./appsec').enable(config)
53
53
  }
54
+
54
55
  if (config.iast.enabled) {
55
56
  require('./appsec/iast').enable(config)
56
57
  }
@@ -64,7 +65,7 @@ class Tracer extends NoopProxy {
64
65
  if (config.isGitUploadEnabled) {
65
66
  sendGitMetadata(config.site, (err) => {
66
67
  if (err) {
67
- log.error(`Error uploading git metadata: ${err}`)
68
+ log.error(`Error uploading git metadata: ${err.message}`)
68
69
  } else {
69
70
  log.debug('Successfully uploaded git metadata')
70
71
  }
@@ -27,6 +27,7 @@ class SpanProcessor {
27
27
  const { flushMinSpans } = this._config
28
28
  const { started, finished } = trace
29
29
 
30
+ if (trace.record === false) return
30
31
  if (started.length === finished.length || finished.length >= flushMinSpans) {
31
32
  this._prioritySampler.sample(spanContext)
32
33
  this._spanSampler.sample(spanContext)
@@ -6,6 +6,7 @@ const Scope = require('./scope')
6
6
  const { storage } = require('../../datadog-core')
7
7
  const { isError } = require('./util')
8
8
  const { setStartupLogConfig } = require('./startup-log')
9
+ const { ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK } = require('../../dd-trace/src/constants')
9
10
 
10
11
  const SPAN_TYPE = tags.SPAN_TYPE
11
12
  const RESOURCE_NAME = tags.RESOURCE_NAME
@@ -143,9 +144,9 @@ class DatadogTracer extends Tracer {
143
144
  function addError (span, error) {
144
145
  if (isError(error)) {
145
146
  span.addTags({
146
- 'error.type': error.name,
147
- 'error.msg': error.message,
148
- 'error.stack': error.stack
147
+ [ERROR_TYPE]: error.name,
148
+ [ERROR_MESSAGE]: error.message,
149
+ [ERROR_STACK]: error.stack
149
150
  })
150
151
  }
151
152
  }