dd-trace 2.4.2 → 2.5.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 (36) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/ci/init.js +6 -0
  3. package/ci/jest/env.js +16 -3
  4. package/ext/exporters.d.ts +2 -1
  5. package/ext/exporters.js +2 -1
  6. package/package.json +3 -2
  7. package/packages/datadog-instrumentations/src/cypress.js +8 -0
  8. package/packages/datadog-plugin-aws-sdk/src/helpers.js +4 -4
  9. package/packages/datadog-plugin-aws-sdk/src/index.js +1 -1
  10. package/packages/datadog-plugin-cucumber/src/index.js +24 -12
  11. package/packages/datadog-plugin-cypress/src/index.js +10 -5
  12. package/packages/datadog-plugin-cypress/src/plugin.js +13 -1
  13. package/packages/datadog-plugin-mocha/src/index.js +10 -1
  14. package/packages/dd-trace/lib/version.js +1 -1
  15. package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +3 -1
  16. package/packages/dd-trace/src/appsec/recommended.json +15 -5
  17. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +32 -0
  18. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +51 -0
  19. package/packages/dd-trace/src/config.js +8 -1
  20. package/packages/dd-trace/src/encode/0.4.js +0 -1
  21. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +193 -0
  22. package/packages/dd-trace/src/encode/tags-processors.js +116 -0
  23. package/packages/dd-trace/src/exporter.js +3 -0
  24. package/packages/dd-trace/src/exporters/agent/index.js +1 -1
  25. package/packages/dd-trace/src/exporters/agent/writer.js +7 -32
  26. package/packages/dd-trace/src/exporters/{agent → common}/docker.js +0 -0
  27. package/packages/dd-trace/src/exporters/common/request.js +83 -0
  28. package/packages/dd-trace/src/exporters/common/writer.js +36 -0
  29. package/packages/dd-trace/src/exporters/{agent/scheduler.js → scheduler.js} +0 -0
  30. package/packages/dd-trace/src/instrumenter.js +3 -0
  31. package/packages/dd-trace/src/pkg.js +11 -6
  32. package/packages/dd-trace/src/plugins/util/test.js +60 -1
  33. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
  34. package/packages/dd-trace/src/proxy.js +2 -0
  35. package/packages/dd-trace/src/telemetry.js +187 -0
  36. package/packages/dd-trace/src/exporters/agent/request.js +0 -86
@@ -7,6 +7,7 @@ require,@types/node,MIT,Copyright Authors
7
7
  require,crypto-randomuuid,MIT,Copyright 2021 Node.js Foundation and contributors
8
8
  require,diagnostics_channel,MIT,Copyright 2021 Simon D.
9
9
  require,form-data,MIT,Copyright 2012 Felix Geisendörfer and contributors
10
+ require,ignore,MIT,Copyright 2013 Kael Zhang and contributors
10
11
  require,import-in-the-middle,Apache license 2.0,Copyright 2021 Datadog Inc.
11
12
  require,koalas,MIT,Copyright 2013-2017 Brian Woodward
12
13
  require,limiter,MIT,Copyright 2011 John Hurliman
package/ci/init.js CHANGED
@@ -11,6 +11,12 @@ const options = {
11
11
  }
12
12
  }
13
13
 
14
+ if (process.env.DD_CIVISIBILITY_AGENTLESS_ENABLED && (process.env.DATADOG_API_KEY || process.env.DD_API_KEY)) {
15
+ options.experimental = {
16
+ exporter: 'datadog'
17
+ }
18
+ }
19
+
14
20
  // TODO: remove this in a later major version since we now recommend using
15
21
  // `NODE_OPTIONS='-r dd-trace/ci/init'`.
16
22
  try {
package/ci/jest/env.js CHANGED
@@ -1,13 +1,26 @@
1
1
  const tracer = require('../../packages/dd-trace')
2
2
  const { ORIGIN_KEY } = require('../../packages/dd-trace/src/constants')
3
3
 
4
- tracer.init({
4
+ const options = {
5
5
  startupLogs: false,
6
- flushInterval: 400000,
7
6
  tags: {
8
7
  [ORIGIN_KEY]: 'ciapp-test'
9
8
  }
10
- })
9
+ }
10
+
11
+ if (process.env.DD_CIVISIBILITY_AGENTLESS_ENABLED && (process.env.DATADOG_API_KEY || process.env.DD_API_KEY)) {
12
+ tracer.init({
13
+ ...options,
14
+ experimental: {
15
+ exporter: 'datadog'
16
+ }
17
+ })
18
+ } else {
19
+ tracer.init({
20
+ ...options,
21
+ flushInterval: 400000
22
+ })
23
+ }
11
24
 
12
25
  tracer.use('fs', false)
13
26
 
@@ -1,6 +1,7 @@
1
1
  declare const exporters: {
2
2
  LOG: 'log',
3
- AGENT: 'agent'
3
+ AGENT: 'agent',
4
+ DATADOG: 'datadog'
4
5
  }
5
6
 
6
7
  export = exporters
package/ext/exporters.js CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict'
2
2
  module.exports = {
3
3
  LOG: 'log',
4
- AGENT: 'agent'
4
+ AGENT: 'agent',
5
+ DATADOG: 'datadog'
5
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "2.4.2",
3
+ "version": "2.5.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -61,7 +61,7 @@
61
61
  "node": ">=12"
62
62
  },
63
63
  "dependencies": {
64
- "@datadog/native-appsec": "^1.0.0",
64
+ "@datadog/native-appsec": "^1.0.1",
65
65
  "@datadog/native-metrics": "^1.1.0",
66
66
  "@datadog/pprof": "^0.3.0",
67
67
  "@datadog/sketches-js": "^1.0.4",
@@ -69,6 +69,7 @@
69
69
  "crypto-randomuuid": "^1.0.0",
70
70
  "diagnostics_channel": "^1.1.0",
71
71
  "form-data": "^3.0.0",
72
+ "ignore": "^5.2.0",
72
73
  "import-in-the-middle": "^1.2.1",
73
74
  "koalas": "^1.0.2",
74
75
  "limiter": "^1.1.4",
@@ -0,0 +1,8 @@
1
+ const { addHook } = require('./helpers/instrument')
2
+
3
+ // No handler because this is only useful for testing.
4
+ // Cypress plugin does not patch any library.
5
+ addHook({
6
+ name: 'cypress',
7
+ versions: ['>=6.7.0']
8
+ })
@@ -19,7 +19,7 @@ function getService (Service) {
19
19
  }
20
20
 
21
21
  const helpers = {
22
- finish (span, err) {
22
+ finish (config, span, response, err) {
23
23
  if (err) {
24
24
  span.setTag('error', err)
25
25
 
@@ -28,6 +28,8 @@ const helpers = {
28
28
  }
29
29
  }
30
30
 
31
+ config.hooks.request(span, response)
32
+
31
33
  span.finish()
32
34
  },
33
35
 
@@ -43,14 +45,12 @@ const helpers = {
43
45
  : true
44
46
  },
45
47
 
46
- addResponseTags (span, response, serviceName, config) {
48
+ addResponseTags (span, response, serviceName) {
47
49
  if (!span) return
48
50
 
49
51
  if (response.request) {
50
52
  this.addServicesTags(span, response, serviceName)
51
53
  }
52
-
53
- config.hooks.request(span, response)
54
54
  },
55
55
 
56
56
  addServicesTags (span, response, serviceName) {
@@ -36,7 +36,7 @@ function createWrapRequest (tracer, config) {
36
36
  if (!span) return
37
37
 
38
38
  awsHelpers.addResponseTags(span, response, serviceIdentifier, config, tracer)
39
- awsHelpers.finish(span, response.error)
39
+ awsHelpers.finish(config, span, response, response.error)
40
40
  })
41
41
 
42
42
  analyticsSampler.sample(span, config.measured)
@@ -12,9 +12,12 @@ const {
12
12
  TEST_FRAMEWORK_VERSION,
13
13
  ERROR_MESSAGE,
14
14
  TEST_STATUS,
15
+ TEST_CODE_OWNERS,
15
16
  finishAllTraceSpans,
16
17
  getTestEnvironmentMetadata,
17
- getTestSuitePath
18
+ getTestSuitePath,
19
+ getCodeOwnersFileEntries,
20
+ getCodeOwnersForFilename
18
21
  } = require('../../dd-trace/src/plugins/util/test')
19
22
  const { SPAN_TYPE, RESOURCE_NAME, SAMPLING_PRIORITY } = require('../../../ext/tags')
20
23
  const { SAMPLING_RULE_DECISION } = require('../../dd-trace/src/constants')
@@ -30,26 +33,35 @@ class CucumberPlugin extends Plugin {
30
33
 
31
34
  const testEnvironmentMetadata = getTestEnvironmentMetadata('cucumber', this.config)
32
35
  const sourceRoot = process.cwd()
36
+ const codeOwnersEntries = getCodeOwnersFileEntries(sourceRoot)
33
37
 
34
38
  this.addSub('ci:cucumber:run:start', ({ pickleName, pickleUri }) => {
35
39
  const store = storage.getStore()
36
40
  const childOf = store ? store.span : store
37
41
  const testSuite = getTestSuitePath(pickleUri, sourceRoot)
38
42
 
43
+ const testSpanMetadata = {
44
+ [SPAN_TYPE]: 'test',
45
+ [RESOURCE_NAME]: pickleName,
46
+ [TEST_TYPE]: 'test',
47
+ [TEST_NAME]: pickleName,
48
+ [TEST_SUITE]: testSuite,
49
+ [SAMPLING_RULE_DECISION]: 1,
50
+ [SAMPLING_PRIORITY]: AUTO_KEEP,
51
+ [TEST_FRAMEWORK_VERSION]: this.tracer._version,
52
+ ...testEnvironmentMetadata
53
+ }
54
+
55
+ const codeOwners = getCodeOwnersForFilename(testSuite, codeOwnersEntries)
56
+ if (codeOwners) {
57
+ testSpanMetadata[TEST_CODE_OWNERS] = codeOwners
58
+ }
59
+
39
60
  const span = this.tracer.startSpan('cucumber.test', {
40
61
  childOf,
41
- tags: {
42
- [SPAN_TYPE]: 'test',
43
- [RESOURCE_NAME]: pickleName,
44
- [TEST_TYPE]: 'test',
45
- [TEST_NAME]: pickleName,
46
- [TEST_SUITE]: testSuite,
47
- [SAMPLING_RULE_DECISION]: 1,
48
- [SAMPLING_PRIORITY]: AUTO_KEEP,
49
- [TEST_FRAMEWORK_VERSION]: this.tracer._version,
50
- ...testEnvironmentMetadata
51
- }
62
+ tags: testSpanMetadata
52
63
  })
64
+
53
65
  span.context()._trace.origin = CI_APP_ORIGIN
54
66
  this.enter(span, store)
55
67
  })
@@ -1,6 +1,11 @@
1
- module.exports = [
2
- {
3
- name: 'cypress',
4
- versions: ['>=6.7.0']
1
+ const Plugin = require('../../dd-trace/src/plugins/plugin')
2
+
3
+ // Cypress plugin does not patch any library. This is just a placeholder to
4
+ // follow the structure of the plugins
5
+ class CypressPlugin extends Plugin {
6
+ static get name () {
7
+ return 'cypress'
5
8
  }
6
- ]
9
+ }
10
+
11
+ module.exports = CypressPlugin
@@ -5,9 +5,12 @@ const {
5
5
  TEST_STATUS,
6
6
  TEST_FRAMEWORK_VERSION,
7
7
  TEST_IS_RUM_ACTIVE,
8
+ TEST_CODE_OWNERS,
8
9
  getTestEnvironmentMetadata,
9
10
  CI_APP_ORIGIN,
10
- getTestParentSpan
11
+ getTestParentSpan,
12
+ getCodeOwnersFileEntries,
13
+ getCodeOwnersForFilename
11
14
  } = require('../../dd-trace/src/plugins/util/test')
12
15
 
13
16
  const { SAMPLING_RULE_DECISION, ORIGIN_KEY } = require('../../dd-trace/src/constants')
@@ -39,6 +42,9 @@ function getTestSpanMetadata (tracer, testName, testSuite, cypressConfig) {
39
42
  module.exports = (on, config) => {
40
43
  const tracer = require('../../dd-trace')
41
44
  const testEnvironmentMetadata = getTestEnvironmentMetadata('cypress')
45
+
46
+ const codeOwnersEntries = getCodeOwnersFileEntries()
47
+
42
48
  let activeSpan = null
43
49
  on('after:run', () => {
44
50
  return new Promise(resolve => {
@@ -55,6 +61,12 @@ module.exports = (on, config) => {
55
61
  ...testSpanMetadata
56
62
  } = getTestSpanMetadata(tracer, testName, testSuite, config)
57
63
 
64
+ const codeOwners = getCodeOwnersForFilename(testSuite, codeOwnersEntries)
65
+
66
+ if (codeOwners) {
67
+ testSpanMetadata[TEST_CODE_OWNERS] = codeOwners
68
+ }
69
+
58
70
  if (!activeSpan) {
59
71
  activeSpan = tracer.startSpan('cypress.test', {
60
72
  childOf,
@@ -5,6 +5,7 @@ const { storage } = require('../../datadog-core')
5
5
 
6
6
  const {
7
7
  CI_APP_ORIGIN,
8
+ TEST_CODE_OWNERS,
8
9
  TEST_TYPE,
9
10
  TEST_NAME,
10
11
  TEST_SUITE,
@@ -15,7 +16,9 @@ const {
15
16
  getTestEnvironmentMetadata,
16
17
  getTestSuitePath,
17
18
  getTestParentSpan,
18
- getTestParametersString
19
+ getTestParametersString,
20
+ getCodeOwnersFileEntries,
21
+ getCodeOwnersForFilename
19
22
  } = require('../../dd-trace/src/plugins/util/test')
20
23
  const { SPAN_TYPE, RESOURCE_NAME, SAMPLING_PRIORITY } = require('../../../ext/tags')
21
24
  const { SAMPLING_RULE_DECISION } = require('../../dd-trace/src/constants')
@@ -54,6 +57,7 @@ class MochaPlugin extends Plugin {
54
57
  this._testNameToParams = {}
55
58
  this.testEnvironmentMetadata = getTestEnvironmentMetadata('mocha', this.config)
56
59
  this.sourceRoot = process.cwd()
60
+ this.codeOwnersEntries = getCodeOwnersFileEntries(this.sourceRoot)
57
61
 
58
62
  this.addSub('ci:mocha:test:start', (test) => {
59
63
  const store = storage.getStore()
@@ -139,6 +143,11 @@ class MochaPlugin extends Plugin {
139
143
  if (testParametersString) {
140
144
  testSpanMetadata[TEST_PARAMETERS] = testParametersString
141
145
  }
146
+ const codeOwners = getCodeOwnersForFilename(testSpanMetadata[TEST_SUITE], this.codeOwnersEntries)
147
+
148
+ if (codeOwners) {
149
+ testSpanMetadata[TEST_CODE_OWNERS] = codeOwners
150
+ }
142
151
 
143
152
  const testSpan = this.tracer
144
153
  .startSpan('mocha.test', {
@@ -1 +1 @@
1
- module.exports = '2.4.2'
1
+ module.exports = '2.5.0'
@@ -70,7 +70,7 @@ class WAFCallback {
70
70
  }
71
71
  }
72
72
 
73
- if (!wafContext) {
73
+ if (!wafContext || wafContext.disposed) {
74
74
  wafContext = this.ddwaf.createContext()
75
75
  }
76
76
 
@@ -87,6 +87,8 @@ class WAFCallback {
87
87
  } catch (err) {
88
88
  log.error('Error while running the AppSec WAF')
89
89
  log.error(err)
90
+ } finally {
91
+ wafContext.dispose()
90
92
  }
91
93
  }
92
94
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": "2.2",
3
3
  "metadata": {
4
- "rules_version": "1.3.0"
4
+ "rules_version": "1.3.1"
5
5
  },
6
6
  "rules": [
7
7
  {
@@ -3040,7 +3040,9 @@
3040
3040
  "operator": "match_regex"
3041
3041
  }
3042
3042
  ],
3043
- "transformers": []
3043
+ "transformers": [
3044
+ "keys_only"
3045
+ ]
3044
3046
  },
3045
3047
  {
3046
3048
  "id": "crs-942-360",
@@ -4097,15 +4099,23 @@
4097
4099
  "parameters": {
4098
4100
  "inputs": [
4099
4101
  {
4100
- "address": "server.request.headers.no_cookies"
4102
+ "address": "server.request.query"
4103
+ },
4104
+ {
4105
+ "address": "server.request.body"
4106
+ },
4107
+ {
4108
+ "address": "server.request.path_params"
4101
4109
  }
4102
4110
  ],
4103
- "regex": "\\$(eq|ne|lte?|gte?|n?in)\\b"
4111
+ "regex": "^\\$(eq|ne|(l|g)te?|n?in|not|(n|x|)or|and|regex|where|expr|exists)$"
4104
4112
  },
4105
4113
  "operator": "match_regex"
4106
4114
  }
4107
4115
  ],
4108
- "transformers": []
4116
+ "transformers": [
4117
+ "keys_only"
4118
+ ]
4109
4119
  },
4110
4120
  {
4111
4121
  "id": "sqr-000-008",
@@ -0,0 +1,32 @@
1
+ 'use strict'
2
+
3
+ const URL = require('url').URL
4
+ const Writer = require('./writer')
5
+ const Scheduler = require('../../../exporters/scheduler')
6
+
7
+ class AgentlessCiVisibilityExporter {
8
+ constructor (config) {
9
+ const { flushInterval, tags, site, url } = config
10
+ this._url = url || new URL(`https://citestcycle-intake.${site}`)
11
+ this._writer = new Writer({ url: this._url, tags })
12
+
13
+ if (flushInterval > 0) {
14
+ this._scheduler = new Scheduler(() => this._writer.flush(), flushInterval)
15
+ }
16
+ this._scheduler && this._scheduler.start()
17
+ }
18
+
19
+ export (trace) {
20
+ this._writer.append(trace)
21
+
22
+ if (!this._scheduler) {
23
+ this._writer.flush()
24
+ }
25
+ }
26
+
27
+ flush () {
28
+ this._writer.flush()
29
+ }
30
+ }
31
+
32
+ module.exports = AgentlessCiVisibilityExporter
@@ -0,0 +1,51 @@
1
+ 'use strict'
2
+ const request = require('../../../exporters/common/request')
3
+ const log = require('../../../log')
4
+
5
+ const { AgentlessCiVisibilityEncoder } = require('../../../encode/agentless-ci-visibility')
6
+ const BaseWriter = require('../../../exporters/common/writer')
7
+
8
+ class Writer extends BaseWriter {
9
+ constructor ({ url, tags }) {
10
+ super(...arguments)
11
+ const { 'runtime-id': runtimeId, env, service } = tags
12
+ this._url = url
13
+ this._encoder = new AgentlessCiVisibilityEncoder({ runtimeId, env, service })
14
+ }
15
+
16
+ _sendPayload (data, _, done) {
17
+ makeRequest(data, this._url, (err, res) => {
18
+ if (err) {
19
+ log.error(err)
20
+ done()
21
+ return
22
+ }
23
+ log.debug(`Response from the intake: ${res}`)
24
+ done()
25
+ })
26
+ }
27
+ }
28
+
29
+ function makeRequest (data, url, cb) {
30
+ const options = {
31
+ path: '/api/v2/citestcycle',
32
+ method: 'POST',
33
+ headers: {
34
+ 'Content-Type': 'application/msgpack',
35
+ 'dd-api-key': process.env.DATADOG_API_KEY || process.env.DD_API_KEY
36
+ },
37
+ timeout: 15000
38
+ }
39
+
40
+ options.protocol = url.protocol
41
+ options.hostname = url.hostname
42
+ options.port = url.port
43
+
44
+ log.debug(() => `Request to the intake: ${JSON.stringify(options)}`)
45
+
46
+ request(data, options, false, (err, res) => {
47
+ cb(err, res)
48
+ })
49
+ }
50
+
51
+ module.exports = Writer
@@ -66,6 +66,7 @@ class Config {
66
66
  process.env.DD_TRACE_URL,
67
67
  null
68
68
  )
69
+ const DD_CIVISIBILITY_AGENTLESS_URL = process.env.DD_CIVISIBILITY_AGENTLESS_URL
69
70
  const DD_SERVICE = options.service ||
70
71
  process.env.DD_SERVICE ||
71
72
  process.env.DD_SERVICE_NAME ||
@@ -90,6 +91,10 @@ class Config {
90
91
  process.env.DD_TRACE_STARTUP_LOGS,
91
92
  false
92
93
  )
94
+ const DD_TRACE_TELEMETRY_ENABLED = coalesce(
95
+ process.env.DD_TRACE_TELEMETRY_ENABLED,
96
+ true
97
+ )
93
98
  const DD_TRACE_DEBUG = coalesce(
94
99
  process.env.DD_TRACE_DEBUG,
95
100
  false
@@ -171,7 +176,8 @@ class Config {
171
176
  this.debug = isTrue(DD_TRACE_DEBUG)
172
177
  this.logInjection = isTrue(DD_LOGS_INJECTION)
173
178
  this.env = DD_ENV
174
- this.url = getAgentUrl(DD_TRACE_AGENT_URL, options)
179
+ this.url = DD_CIVISIBILITY_AGENTLESS_URL ? new URL(DD_CIVISIBILITY_AGENTLESS_URL)
180
+ : getAgentUrl(DD_TRACE_AGENT_URL, options)
175
181
  this.site = coalesce(options.site, process.env.DD_SITE, 'datadoghq.com')
176
182
  this.hostname = DD_AGENT_HOST || (this.url && this.url.hostname)
177
183
  this.port = String(DD_TRACE_AGENT_PORT || (this.url && this.url.port))
@@ -212,6 +218,7 @@ class Config {
212
218
  }
213
219
  this.lookup = options.lookup
214
220
  this.startupLogs = isTrue(DD_TRACE_STARTUP_LOGS)
221
+ this.telemetryEnabled = isTrue(DD_TRACE_TELEMETRY_ENABLED)
215
222
  this.protocolVersion = DD_TRACE_AGENT_PROTOCOL_VERSION
216
223
  this.appsec = {
217
224
  enabled: isTrue(DD_APPSEC_ENABLED),
@@ -196,7 +196,6 @@ class AgentEncoder {
196
196
 
197
197
  for (const key of keys) {
198
198
  if (typeof value[key] !== 'string' && typeof value[key] !== 'number') return
199
-
200
199
  length++
201
200
 
202
201
  this._encodeString(bytes, key)