dd-trace 3.41.0 → 3.43.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 (76) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/index.d.ts +29 -0
  3. package/package.json +7 -6
  4. package/packages/datadog-instrumentations/src/aerospike.js +47 -0
  5. package/packages/datadog-instrumentations/src/apollo-server-core.js +41 -0
  6. package/packages/datadog-instrumentations/src/apollo-server.js +83 -0
  7. package/packages/datadog-instrumentations/src/graphql.js +18 -4
  8. package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
  9. package/packages/datadog-instrumentations/src/http/client.js +10 -0
  10. package/packages/datadog-instrumentations/src/jest.js +11 -5
  11. package/packages/datadog-instrumentations/src/kafkajs.js +27 -0
  12. package/packages/datadog-instrumentations/src/next.js +18 -6
  13. package/packages/datadog-instrumentations/src/restify.js +1 -1
  14. package/packages/datadog-instrumentations/src/rhea.js +15 -9
  15. package/packages/datadog-plugin-aerospike/src/index.js +113 -0
  16. package/packages/datadog-plugin-graphql/src/resolve.js +26 -18
  17. package/packages/datadog-plugin-http/src/client.js +19 -2
  18. package/packages/datadog-plugin-kafkajs/src/consumer.js +51 -0
  19. package/packages/datadog-plugin-kafkajs/src/producer.js +55 -0
  20. package/packages/datadog-plugin-next/src/index.js +40 -14
  21. package/packages/dd-trace/src/appsec/activation.js +29 -0
  22. package/packages/dd-trace/src/appsec/addresses.js +3 -1
  23. package/packages/dd-trace/src/appsec/api_security_sampler.js +48 -0
  24. package/packages/dd-trace/src/appsec/blocked_templates.js +4 -1
  25. package/packages/dd-trace/src/appsec/blocking.js +95 -43
  26. package/packages/dd-trace/src/appsec/channels.js +4 -1
  27. package/packages/dd-trace/src/appsec/graphql.js +146 -0
  28. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  29. package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +105 -0
  30. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/constants.js +7 -0
  31. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +12 -19
  32. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/header-sensitive-analyzer.js +20 -0
  33. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +6 -10
  34. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +18 -25
  35. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +79 -85
  36. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +27 -36
  37. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +14 -11
  38. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
  39. package/packages/dd-trace/src/appsec/index.js +32 -31
  40. package/packages/dd-trace/src/appsec/recommended.json +1395 -2
  41. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
  42. package/packages/dd-trace/src/appsec/remote_config/index.js +36 -15
  43. package/packages/dd-trace/src/appsec/reporter.js +19 -0
  44. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  45. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +28 -13
  46. package/packages/dd-trace/src/appsec/waf/waf_manager.js +0 -1
  47. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +17 -1
  48. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +75 -56
  49. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +15 -9
  50. package/packages/dd-trace/src/config.js +36 -2
  51. package/packages/dd-trace/src/datastreams/processor.js +107 -12
  52. package/packages/dd-trace/src/noop/proxy.js +4 -0
  53. package/packages/dd-trace/src/opentracing/span.js +2 -0
  54. package/packages/dd-trace/src/plugins/ci_plugin.js +2 -1
  55. package/packages/dd-trace/src/plugins/index.js +1 -0
  56. package/packages/dd-trace/src/plugins/util/git.js +2 -2
  57. package/packages/dd-trace/src/plugins/util/test.js +3 -2
  58. package/packages/dd-trace/src/plugins/util/user-provided-git.js +3 -2
  59. package/packages/dd-trace/src/profiler.js +5 -3
  60. package/packages/dd-trace/src/profiling/config.js +8 -0
  61. package/packages/dd-trace/src/profiling/profiler.js +17 -10
  62. package/packages/dd-trace/src/profiling/profilers/events.js +181 -83
  63. package/packages/dd-trace/src/profiling/profilers/shared.js +33 -3
  64. package/packages/dd-trace/src/profiling/profilers/space.js +2 -1
  65. package/packages/dd-trace/src/profiling/profilers/wall.js +17 -12
  66. package/packages/dd-trace/src/proxy.js +25 -1
  67. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +5 -0
  68. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
  69. package/packages/dd-trace/src/spanleak.js +98 -0
  70. package/packages/dd-trace/src/startup-log.js +7 -1
  71. package/packages/dd-trace/src/telemetry/dependencies.js +55 -9
  72. package/packages/dd-trace/src/telemetry/index.js +135 -43
  73. package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
  74. package/packages/dd-trace/src/telemetry/send-data.js +47 -5
  75. package/packages/dd-trace/src/tracer.js +4 -0
  76. package/scripts/install_plugin_modules.js +11 -3
@@ -9,5 +9,6 @@ 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
13
14
  }
@@ -1,38 +1,59 @@
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)
10
13
 
11
- if (config.appsec.enabled === undefined) { // only activate ASM_FEATURES when conf is not set locally
12
- rc.updateCapabilities(RemoteConfigCapabilities.ASM_ACTIVATION, true)
14
+ const activation = Activation.fromConfig(config)
15
+
16
+ if (activation !== Activation.DISABLED) {
17
+ if (activation === Activation.ONECLICK) {
18
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_ACTIVATION, true)
19
+ }
13
20
 
14
- rc.on('ASM_FEATURES', (action, conf) => {
15
- if (conf && conf.asm && typeof conf.asm.enabled === 'boolean') {
16
- let shouldEnable
21
+ if (config.appsec.apiSecurity?.enabled) {
22
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_API_SECURITY_SAMPLE_RATE, true)
23
+ }
17
24
 
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
- }
25
+ rc.on('ASM_FEATURES', (action, rcConfig) => {
26
+ if (!rcConfig) return
23
27
 
24
- if (shouldEnable) {
25
- require('..').enable(config)
26
- } else {
27
- require('..').disable()
28
- }
28
+ if (activation === Activation.ONECLICK) {
29
+ enableOrDisableAppsec(action, rcConfig, config)
29
30
  }
31
+
32
+ apiSecuritySampler.setRequestSampling(rcConfig.api_security?.request_sample_rate)
30
33
  })
31
34
  }
32
35
 
33
36
  return rc
34
37
  }
35
38
 
39
+ function enableOrDisableAppsec (action, rcConfig, config) {
40
+ if (typeof rcConfig.asm?.enabled === 'boolean') {
41
+ let shouldEnable
42
+
43
+ if (action === 'apply' || action === 'modify') {
44
+ shouldEnable = rcConfig.asm.enabled // take control
45
+ } else {
46
+ shouldEnable = config.appsec.enabled // give back control to local config
47
+ }
48
+
49
+ if (shouldEnable) {
50
+ require('..').enable(config)
51
+ } else {
52
+ require('..').disable()
53
+ }
54
+ }
55
+ }
56
+
36
57
  function enableWafUpdate (appsecConfig) {
37
58
  if (rc && appsecConfig && !appsecConfig.customRulesProvided) {
38
59
  // dirty require to make startup faster for serverless
@@ -10,6 +10,7 @@ const {
10
10
  incrementWafUpdatesMetric,
11
11
  incrementWafRequestsMetric
12
12
  } = require('./telemetry')
13
+ const zlib = require('zlib')
13
14
 
14
15
  // default limiter, configurable with setRateLimit()
15
16
  let limiter = new Limiter(100)
@@ -140,6 +141,23 @@ function reportAttack (attackData) {
140
141
  rootSpan.addTags(newTags)
141
142
  }
142
143
 
144
+ function reportSchemas (derivatives) {
145
+ if (!derivatives) return
146
+
147
+ const req = storage.getStore()?.req
148
+ const rootSpan = web.root(req)
149
+
150
+ if (!rootSpan) return
151
+
152
+ const tags = {}
153
+ for (const [address, value] of Object.entries(derivatives)) {
154
+ const gzippedValue = zlib.gzipSync(JSON.stringify(value))
155
+ tags[address] = gzippedValue.toString('base64')
156
+ }
157
+
158
+ rootSpan.addTags(tags)
159
+ }
160
+
143
161
  function finishRequest (req, res) {
144
162
  const rootSpan = web.root(req)
145
163
  if (!rootSpan) return
@@ -175,6 +193,7 @@ module.exports = {
175
193
  reportMetrics,
176
194
  reportAttack,
177
195
  reportWafUpdate: incrementWafUpdatesMetric,
196
+ reportSchemas,
178
197
  finishRequest,
179
198
  setRateLimit,
180
199
  mapHeaderAndTags
@@ -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
@@ -143,7 +143,23 @@ class CiVisibilityExporter extends AgentInfoExporter {
143
143
  * where the tests run in a subprocess, because `getItrConfiguration` is called only once.
144
144
  */
145
145
  this._itrConfig = itrConfig
146
- callback(err, itrConfig)
146
+
147
+ if (err) {
148
+ callback(err, {})
149
+ } else if (itrConfig?.requireGit) {
150
+ // If the backend requires git, we'll wait for the upload to finish and request settings again
151
+ this._gitUploadPromise.then(gitUploadError => {
152
+ if (gitUploadError) {
153
+ return callback(gitUploadError, {})
154
+ }
155
+ getItrConfigurationRequest(configuration, (err, finalItrConfig) => {
156
+ this._itrConfig = finalItrConfig
157
+ callback(err, finalItrConfig)
158
+ })
159
+ })
160
+ } else {
161
+ callback(null, itrConfig)
162
+ }
147
163
  })
148
164
  })
149
165
  }
@@ -10,7 +10,7 @@ const {
10
10
  getLatestCommits,
11
11
  getRepositoryUrl,
12
12
  generatePackFilesForCommits,
13
- getCommitsToUpload,
13
+ getCommitsRevList,
14
14
  isShallowRepository,
15
15
  unshallowRepository
16
16
  } = require('../../../plugins/util/git')
@@ -46,11 +46,7 @@ function getCommonRequestOptions (url) {
46
46
  * The response are the commits for which the backend already has information
47
47
  * This response is used to know which commits can be ignored from there on
48
48
  */
49
- function getCommitsToExclude ({ url, isEvpProxy, repositoryUrl }, callback) {
50
- const latestCommits = getLatestCommits()
51
-
52
- log.debug(`There were ${latestCommits.length} commits since last month.`)
53
-
49
+ function getCommitsToUpload ({ url, repositoryUrl, latestCommits, isEvpProxy }, callback) {
54
50
  const commonOptions = getCommonRequestOptions(url)
55
51
 
56
52
  const options = {
@@ -83,13 +79,23 @@ function getCommitsToExclude ({ url, isEvpProxy, repositoryUrl }, callback) {
83
79
  const error = new Error(`Error fetching commits to exclude: ${err.message}`)
84
80
  return callback(error)
85
81
  }
86
- let commitsToExclude
82
+ let alreadySeenCommits
87
83
  try {
88
- commitsToExclude = validateCommits(JSON.parse(response).data)
84
+ alreadySeenCommits = validateCommits(JSON.parse(response).data)
89
85
  } catch (e) {
90
86
  return callback(new Error(`Can't parse commits to exclude response: ${e.message}`))
91
87
  }
92
- callback(null, commitsToExclude, latestCommits)
88
+ log.debug(`There are ${alreadySeenCommits.length} commits to exclude.`)
89
+ const commitsToInclude = latestCommits.filter((commit) => !alreadySeenCommits.includes(commit))
90
+ log.debug(`There are ${commitsToInclude.length} commits to include.`)
91
+
92
+ if (!commitsToInclude.length) {
93
+ return callback(null, [])
94
+ }
95
+
96
+ const commitsToUpload = getCommitsRevList(alreadySeenCommits, commitsToInclude)
97
+
98
+ callback(null, commitsToUpload)
93
99
  })
94
100
  }
95
101
 
@@ -150,6 +156,53 @@ function uploadPackFile ({ url, isEvpProxy, packFileToUpload, repositoryUrl, hea
150
156
  })
151
157
  }
152
158
 
159
+ function generateAndUploadPackFiles ({
160
+ url,
161
+ isEvpProxy,
162
+ commitsToUpload,
163
+ repositoryUrl,
164
+ headCommit
165
+ }, callback) {
166
+ log.debug(`There are ${commitsToUpload.length} commits to upload`)
167
+
168
+ const packFilesToUpload = generatePackFilesForCommits(commitsToUpload)
169
+
170
+ log.debug(`Uploading ${packFilesToUpload.length} packfiles.`)
171
+
172
+ if (!packFilesToUpload.length) {
173
+ return callback(new Error('Failed to generate packfiles'))
174
+ }
175
+
176
+ let packFileIndex = 0
177
+ // This uploads packfiles sequentially
178
+ const uploadPackFileCallback = (err) => {
179
+ if (err || packFileIndex === packFilesToUpload.length) {
180
+ return callback(err)
181
+ }
182
+ return uploadPackFile(
183
+ {
184
+ packFileToUpload: packFilesToUpload[packFileIndex++],
185
+ url,
186
+ isEvpProxy,
187
+ repositoryUrl,
188
+ headCommit
189
+ },
190
+ uploadPackFileCallback
191
+ )
192
+ }
193
+
194
+ uploadPackFile(
195
+ {
196
+ packFileToUpload: packFilesToUpload[packFileIndex++],
197
+ url,
198
+ isEvpProxy,
199
+ repositoryUrl,
200
+ headCommit
201
+ },
202
+ uploadPackFileCallback
203
+ )
204
+ }
205
+
153
206
  /**
154
207
  * This function uploads git metadata to CI Visibility's backend.
155
208
  */
@@ -165,65 +218,31 @@ function sendGitMetadata (url, isEvpProxy, configRepositoryUrl, callback) {
165
218
  return callback(new Error('Repository URL is empty'))
166
219
  }
167
220
 
168
- if (isShallowRepository()) {
169
- log.debug('It is shallow clone, unshallowing...')
170
- unshallowRepository()
171
- }
221
+ const latestCommits = getLatestCommits()
222
+ log.debug(`There were ${latestCommits.length} commits since last month.`)
223
+ const [headCommit] = latestCommits
172
224
 
173
- getCommitsToExclude({ url, repositoryUrl, isEvpProxy }, (err, commitsToExclude, latestCommits) => {
225
+ const getOnFinishGetCommitsToUpload = (hasCheckedShallow) => (err, commitsToUpload) => {
174
226
  if (err) {
175
227
  return callback(err)
176
228
  }
177
- log.debug(`There are ${commitsToExclude.length} commits to exclude.`)
178
- const [headCommit] = latestCommits
179
- const commitsToInclude = latestCommits.filter((commit) => !commitsToExclude.includes(commit))
180
- log.debug(`There are ${commitsToInclude.length} commits to include.`)
181
-
182
- const commitsToUpload = getCommitsToUpload(commitsToExclude, commitsToInclude)
183
229
 
184
230
  if (!commitsToUpload.length) {
185
231
  log.debug('No commits to upload')
186
232
  return callback(null)
187
233
  }
188
- log.debug(`There are ${commitsToUpload.length} commits to upload`)
189
-
190
- const packFilesToUpload = generatePackFilesForCommits(commitsToUpload)
191
-
192
- log.debug(`Uploading ${packFilesToUpload.length} packfiles.`)
193
-
194
- if (!packFilesToUpload.length) {
195
- return callback(new Error('Failed to generate packfiles'))
196
- }
197
234
 
198
- let packFileIndex = 0
199
- // This uploads packfiles sequentially
200
- const uploadPackFileCallback = (err) => {
201
- if (err || packFileIndex === packFilesToUpload.length) {
202
- return callback(err)
203
- }
204
- return uploadPackFile(
205
- {
206
- packFileToUpload: packFilesToUpload[packFileIndex++],
207
- url,
208
- isEvpProxy,
209
- repositoryUrl,
210
- headCommit
211
- },
212
- uploadPackFileCallback
213
- )
235
+ // If it has already unshallowed or the clone is not shallow, we move on
236
+ if (hasCheckedShallow || !isShallowRepository()) {
237
+ return generateAndUploadPackFiles({ url, isEvpProxy, commitsToUpload, repositoryUrl, headCommit }, callback)
214
238
  }
239
+ // Otherwise we unshallow and get commits to upload again
240
+ log.debug('It is shallow clone, unshallowing...')
241
+ unshallowRepository()
242
+ getCommitsToUpload({ url, repositoryUrl, latestCommits, isEvpProxy }, getOnFinishGetCommitsToUpload(true))
243
+ }
215
244
 
216
- uploadPackFile(
217
- {
218
- packFileToUpload: packFilesToUpload[packFileIndex++],
219
- url,
220
- isEvpProxy,
221
- repositoryUrl,
222
- headCommit
223
- },
224
- uploadPackFileCallback
225
- )
226
- })
245
+ getCommitsToUpload({ url, repositoryUrl, latestCommits, isEvpProxy }, getOnFinishGetCommitsToUpload(false))
227
246
  }
228
247
 
229
248
  module.exports = {
@@ -15,6 +15,7 @@ function getItrConfiguration ({
15
15
  runtimeName,
16
16
  runtimeVersion,
17
17
  branch,
18
+ testLevel = 'suite',
18
19
  custom
19
20
  }, done) {
20
21
  const options = {
@@ -23,7 +24,8 @@ function getItrConfiguration ({
23
24
  headers: {
24
25
  'Content-Type': 'application/json'
25
26
  },
26
- url
27
+ url,
28
+ timeout: 20000
27
29
  }
28
30
 
29
31
  if (isEvpProxy) {
@@ -42,7 +44,7 @@ function getItrConfiguration ({
42
44
  id: id().toString(10),
43
45
  type: 'ci_app_test_service_libraries_settings',
44
46
  attributes: {
45
- test_level: 'suite',
47
+ test_level: testLevel,
46
48
  configurations: {
47
49
  'os.platform': osPlatform,
48
50
  'os.version': osVersion,
@@ -67,25 +69,29 @@ function getItrConfiguration ({
67
69
  try {
68
70
  const {
69
71
  data: {
70
- attributes
72
+ attributes: {
73
+ code_coverage: isCodeCoverageEnabled,
74
+ tests_skipping: isSuitesSkippingEnabled,
75
+ itr_enabled: isItrEnabled,
76
+ require_git: requireGit
77
+ }
71
78
  }
72
79
  } = JSON.parse(res)
73
80
 
74
- let isCodeCoverageEnabled = attributes.code_coverage
75
- let isSuitesSkippingEnabled = attributes.tests_skipping
81
+ const settings = { isCodeCoverageEnabled, isSuitesSkippingEnabled, isItrEnabled, requireGit }
76
82
 
77
- log.debug(() => `Remote settings: ${{ isCodeCoverageEnabled, isSuitesSkippingEnabled }}`)
83
+ log.debug(() => `Remote settings: ${JSON.stringify(settings)}`)
78
84
 
79
85
  if (process.env.DD_CIVISIBILITY_DANGEROUSLY_FORCE_COVERAGE) {
80
- isCodeCoverageEnabled = true
86
+ settings.isCodeCoverageEnabled = true
81
87
  log.debug(() => 'Dangerously set code coverage to true')
82
88
  }
83
89
  if (process.env.DD_CIVISIBILITY_DANGEROUSLY_FORCE_TEST_SKIPPING) {
84
- isSuitesSkippingEnabled = true
90
+ settings.isSuitesSkippingEnabled = true
85
91
  log.debug(() => 'Dangerously set test skipping to true')
86
92
  }
87
93
 
88
- done(null, { isCodeCoverageEnabled, isSuitesSkippingEnabled })
94
+ done(null, settings)
89
95
  } catch (err) {
90
96
  done(err)
91
97
  }
@@ -399,7 +399,6 @@ class Config {
399
399
  appsec.enabled,
400
400
  process.env.DD_APPSEC_ENABLED && isTrue(process.env.DD_APPSEC_ENABLED)
401
401
  )
402
-
403
402
  const DD_APPSEC_RULES = coalesce(
404
403
  appsec.rules,
405
404
  process.env.DD_APPSEC_RULES
@@ -436,11 +435,25 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
436
435
  maybeFile(appsec.blockedTemplateJson),
437
436
  maybeFile(process.env.DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON)
438
437
  )
438
+ const DD_APPSEC_GRAPHQL_BLOCKED_TEMPLATE_JSON = coalesce(
439
+ maybeFile(appsec.blockedTemplateGraphql),
440
+ maybeFile(process.env.DD_APPSEC_GRAPHQL_BLOCKED_TEMPLATE_JSON)
441
+ )
439
442
  const DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING = coalesce(
440
443
  appsec.eventTracking && appsec.eventTracking.mode,
441
444
  process.env.DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING,
442
445
  'safe'
443
446
  ).toLowerCase()
447
+ const DD_EXPERIMENTAL_API_SECURITY_ENABLED = coalesce(
448
+ appsec?.apiSecurity?.enabled,
449
+ isTrue(process.env.DD_EXPERIMENTAL_API_SECURITY_ENABLED),
450
+ false
451
+ )
452
+ const DD_API_SECURITY_REQUEST_SAMPLE_RATE = coalesce(
453
+ appsec?.apiSecurity?.requestSampling,
454
+ parseFloat(process.env.DD_API_SECURITY_REQUEST_SAMPLE_RATE),
455
+ 0.1
456
+ )
444
457
 
445
458
  const remoteConfigOptions = options.remoteConfig || {}
446
459
  const DD_REMOTE_CONFIGURATION_ENABLED = coalesce(
@@ -465,6 +478,11 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
465
478
  DD_IAST_ENABLED
466
479
  )
467
480
 
481
+ const DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED = coalesce(
482
+ process.env.DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED,
483
+ true
484
+ )
485
+
468
486
  const defaultIastRequestSampling = 30
469
487
  const iastRequestSampling = coalesce(
470
488
  parseInt(iastOptions?.requestSampling),
@@ -526,6 +544,12 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
526
544
  true
527
545
  )
528
546
 
547
+ // 0: disabled, 1: logging, 2: garbage collection + logging
548
+ const DD_TRACE_SPAN_LEAK_DEBUG = coalesce(
549
+ process.env.DD_TRACE_SPAN_LEAK_DEBUG,
550
+ 0
551
+ )
552
+
529
553
  const ingestion = options.ingestion || {}
530
554
  const dogstatsd = coalesce(options.dogstatsd, {})
531
555
  const sampler = {
@@ -609,7 +633,8 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
609
633
  heartbeatInterval: DD_TELEMETRY_HEARTBEAT_INTERVAL,
610
634
  debug: isTrue(DD_TELEMETRY_DEBUG),
611
635
  logCollection: isTrue(DD_TELEMETRY_LOG_COLLECTION_ENABLED),
612
- metrics: isTrue(DD_TELEMETRY_METRICS_ENABLED)
636
+ metrics: isTrue(DD_TELEMETRY_METRICS_ENABLED),
637
+ dependencyCollection: DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED
613
638
  }
614
639
  this.protocolVersion = DD_TRACE_AGENT_PROTOCOL_VERSION
615
640
  this.tagsHeaderMaxLength = parseInt(DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH)
@@ -623,11 +648,18 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
623
648
  obfuscatorValueRegex: DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP,
624
649
  blockedTemplateHtml: DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML,
625
650
  blockedTemplateJson: DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON,
651
+ blockedTemplateGraphql: DD_APPSEC_GRAPHQL_BLOCKED_TEMPLATE_JSON,
626
652
  eventTracking: {
627
653
  enabled: ['extended', 'safe'].includes(DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING),
628
654
  mode: DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING
655
+ },
656
+ apiSecurity: {
657
+ enabled: DD_EXPERIMENTAL_API_SECURITY_ENABLED,
658
+ // Coerce value between 0 and 1
659
+ requestSampling: Math.min(1, Math.max(0, DD_API_SECURITY_REQUEST_SAMPLE_RATE))
629
660
  }
630
661
  }
662
+
631
663
  this.remoteConfig = {
632
664
  enabled: DD_REMOTE_CONFIGURATION_ENABLED,
633
665
  pollInterval: DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS
@@ -701,6 +733,8 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
701
733
  this.isGCPFunction = isGCPFunction
702
734
  this.isAzureFunctionConsumptionPlan = isAzureFunctionConsumptionPlan
703
735
 
736
+ this.spanLeakDebug = Number(DD_TRACE_SPAN_LEAK_DEBUG)
737
+
704
738
  tagger.add(this.tags, {
705
739
  service: this.service,
706
740
  env: this.env,