dd-trace 5.0.0 → 5.1.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 (28) hide show
  1. package/MIGRATING.md +15 -0
  2. package/README.md +11 -9
  3. package/package.json +4 -3
  4. package/packages/datadog-instrumentations/src/cucumber.js +3 -1
  5. package/packages/datadog-instrumentations/src/jest.js +1 -0
  6. package/packages/datadog-instrumentations/src/mocha.js +9 -2
  7. package/packages/datadog-plugin-cucumber/src/index.js +11 -7
  8. package/packages/datadog-plugin-cypress/src/plugin.js +60 -46
  9. package/packages/datadog-plugin-jest/src/index.js +7 -1
  10. package/packages/datadog-plugin-mocha/src/index.js +11 -2
  11. package/packages/datadog-plugin-playwright/src/index.js +2 -0
  12. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  13. package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +3 -3
  14. package/packages/dd-trace/src/appsec/iast/analyzers/weak-randomness-analyzer.js +19 -0
  15. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +1 -0
  16. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +12 -1
  17. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
  18. package/packages/dd-trace/src/appsec/remote_config/manager.js +9 -8
  19. package/packages/dd-trace/src/appsec/reporter.js +2 -1
  20. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +4 -2
  21. package/packages/dd-trace/src/config.js +10 -6
  22. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +25 -2
  23. package/packages/dd-trace/src/plugins/ci_plugin.js +9 -3
  24. package/packages/dd-trace/src/plugins/util/test.js +2 -0
  25. package/packages/dd-trace/src/profiling/config.js +19 -5
  26. package/packages/dd-trace/src/telemetry/index.js +8 -3
  27. package/packages/dd-trace/src/telemetry/send-data.js +2 -2
  28. package/scripts/st.js +105 -0
package/MIGRATING.md CHANGED
@@ -4,6 +4,21 @@ This guide describes the steps to upgrade dd-trace from a major version to the
4
4
  next. If you are having any issues related to migrating, please feel free to
5
5
  open an issue or contact our [support](https://www.datadoghq.com/support/) team.
6
6
 
7
+ ## 4.0 to 5.0
8
+
9
+ ### Node 16 is no longer supported
10
+
11
+ Node.js 16 has reached EOL in September 2023 and is no longer supported. Generally
12
+ speaking, we highly recommend always keeping Node.js up to date regardless of
13
+ our support policy.
14
+
15
+ ### Update `trace<T>` TypeScript declaration
16
+
17
+ The TypeScript declaration for `trace<T>` has been updated to enforce
18
+ that calls to `tracer.trace(name, fn)` must receive a function which takes at least
19
+ the span object. Previously the span was technically optional when it should not have
20
+ been as the span must be handled.
21
+
7
22
  ## 3.0 to 4.0
8
23
 
9
24
  ### Node 14 is no longer supported
package/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # `dd-trace`: Node.js APM Tracer Library
2
2
 
3
- [![npm v4](https://img.shields.io/npm/v/dd-trace/latest?color=blue&label=dd-trace%40v4&logo=npm)](https://www.npmjs.com/package/dd-trace)
4
- [![npm v3](https://img.shields.io/npm/v/dd-trace/latest-node14?color=blue&label=dd-trace%40v3&logo=npm)](https://www.npmjs.com/package/dd-trace/v/latest-node12)
3
+ [![npm v5](https://img.shields.io/npm/v/dd-trace/latest?color=blue&label=dd-trace%40v5&logo=npm)](https://www.npmjs.com/package/dd-trace)
4
+ [![npm v4](https://img.shields.io/npm/v/dd-trace/latest-node16?color=blue&label=dd-trace%40v4&logo=npm)](https://www.npmjs.com/package/dd-trace/v/latest-node16)
5
+ [![npm v3](https://img.shields.io/npm/v/dd-trace/latest-node14?color=blue&label=dd-trace%40v3&logo=npm)](https://www.npmjs.com/package/dd-trace/v/latest-node14)
5
6
  [![codecov](https://codecov.io/gh/DataDog/dd-trace-js/branch/master/graph/badge.svg)](https://codecov.io/gh/DataDog/dd-trace-js)
6
7
 
7
8
  <img align="right" src="https://user-images.githubusercontent.com/551402/208212084-1d0c07e2-4135-4c61-b2da-8f2fddbc66ed.png" alt="Bits the dog JavaScript" width="200px"/>
@@ -28,24 +29,25 @@ Most of the documentation for `dd-trace` is available on these webpages:
28
29
  | [`v1`](https://github.com/DataDog/dd-trace-js/tree/v1.x) | ![npm v1](https://img.shields.io/npm/v/dd-trace/legacy-v1?color=white&label=%20&style=flat-square) | `>= v12` | **End of Life** | 2021-07-13 | 2022-02-25 |
29
30
  | [`v2`](https://github.com/DataDog/dd-trace-js/tree/v2.x) | ![npm v2](https://img.shields.io/npm/v/dd-trace/latest-node12?color=white&label=%20&style=flat-square) | `>= v12` | **End of Life** | 2022-01-28 | 2023-08-15 |
30
31
  | [`v3`](https://github.com/DataDog/dd-trace-js/tree/v3.x) | ![npm v3](https://img.shields.io/npm/v/dd-trace/latest-node14?color=white&label=%20&style=flat-square) | `>= v14` | **Maintenance** | 2022-08-15 | 2024-05-15 |
31
- | [`v4`](https://github.com/DataDog/dd-trace-js/tree/v4.x) | ![npm v4](https://img.shields.io/npm/v/dd-trace/latest?color=white&label=%20&style=flat-square) | `>= v16` | **Current** | 2023-05-12 | Unknown |
32
+ | [`v4`](https://github.com/DataDog/dd-trace-js/tree/v4.x) | ![npm v4](https://img.shields.io/npm/v/dd-trace/latest-node16?color=white&label=%20&style=flat-square) | `>= v16` | **Maintenance** | 2023-05-12 | 2025-01-11 |
33
+ | [`v5`](https://github.com/DataDog/dd-trace-js/tree/v5.x) | ![npm v5](https://img.shields.io/npm/v/dd-trace/latest?color=white&label=%20&style=flat-square) | `>= v18` | **Current** | 2024-01-11 | Unknown |
32
34
 
33
- We currently maintain two release lines, namely `v3` and `v4`.
34
- Features and bug fixes that are merged are released to the `v4` line and, if appropriate, also the `v3` line.
35
+ We currently maintain three release lines, namely `v5`, `v4` and `v3`.
36
+ Features and bug fixes that are merged are released to the `v5` line and, if appropriate, also the `v4` & `v3` line.
35
37
 
36
- For any new projects it is recommended to use the `v4` release line:
38
+ For any new projects it is recommended to use the `v5` release line:
37
39
 
38
40
  ```sh
39
41
  $ npm install dd-trace
40
42
  $ yarn add dd-trace
41
43
  ```
42
44
 
43
- However, existing projects that already use the `v3` release line, or projects that need to support EOL versions of Node.js, may continue to use these release lines.
45
+ However, existing projects that already use the `v4` & `v3` release line, or projects that need to support EOL versions of Node.js, may continue to use these release lines.
44
46
  This is done by specifying the version when installing the package.
45
47
 
46
48
  ```sh
47
- $ npm install dd-trace@3
48
- $ yarn add dd-trace@3
49
+ $ npm install dd-trace@4
50
+ $ yarn add dd-trace@4
49
51
  ```
50
52
 
51
53
  Any backwards-breaking functionality that is introduced into the library will result in an increase of the major version of the library and therefore a new release line.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.0.0",
3
+ "version": "5.1.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -36,6 +36,7 @@
36
36
  "test:integration:cucumber": "mocha --colors --timeout 30000 \"integration-tests/cucumber/*.spec.js\"",
37
37
  "test:integration:cypress": "mocha --colors --timeout 30000 \"integration-tests/cypress/*.spec.js\"",
38
38
  "test:integration:playwright": "mocha --colors --timeout 30000 \"integration-tests/playwright/*.spec.js\"",
39
+ "test:integration:profiler": "mocha --colors --timeout 90000 \"integration-tests/profiler/*.spec.js\"",
39
40
  "test:integration:serverless": "mocha --colors --timeout 30000 \"integration-tests/serverless/*.spec.js\"",
40
41
  "test:integration:plugins": "mocha --colors --exit -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/integration-test/**/*.spec.js\"",
41
42
  "test:unit:plugins": "mocha --colors --exit -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-instrumentations/test/@($(echo $PLUGINS)).spec.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/**/*.spec.js\" --exclude \"packages/datadog-plugin-@($(echo $PLUGINS))/test/integration-test/**/*.spec.js\"",
@@ -68,7 +69,7 @@
68
69
  "node": ">=18"
69
70
  },
70
71
  "dependencies": {
71
- "@datadog/native-appsec": "6.0.0",
72
+ "@datadog/native-appsec": "7.0.0",
72
73
  "@datadog/native-iast-rewriter": "2.2.2",
73
74
  "@datadog/native-iast-taint-tracking": "1.6.4",
74
75
  "@datadog/native-metrics": "^2.0.0",
@@ -79,7 +80,7 @@
79
80
  "crypto-randomuuid": "^1.0.0",
80
81
  "dc-polyfill": "^0.1.2",
81
82
  "ignore": "^5.2.4",
82
- "import-in-the-middle": "^1.7.1",
83
+ "import-in-the-middle": "^1.7.3",
83
84
  "int64-buffer": "^0.1.9",
84
85
  "ipaddr.js": "^2.1.0",
85
86
  "istanbul-lib-coverage": "3.2.0",
@@ -44,6 +44,7 @@ const patched = new WeakSet()
44
44
  let pickleByFile = {}
45
45
  const pickleResultByFile = {}
46
46
  let skippableSuites = []
47
+ let itrCorrelationId = ''
47
48
  let isForcedToRun = false
48
49
  let isUnskippable = false
49
50
 
@@ -102,7 +103,7 @@ function wrapRun (pl, isLatestVersion) {
102
103
  const testSuitePath = getTestSuitePath(testSuiteFullPath, process.cwd())
103
104
  isForcedToRun = isUnskippable && skippableSuites.includes(testSuitePath)
104
105
 
105
- testSuiteStartCh.publish({ testSuitePath, isUnskippable, isForcedToRun })
106
+ testSuiteStartCh.publish({ testSuitePath, isUnskippable, isForcedToRun, itrCorrelationId })
106
107
  }
107
108
 
108
109
  const testSourceLine = this.gherkinDocument &&
@@ -304,6 +305,7 @@ addHook({
304
305
  this.pickleIds = picklesToRun
305
306
 
306
307
  skippedSuites = Array.from(filteredPickles.skippedSuites)
308
+ itrCorrelationId = skippableResponse.itrCorrelationId
307
309
  }
308
310
 
309
311
  pickleByFile = getPickleByFile(this)
@@ -497,6 +497,7 @@ addHook({
497
497
  _ddTestCommand,
498
498
  _ddForcedToRun,
499
499
  _ddUnskippable,
500
+ _ddItrCorrelationId,
500
501
  ...restOfTestEnvironmentOptions
501
502
  } = testEnvironmentOptions
502
503
 
@@ -53,6 +53,7 @@ let isSuitesSkipped = false
53
53
  let skippedSuites = []
54
54
  const unskippableSuites = []
55
55
  let isForcedToRun = false
56
+ let itrCorrelationId = ''
56
57
 
57
58
  function getSuitesByTestFile (root) {
58
59
  const suitesByTestFile = {}
@@ -191,7 +192,12 @@ function mochaHook (Runner) {
191
192
  const isUnskippable = unskippableSuites.includes(suite.file)
192
193
  isForcedToRun = isUnskippable && suitesToSkip.includes(getTestSuitePath(suite.file, process.cwd()))
193
194
  asyncResource.runInAsyncScope(() => {
194
- testSuiteStartCh.publish({ testSuite: suite.file, isUnskippable, isForcedToRun })
195
+ testSuiteStartCh.publish({
196
+ testSuite: suite.file,
197
+ isUnskippable,
198
+ isForcedToRun,
199
+ itrCorrelationId
200
+ })
195
201
  })
196
202
  }
197
203
  })
@@ -395,11 +401,12 @@ addHook({
395
401
  }
396
402
  })
397
403
 
398
- const onReceivedSkippableSuites = ({ err, skippableSuites }) => {
404
+ const onReceivedSkippableSuites = ({ err, skippableSuites, itrCorrelationId: responseItrCorrelationId }) => {
399
405
  if (err) {
400
406
  suitesToSkip = []
401
407
  } else {
402
408
  suitesToSkip = skippableSuites
409
+ itrCorrelationId = responseItrCorrelationId
403
410
  }
404
411
  // We remove the suites that we skip through ITR
405
412
  const filteredSuites = getFilteredSuites(runner.suite.suites)
@@ -13,7 +13,8 @@ const {
13
13
  addIntelligentTestRunnerSpanTags,
14
14
  TEST_ITR_UNSKIPPABLE,
15
15
  TEST_ITR_FORCED_RUN,
16
- TEST_CODE_OWNERS
16
+ TEST_CODE_OWNERS,
17
+ ITR_CORRELATION_ID
17
18
  } = require('../../dd-trace/src/plugins/util/test')
18
19
  const { RESOURCE_NAME } = require('../../../ext/tags')
19
20
  const { COMPONENT, ERROR_MESSAGE } = require('../../dd-trace/src/constants')
@@ -74,7 +75,7 @@ class CucumberPlugin extends CiPlugin {
74
75
  this.tracer._exporter.flush()
75
76
  })
76
77
 
77
- this.addSub('ci:cucumber:test-suite:start', ({ testSuitePath, isUnskippable, isForcedToRun }) => {
78
+ this.addSub('ci:cucumber:test-suite:start', ({ testSuitePath, isUnskippable, isForcedToRun, itrCorrelationId }) => {
78
79
  const testSuiteMetadata = getTestSuiteCommonTags(
79
80
  this.command,
80
81
  this.frameworkVersion,
@@ -89,6 +90,9 @@ class CucumberPlugin extends CiPlugin {
89
90
  this.telemetry.count(TELEMETRY_ITR_FORCED_TO_RUN, { testLevel: 'suite' })
90
91
  testSuiteMetadata[TEST_ITR_FORCED_RUN] = 'true'
91
92
  }
93
+ if (itrCorrelationId) {
94
+ testSuiteMetadata[ITR_CORRELATION_ID] = itrCorrelationId
95
+ }
92
96
  this.testSuiteSpan = this.tracer.startSpan('cucumber.test_suite', {
93
97
  childOf: this.testModuleSpan,
94
98
  tags: {
@@ -169,12 +173,12 @@ class CucumberPlugin extends CiPlugin {
169
173
  }
170
174
 
171
175
  span.finish()
172
- this.telemetry.ciVisEvent(
173
- TELEMETRY_EVENT_FINISHED,
174
- 'test',
175
- { hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS] }
176
- )
177
176
  if (!isStep) {
177
+ this.telemetry.ciVisEvent(
178
+ TELEMETRY_EVENT_FINISHED,
179
+ 'test',
180
+ { hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS] }
181
+ )
178
182
  finishAllTraceSpans(span)
179
183
  }
180
184
  })
@@ -23,7 +23,8 @@ const {
23
23
  addIntelligentTestRunnerSpanTags,
24
24
  TEST_SKIPPED_BY_ITR,
25
25
  TEST_ITR_UNSKIPPABLE,
26
- TEST_ITR_FORCED_RUN
26
+ TEST_ITR_FORCED_RUN,
27
+ ITR_CORRELATION_ID
27
28
  } = require('../../dd-trace/src/plugins/util/test')
28
29
  const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
29
30
  const log = require('../../dd-trace/src/log')
@@ -39,6 +40,7 @@ const {
39
40
  incrementCountMetric,
40
41
  distributionMetric
41
42
  } = require('../../dd-trace/src/ci-visibility/telemetry')
43
+ const { appClosing: appClosingTelemetry } = require('../../dd-trace/src/telemetry')
42
44
  const {
43
45
  GIT_REPOSITORY_URL,
44
46
  GIT_COMMIT_SHA,
@@ -137,10 +139,11 @@ function getSkippableTests (isSuitesSkippingEnabled, tracer, testConfiguration)
137
139
  if (!tracer._tracer._exporter || !tracer._tracer._exporter.getItrConfiguration) {
138
140
  return resolve({ err: new Error('CI Visibility was not initialized correctly') })
139
141
  }
140
- tracer._tracer._exporter.getSkippableSuites(testConfiguration, (err, skippableTests) => {
142
+ tracer._tracer._exporter.getSkippableSuites(testConfiguration, (err, skippableTests, correlationId) => {
141
143
  resolve({
142
144
  err,
143
- skippableTests
145
+ skippableTests,
146
+ correlationId
144
147
  })
145
148
  })
146
149
  })
@@ -214,6 +217,7 @@ module.exports = (on, config) => {
214
217
  let isSuitesSkippingEnabled = false
215
218
  let isCodeCoverageEnabled = false
216
219
  let testsToSkip = []
220
+ let itrCorrelationId = ''
217
221
  const unskippableSuites = []
218
222
  let hasForcedToRunSuites = false
219
223
  let hasUnskippableSuites = false
@@ -288,52 +292,54 @@ module.exports = (on, config) => {
288
292
  isCodeCoverageEnabled = itrConfig.isCodeCoverageEnabled
289
293
  }
290
294
 
291
- return getSkippableTests(isSuitesSkippingEnabled, tracer, testConfiguration).then(({ err, skippableTests }) => {
292
- if (err) {
293
- log.error(err)
294
- } else {
295
- testsToSkip = skippableTests || []
296
- }
297
-
298
- // `details.specs` are test files
299
- details.specs.forEach(({ absolute, relative }) => {
300
- const isUnskippableSuite = isMarkedAsUnskippable({ path: absolute })
301
- if (isUnskippableSuite) {
302
- unskippableSuites.push(relative)
295
+ return getSkippableTests(isSuitesSkippingEnabled, tracer, testConfiguration)
296
+ .then(({ err, skippableTests, correlationId }) => {
297
+ if (err) {
298
+ log.error(err)
299
+ } else {
300
+ testsToSkip = skippableTests || []
301
+ itrCorrelationId = correlationId
303
302
  }
304
- })
305
-
306
- const childOf = getTestParentSpan(tracer)
307
- rootDir = getRootDir(details)
308
-
309
- command = getCypressCommand(details)
310
- frameworkVersion = getCypressVersion(details)
311
-
312
- const testSessionSpanMetadata = getTestSessionCommonTags(command, frameworkVersion, TEST_FRAMEWORK_NAME)
313
- const testModuleSpanMetadata = getTestModuleCommonTags(command, frameworkVersion, TEST_FRAMEWORK_NAME)
314
303
 
315
- testSessionSpan = tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_session`, {
316
- childOf,
317
- tags: {
318
- [COMPONENT]: TEST_FRAMEWORK_NAME,
319
- ...testEnvironmentMetadata,
320
- ...testSessionSpanMetadata
321
- }
322
- })
323
- ciVisEvent(TELEMETRY_EVENT_CREATED, 'session')
324
-
325
- testModuleSpan = tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_module`, {
326
- childOf: testSessionSpan,
327
- tags: {
328
- [COMPONENT]: TEST_FRAMEWORK_NAME,
329
- ...testEnvironmentMetadata,
330
- ...testModuleSpanMetadata
331
- }
304
+ // `details.specs` are test files
305
+ details.specs.forEach(({ absolute, relative }) => {
306
+ const isUnskippableSuite = isMarkedAsUnskippable({ path: absolute })
307
+ if (isUnskippableSuite) {
308
+ unskippableSuites.push(relative)
309
+ }
310
+ })
311
+
312
+ const childOf = getTestParentSpan(tracer)
313
+ rootDir = getRootDir(details)
314
+
315
+ command = getCypressCommand(details)
316
+ frameworkVersion = getCypressVersion(details)
317
+
318
+ const testSessionSpanMetadata = getTestSessionCommonTags(command, frameworkVersion, TEST_FRAMEWORK_NAME)
319
+ const testModuleSpanMetadata = getTestModuleCommonTags(command, frameworkVersion, TEST_FRAMEWORK_NAME)
320
+
321
+ testSessionSpan = tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_session`, {
322
+ childOf,
323
+ tags: {
324
+ [COMPONENT]: TEST_FRAMEWORK_NAME,
325
+ ...testEnvironmentMetadata,
326
+ ...testSessionSpanMetadata
327
+ }
328
+ })
329
+ ciVisEvent(TELEMETRY_EVENT_CREATED, 'session')
330
+
331
+ testModuleSpan = tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_module`, {
332
+ childOf: testSessionSpan,
333
+ tags: {
334
+ [COMPONENT]: TEST_FRAMEWORK_NAME,
335
+ ...testEnvironmentMetadata,
336
+ ...testModuleSpanMetadata
337
+ }
338
+ })
339
+ ciVisEvent(TELEMETRY_EVENT_CREATED, 'module')
340
+
341
+ return details
332
342
  })
333
- ciVisEvent(TELEMETRY_EVENT_CREATED, 'module')
334
-
335
- return details
336
- })
337
343
  })
338
344
  })
339
345
  on('after:spec', (spec, { tests, stats }) => {
@@ -357,6 +363,9 @@ module.exports = (on, config) => {
357
363
  if (isSkippedByItr) {
358
364
  skippedTestSpan.setTag(TEST_SKIPPED_BY_ITR, 'true')
359
365
  }
366
+ if (itrCorrelationId) {
367
+ skippedTestSpan.setTag(ITR_CORRELATION_ID, itrCorrelationId)
368
+ }
360
369
  skippedTestSpan.finish()
361
370
  })
362
371
 
@@ -378,6 +387,9 @@ module.exports = (on, config) => {
378
387
  finishedTest.testSpan.setTag(TEST_STATUS, cypressTestStatus)
379
388
  finishedTest.testSpan.setTag('error', latestError)
380
389
  }
390
+ if (itrCorrelationId) {
391
+ finishedTest.testSpan.setTag(ITR_CORRELATION_ID, itrCorrelationId)
392
+ }
381
393
  finishedTest.testSpan.finish(finishedTest.finishTime)
382
394
  })
383
395
 
@@ -429,10 +441,12 @@ module.exports = (on, config) => {
429
441
  }
430
442
  if (exporter.flush) {
431
443
  exporter.flush(() => {
444
+ appClosingTelemetry()
432
445
  resolve(null)
433
446
  })
434
447
  } else if (exporter._writer) {
435
448
  exporter._writer.flush(() => {
449
+ appClosingTelemetry()
436
450
  resolve(null)
437
451
  })
438
452
  }
@@ -13,7 +13,8 @@ const {
13
13
  TEST_SOURCE_START,
14
14
  TEST_ITR_UNSKIPPABLE,
15
15
  TEST_ITR_FORCED_RUN,
16
- TEST_CODE_OWNERS
16
+ TEST_CODE_OWNERS,
17
+ ITR_CORRELATION_ID
17
18
  } = require('../../dd-trace/src/plugins/util/test')
18
19
  const { COMPONENT } = require('../../dd-trace/src/constants')
19
20
  const id = require('../../dd-trace/src/id')
@@ -121,6 +122,7 @@ class JestPlugin extends CiPlugin {
121
122
  config._ddTestSessionId = this.testSessionSpan.context().toTraceId()
122
123
  config._ddTestModuleId = this.testModuleSpan.context().toSpanId()
123
124
  config._ddTestCommand = this.testSessionSpan.context()._tags[TEST_COMMAND]
125
+ config._ddItrCorrelationId = this.itrCorrelationId
124
126
  })
125
127
  })
126
128
 
@@ -129,6 +131,7 @@ class JestPlugin extends CiPlugin {
129
131
  _ddTestSessionId: testSessionId,
130
132
  _ddTestCommand: testCommand,
131
133
  _ddTestModuleId: testModuleId,
134
+ _ddItrCorrelationId: itrCorrelationId,
132
135
  _ddForcedToRun,
133
136
  _ddUnskippable,
134
137
  _ddTestCodeCoverageEnabled
@@ -155,6 +158,9 @@ class JestPlugin extends CiPlugin {
155
158
  }
156
159
  }
157
160
  }
161
+ if (itrCorrelationId) {
162
+ testSuiteMetadata[ITR_CORRELATION_ID] = itrCorrelationId
163
+ }
158
164
 
159
165
  this.testSuiteSpan = this.tracer.startSpan('jest.test_suite', {
160
166
  childOf: testSessionSpanContext,
@@ -14,7 +14,8 @@ const {
14
14
  TEST_SOURCE_START,
15
15
  TEST_ITR_UNSKIPPABLE,
16
16
  TEST_ITR_FORCED_RUN,
17
- TEST_CODE_OWNERS
17
+ TEST_CODE_OWNERS,
18
+ ITR_CORRELATION_ID
18
19
  } = require('../../dd-trace/src/plugins/util/test')
19
20
  const { COMPONENT } = require('../../dd-trace/src/constants')
20
21
  const {
@@ -66,7 +67,12 @@ class MochaPlugin extends CiPlugin {
66
67
  this.telemetry.distribution(TELEMETRY_CODE_COVERAGE_NUM_FILES, {}, relativeCoverageFiles.length)
67
68
  })
68
69
 
69
- this.addSub('ci:mocha:test-suite:start', ({ testSuite, isUnskippable, isForcedToRun }) => {
70
+ this.addSub('ci:mocha:test-suite:start', ({
71
+ testSuite,
72
+ isUnskippable,
73
+ isForcedToRun,
74
+ itrCorrelationId
75
+ }) => {
70
76
  const store = storage.getStore()
71
77
  const testSuiteMetadata = getTestSuiteCommonTags(
72
78
  this.command,
@@ -95,6 +101,9 @@ class MochaPlugin extends CiPlugin {
95
101
  if (this.itrConfig?.isCodeCoverageEnabled) {
96
102
  this.telemetry.ciVisEvent(TELEMETRY_CODE_COVERAGE_STARTED, 'suite', { library: 'istanbul' })
97
103
  }
104
+ if (itrCorrelationId) {
105
+ testSuiteSpan.setTag(ITR_CORRELATION_ID, itrCorrelationId)
106
+ }
98
107
  this.enter(testSuiteSpan, store)
99
108
  this._testSuites.set(testSuite, testSuiteSpan)
100
109
  })
@@ -17,6 +17,7 @@ const {
17
17
  TELEMETRY_EVENT_CREATED,
18
18
  TELEMETRY_EVENT_FINISHED
19
19
  } = require('../../dd-trace/src/ci-visibility/telemetry')
20
+ const { appClosing: appClosingTelemetry } = require('../../dd-trace/src/telemetry')
20
21
 
21
22
  class PlaywrightPlugin extends CiPlugin {
22
23
  static get id () {
@@ -37,6 +38,7 @@ class PlaywrightPlugin extends CiPlugin {
37
38
  this.testSessionSpan.finish()
38
39
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session')
39
40
  finishAllTraceSpans(this.testSessionSpan)
41
+ appClosingTelemetry()
40
42
  this.tracer._exporter.flush(onDone)
41
43
  })
42
44
 
@@ -16,5 +16,6 @@ module.exports = {
16
16
  'UNVALIDATED_REDIRECT_ANALYZER': require('./unvalidated-redirect-analyzer'),
17
17
  'WEAK_CIPHER_ANALYZER': require('./weak-cipher-analyzer'),
18
18
  'WEAK_HASH_ANALYZER': require('./weak-hash-analyzer'),
19
+ 'WEAK_RANDOMNESS_ANALYZER': require('./weak-randomness-analyzer'),
19
20
  'XCONTENTTYPE_HEADER_MISSING_ANALYZER': require('./xcontenttype-header-missing-analyzer')
20
21
  }
@@ -48,7 +48,7 @@ class HeaderInjectionAnalyzer extends InjectionAnalyzer {
48
48
  if (ranges?.length > 0) {
49
49
  return !(this.isCookieExclusion(lowerCasedHeaderName, ranges) ||
50
50
  this.isSameHeaderExclusion(lowerCasedHeaderName, ranges) ||
51
- this.isAccessControlAllowOriginExclusion(lowerCasedHeaderName, ranges))
51
+ this.isAccessControlAllowExclusion(lowerCasedHeaderName, ranges))
52
52
  }
53
53
 
54
54
  return false
@@ -84,8 +84,8 @@ class HeaderInjectionAnalyzer extends InjectionAnalyzer {
84
84
  return false
85
85
  }
86
86
 
87
- isAccessControlAllowOriginExclusion (name, ranges) {
88
- if (name === 'access-control-allow-origin') {
87
+ isAccessControlAllowExclusion (name, ranges) {
88
+ if (name?.startsWith('access-control-allow-')) {
89
89
  return ranges
90
90
  .every(range => range.iinfo.type === HTTP_REQUEST_HEADER_VALUE)
91
91
  }
@@ -0,0 +1,19 @@
1
+ 'use strict'
2
+ const Analyzer = require('./vulnerability-analyzer')
3
+ const { WEAK_RANDOMNESS } = require('../vulnerabilities')
4
+
5
+ class WeakRandomnessAnalyzer extends Analyzer {
6
+ constructor () {
7
+ super(WEAK_RANDOMNESS)
8
+ }
9
+
10
+ onConfigure () {
11
+ this.addSub('datadog:random:call', ({ fn }) => this.analyze(fn))
12
+ }
13
+
14
+ _isVulnerable (fn) {
15
+ return fn === Math.random
16
+ }
17
+ }
18
+
19
+ module.exports = new WeakRandomnessAnalyzer()
@@ -3,6 +3,7 @@
3
3
  const csiMethods = [
4
4
  { src: 'concat' },
5
5
  { src: 'plusOperator', operator: true },
6
+ { src: 'random' },
6
7
  { src: 'replace' },
7
8
  { src: 'slice' },
8
9
  { src: 'substr' },
@@ -1,5 +1,6 @@
1
1
  'use strict'
2
2
 
3
+ const dc = require('dc-polyfill')
3
4
  const TaintedUtils = require('@datadog/native-iast-taint-tracking')
4
5
  const { storage } = require('../../../../../datadog-core')
5
6
  const iastContextFunctions = require('../iast-context')
@@ -7,12 +8,15 @@ const iastLog = require('../iast-log')
7
8
  const { EXECUTED_PROPAGATION } = require('../telemetry/iast-metric')
8
9
  const { isDebugAllowed } = require('../telemetry/verbosity')
9
10
 
11
+ const mathRandomCallCh = dc.channel('datadog:random:call')
12
+
10
13
  function noop (res) { return res }
11
14
  // NOTE: methods of this object must be synchronized with csi-methods.js file definitions!
12
15
  // Otherwise you may end up rewriting a method and not providing its rewritten implementation
13
16
  const TaintTrackingNoop = {
14
17
  plusOperator: noop,
15
18
  concat: noop,
19
+ random: noop,
16
20
  replace: noop,
17
21
  slice: noop,
18
22
  substr: noop,
@@ -110,7 +114,14 @@ function csiMethodsOverrides (getContext) {
110
114
  getContext,
111
115
  String.prototype.trim,
112
116
  String.prototype.trimStart
113
- )
117
+ ),
118
+
119
+ random: function (res, fn) {
120
+ if (mathRandomCallCh.hasSubscribers) {
121
+ mathRandomCallCh.publish({ fn })
122
+ }
123
+ return res
124
+ }
114
125
  }
115
126
  }
116
127
 
@@ -14,5 +14,6 @@ module.exports = {
14
14
  UNVALIDATED_REDIRECT: 'UNVALIDATED_REDIRECT',
15
15
  WEAK_CIPHER: 'WEAK_CIPHER',
16
16
  WEAK_HASH: 'WEAK_HASH',
17
+ WEAK_RANDOMNESS: 'WEAK_RANDOMNESS',
17
18
  XCONTENTTYPE_HEADER_MISSING: 'XCONTENTTYPE_HEADER_MISSING'
18
19
  }
@@ -25,7 +25,8 @@ class RemoteConfigManager extends EventEmitter {
25
25
  super()
26
26
 
27
27
  const pollInterval = Math.floor(config.remoteConfig.pollInterval * 1000)
28
- const url = config.url || new URL(format({
28
+
29
+ this.url = config.url || new URL(format({
29
30
  protocol: 'http:',
30
31
  hostname: config.hostname || 'localhost',
31
32
  port: config.port
@@ -33,12 +34,6 @@ class RemoteConfigManager extends EventEmitter {
33
34
 
34
35
  this.scheduler = new Scheduler((cb) => this.poll(cb), pollInterval)
35
36
 
36
- this.requestOptions = {
37
- url,
38
- method: 'POST',
39
- path: '/v0.7/config'
40
- }
41
-
42
37
  this.state = {
43
38
  client: {
44
39
  state: { // updated by `parseConfig()`
@@ -122,7 +117,13 @@ class RemoteConfigManager extends EventEmitter {
122
117
  }
123
118
 
124
119
  poll (cb) {
125
- request(this.getPayload(), this.requestOptions, (err, data, statusCode) => {
120
+ const options = {
121
+ url: this.url,
122
+ method: 'POST',
123
+ path: '/v0.7/config'
124
+ }
125
+
126
+ request(this.getPayload(), options, (err, data, statusCode) => {
126
127
  // 404 means RC is disabled, ignore it
127
128
  if (statusCode === 404) return cb()
128
129
 
@@ -29,9 +29,10 @@ const REQUEST_HEADERS_MAP = mapHeaderAndTags([
29
29
  'accept-encoding',
30
30
  'accept-language',
31
31
  'host',
32
- 'user-agent',
33
32
  'forwarded',
33
+ 'user-agent',
34
34
  'via',
35
+ 'x-amzn-trace-id',
35
36
 
36
37
  ...ipHeaderList,
37
38
  ...contentHeaderList
@@ -83,7 +83,8 @@ function getSkippableSuites ({
83
83
  } else {
84
84
  let skippableSuites = []
85
85
  try {
86
- skippableSuites = JSON.parse(res)
86
+ const parsedResponse = JSON.parse(res)
87
+ skippableSuites = parsedResponse
87
88
  .data
88
89
  .filter(({ type }) => type === testLevel)
89
90
  .map(({ attributes: { suite, name } }) => {
@@ -92,6 +93,7 @@ function getSkippableSuites ({
92
93
  }
93
94
  return { suite, name }
94
95
  })
96
+ const { meta: { correlation_id: correlationId } } = parsedResponse
95
97
  incrementCountMetric(
96
98
  testLevel === 'test'
97
99
  ? TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_TESTS : TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_SUITES,
@@ -100,7 +102,7 @@ function getSkippableSuites ({
100
102
  )
101
103
  distributionMetric(TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_BYTES, {}, res.length)
102
104
  log.debug(() => `Number of received skippable ${testLevel}s: ${skippableSuites.length}`)
103
- done(null, skippableSuites)
105
+ done(null, skippableSuites, correlationId)
104
106
  } catch (err) {
105
107
  done(err)
106
108
  }
@@ -795,9 +795,9 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
795
795
  tagger.add(tags, DD_TRACE_TAGS)
796
796
  tagger.add(tags, DD_TRACE_GLOBAL_TAGS)
797
797
 
798
- this._setValue(env, 'service', DD_SERVICE || DD_SERVICE_NAME || tags.service)
799
- this._setValue(env, 'env', DD_ENV || tags.env)
800
- this._setValue(env, 'version', DD_VERSION || tags.version)
798
+ this._setString(env, 'service', DD_SERVICE || DD_SERVICE_NAME || tags.service)
799
+ this._setString(env, 'env', DD_ENV || tags.env)
800
+ this._setString(env, 'version', DD_VERSION || tags.version)
801
801
  this._setUnit(env, 'sampleRate', DD_TRACE_SAMPLE_RATE)
802
802
  this._setBoolean(env, 'logInjection', DD_LOGS_INJECTION)
803
803
  this._setArray(env, 'headerTags', DD_TRACE_HEADER_TAGS)
@@ -812,9 +812,9 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
812
812
 
813
813
  tagger.add(tags, options.tags)
814
814
 
815
- this._setValue(opts, 'service', options.service || tags.service)
816
- this._setValue(opts, 'env', options.env || tags.env)
817
- this._setValue(opts, 'version', options.version || tags.version)
815
+ this._setString(opts, 'service', options.service || tags.service)
816
+ this._setString(opts, 'env', options.env || tags.env)
817
+ this._setString(opts, 'version', options.version || tags.version)
818
818
  this._setUnit(opts, 'sampleRate', coalesce(options.sampleRate, options.ingestion.sampleRate))
819
819
  this._setBoolean(opts, 'logInjection', options.logInjection)
820
820
  this._setArray(opts, 'headerTags', options.headerTags)
@@ -875,6 +875,10 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
875
875
  }
876
876
  }
877
877
 
878
+ _setString (obj, name, value) {
879
+ obj[name] = value || undefined // unset for empty strings
880
+ }
881
+
878
882
  _setTags (obj, name, value) {
879
883
  if (!value || Object.keys(value).length === 0) {
880
884
  return this._setValue(obj, name, null)
@@ -2,7 +2,8 @@
2
2
  const { truncateSpan, normalizeSpan } = require('./tags-processors')
3
3
  const { AgentEncoder } = require('./0.4')
4
4
  const { version: ddTraceVersion } = require('../../../../package.json')
5
- const id = require('../../../dd-trace/src/id')
5
+ const { ITR_CORRELATION_ID } = require('../../src/plugins/util/test')
6
+ const id = require('../../src/id')
6
7
  const {
7
8
  distributionMetric,
8
9
  TELEMETRY_ENDPOINT_PAYLOAD_SERIALIZATION_MS,
@@ -45,7 +46,13 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
45
46
  }
46
47
 
47
48
  _encodeTestSuite (bytes, content) {
48
- this._encodeMapPrefix(bytes, TEST_SUITE_KEYS_LENGTH)
49
+ let keysLength = TEST_SUITE_KEYS_LENGTH
50
+ const itrCorrelationId = content.meta[ITR_CORRELATION_ID]
51
+ if (itrCorrelationId) {
52
+ keysLength++
53
+ }
54
+
55
+ this._encodeMapPrefix(bytes, keysLength)
49
56
  this._encodeString(bytes, 'type')
50
57
  this._encodeString(bytes, content.type)
51
58
 
@@ -58,6 +65,12 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
58
65
  this._encodeString(bytes, 'test_suite_id')
59
66
  this._encodeId(bytes, content.span_id)
60
67
 
68
+ if (itrCorrelationId) {
69
+ this._encodeString(bytes, ITR_CORRELATION_ID)
70
+ this._encodeString(bytes, itrCorrelationId)
71
+ delete content.meta[ITR_CORRELATION_ID]
72
+ }
73
+
61
74
  this._encodeString(bytes, 'error')
62
75
  this._encodeNumber(bytes, content.error)
63
76
  this._encodeString(bytes, 'name')
@@ -144,6 +157,10 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
144
157
  if (content.meta.test_suite_id) {
145
158
  totalKeysLength = totalKeysLength + 1
146
159
  }
160
+ const itrCorrelationId = content.meta[ITR_CORRELATION_ID]
161
+ if (itrCorrelationId) {
162
+ totalKeysLength = totalKeysLength + 1
163
+ }
147
164
  this._encodeMapPrefix(bytes, totalKeysLength)
148
165
  if (content.type) {
149
166
  this._encodeString(bytes, 'type')
@@ -194,6 +211,12 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
194
211
  delete content.meta.test_suite_id
195
212
  }
196
213
 
214
+ if (itrCorrelationId) {
215
+ this._encodeString(bytes, ITR_CORRELATION_ID)
216
+ this._encodeString(bytes, itrCorrelationId)
217
+ delete content.meta[ITR_CORRELATION_ID]
218
+ }
219
+
197
220
  this._encodeString(bytes, 'meta')
198
221
  this._encodeMap(bytes, content.meta)
199
222
  this._encodeString(bytes, 'metrics')
@@ -15,7 +15,8 @@ const {
15
15
  TEST_MODULE,
16
16
  getTestSuiteCommonTags,
17
17
  TEST_STATUS,
18
- TEST_SKIPPED_BY_ITR
18
+ TEST_SKIPPED_BY_ITR,
19
+ ITR_CORRELATION_ID
19
20
  } = require('./util/test')
20
21
  const Plugin = require('./plugin')
21
22
  const { COMPONENT } = require('../constants')
@@ -53,11 +54,13 @@ module.exports = class CiPlugin extends Plugin {
53
54
  if (!this.tracer._exporter || !this.tracer._exporter.getSkippableSuites) {
54
55
  return onDone({ err: new Error('CI Visibility was not initialized correctly') })
55
56
  }
56
- this.tracer._exporter.getSkippableSuites(this.testConfiguration, (err, skippableSuites) => {
57
+ this.tracer._exporter.getSkippableSuites(this.testConfiguration, (err, skippableSuites, itrCorrelationId) => {
57
58
  if (err) {
58
59
  log.error(`Skippable suites could not be fetched. ${err.message}`)
60
+ } else {
61
+ this.itrCorrelationId = itrCorrelationId
59
62
  }
60
- onDone({ err, skippableSuites })
63
+ onDone({ err, skippableSuites, itrCorrelationId })
61
64
  })
62
65
  })
63
66
 
@@ -95,6 +98,9 @@ module.exports = class CiPlugin extends Plugin {
95
98
  const testCommand = this.testSessionSpan.context()._tags[TEST_COMMAND]
96
99
  skippedSuites.forEach((testSuite) => {
97
100
  const testSuiteMetadata = getTestSuiteCommonTags(testCommand, frameworkVersion, testSuite, this.constructor.id)
101
+ if (this.itrCorrelationId) {
102
+ testSuiteMetadata[ITR_CORRELATION_ID] = this.itrCorrelationId
103
+ }
98
104
 
99
105
  this.tracer.startSpan(`${this.constructor.id}.test_suite`, {
100
106
  childOf: this.testModuleSpan,
@@ -60,6 +60,7 @@ const TEST_ITR_SKIPPING_COUNT = 'test.itr.tests_skipping.count'
60
60
  const TEST_CODE_COVERAGE_ENABLED = 'test.code_coverage.enabled'
61
61
  const TEST_ITR_UNSKIPPABLE = 'test.itr.unskippable'
62
62
  const TEST_ITR_FORCED_RUN = 'test.itr.forced_run'
63
+ const ITR_CORRELATION_ID = 'itr_correlation_id'
63
64
 
64
65
  const TEST_CODE_COVERAGE_LINES_PCT = 'test.code_coverage.lines_pct'
65
66
 
@@ -111,6 +112,7 @@ module.exports = {
111
112
  TEST_CODE_COVERAGE_LINES_PCT,
112
113
  TEST_ITR_UNSKIPPABLE,
113
114
  TEST_ITR_FORCED_RUN,
115
+ ITR_CORRELATION_ID,
114
116
  addIntelligentTestRunnerSpanTags,
115
117
  getCoveredFilenamesFromCoverage,
116
118
  resetCoverage,
@@ -40,6 +40,7 @@ class Config {
40
40
  DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE,
41
41
  DD_PROFILING_EXPERIMENTAL_OOM_MAX_HEAP_EXTENSION_COUNT,
42
42
  DD_PROFILING_EXPERIMENTAL_OOM_EXPORT_STRATEGIES,
43
+ DD_PROFILING_TIMELINE_ENABLED,
43
44
  DD_PROFILING_EXPERIMENTAL_TIMELINE_ENABLED,
44
45
  DD_PROFILING_CODEHOTSPOTS_ENABLED,
45
46
  DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
@@ -96,11 +97,15 @@ class Config {
96
97
  // depending on those (code hotspots and endpoint collection) need to default
97
98
  // to false on Windows.
98
99
  const samplingContextsAvailable = process.platform !== 'win32'
99
- function checkOptionAllowed (option, description) {
100
- if (option && !samplingContextsAvailable) {
100
+ function checkOptionAllowed (option, description, condition) {
101
+ if (option && !condition) {
101
102
  throw new Error(`${description} not supported on ${process.platform}.`)
102
103
  }
103
104
  }
105
+ function checkOptionWithSamplingContextAllowed (option, description) {
106
+ checkOptionAllowed(option, description, samplingContextsAvailable)
107
+ }
108
+
104
109
  this.flushInterval = flushInterval
105
110
  this.uploadTimeout = uploadTimeout
106
111
  this.sourceMap = sourceMap
@@ -109,7 +114,7 @@ class Config {
109
114
  DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
110
115
  DD_PROFILING_EXPERIMENTAL_ENDPOINT_COLLECTION_ENABLED, samplingContextsAvailable))
111
116
  logExperimentalVarDeprecation('ENDPOINT_COLLECTION_ENABLED')
112
- checkOptionAllowed(this.endpointCollectionEnabled, 'Endpoint collection')
117
+ checkOptionWithSamplingContextAllowed(this.endpointCollectionEnabled, 'Endpoint collection')
113
118
 
114
119
  this.pprofPrefix = pprofPrefix
115
120
  this.v8ProfilerBugWorkaroundEnabled = isTrue(coalesce(options.v8ProfilerBugWorkaround,
@@ -126,8 +131,13 @@ class Config {
126
131
  new AgentExporter(this)
127
132
  ], this)
128
133
 
134
+ // OOM monitoring does not work well on Windows, so it is disabled by default.
135
+ const oomMonitoringSupported = process.platform !== 'win32'
136
+
129
137
  const oomMonitoringEnabled = isTrue(coalesce(options.oomMonitoring,
130
- DD_PROFILING_EXPERIMENTAL_OOM_MONITORING_ENABLED, true))
138
+ DD_PROFILING_EXPERIMENTAL_OOM_MONITORING_ENABLED, oomMonitoringSupported))
139
+ checkOptionAllowed(oomMonitoringEnabled, 'OOM monitoring', oomMonitoringSupported)
140
+
131
141
  const heapLimitExtensionSize = coalesce(options.oomHeapLimitExtensionSize,
132
142
  Number(DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE), 0)
133
143
  const maxHeapExtensionCount = coalesce(options.oomMaxHeapExtensionCount,
@@ -154,16 +164,20 @@ class Config {
154
164
  })
155
165
 
156
166
  this.timelineEnabled = isTrue(coalesce(options.timelineEnabled,
167
+ DD_PROFILING_TIMELINE_ENABLED,
157
168
  DD_PROFILING_EXPERIMENTAL_TIMELINE_ENABLED, false))
169
+ logExperimentalVarDeprecation('TIMELINE_ENABLED')
170
+ checkOptionWithSamplingContextAllowed(this.timelineEnabled, 'Timeline view')
158
171
 
159
172
  this.codeHotspotsEnabled = isTrue(coalesce(options.codeHotspotsEnabled,
160
173
  DD_PROFILING_CODEHOTSPOTS_ENABLED,
161
174
  DD_PROFILING_EXPERIMENTAL_CODEHOTSPOTS_ENABLED, samplingContextsAvailable))
162
175
  logExperimentalVarDeprecation('CODEHOTSPOTS_ENABLED')
163
- checkOptionAllowed(this.codeHotspotsEnabled, 'Code hotspots')
176
+ checkOptionWithSamplingContextAllowed(this.codeHotspotsEnabled, 'Code hotspots')
164
177
 
165
178
  this.cpuProfilingEnabled = isTrue(coalesce(options.cpuProfilingEnabled,
166
179
  DD_PROFILING_EXPERIMENTAL_CPU_ENABLED, false))
180
+ checkOptionWithSamplingContextAllowed(this.cpuProfilingEnabled, 'CPU profiling')
167
181
 
168
182
  this.profilers = ensureProfilers(profilers, this)
169
183
  }
@@ -140,8 +140,7 @@ function appStarted (config) {
140
140
  return app
141
141
  }
142
142
 
143
- function onBeforeExit () {
144
- process.removeListener('beforeExit', onBeforeExit)
143
+ function appClosing () {
145
144
  const { reqType, payload } = createPayload('app-closing')
146
145
  sendData(config, application, host, reqType, payload)
147
146
  // we flush before shutting down. Only in CI Visibility
@@ -150,6 +149,11 @@ function onBeforeExit () {
150
149
  }
151
150
  }
152
151
 
152
+ function onBeforeExit () {
153
+ process.removeListener('beforeExit', onBeforeExit)
154
+ appClosing()
155
+ }
156
+
153
157
  function createAppObject (config) {
154
158
  return {
155
159
  service_name: config.service,
@@ -339,5 +343,6 @@ module.exports = {
339
343
  start,
340
344
  stop,
341
345
  updateIntegrations,
342
- updateConfig
346
+ updateConfig,
347
+ appClosing
343
348
  }
@@ -1,6 +1,7 @@
1
1
 
2
2
  const request = require('../exporters/common/request')
3
3
  const log = require('../log')
4
+ const { isTrue } = require('../util')
4
5
 
5
6
  let agentTelemetry = true
6
7
 
@@ -49,13 +50,12 @@ function sendData (config, application, host, reqType, payload = {}, cb = () =>
49
50
  const {
50
51
  hostname,
51
52
  port,
52
- experimental,
53
53
  isCiVisibility
54
54
  } = config
55
55
 
56
56
  let url = config.url
57
57
 
58
- const isCiVisibilityAgentlessMode = isCiVisibility && experimental?.exporter === 'datadog'
58
+ const isCiVisibilityAgentlessMode = isCiVisibility && isTrue(process.env.DD_CIVISIBILITY_AGENTLESS_ENABLED)
59
59
 
60
60
  if (isCiVisibilityAgentlessMode) {
61
61
  try {
package/scripts/st.js ADDED
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env node
2
+ /* eslint-disable no-console, no-fallthrough */
3
+ 'use strict'
4
+
5
+ const path = require('path')
6
+ const { writeFileSync } = require('fs')
7
+ const { execSync } = require('child_process')
8
+
9
+ const ddtracePath = path.join(__dirname, '..')
10
+ const defaultTestPath = process.env.DD_ST_PATH || path.join(ddtracePath, '..', 'system-tests')
11
+
12
+ const { buildAll, npm, testDir, testArgs } = parseArgs()
13
+
14
+ const binariesPath = path.join(testDir, 'binaries')
15
+
16
+ if (npm) {
17
+ console.log('Using NPM package:', npm)
18
+
19
+ writeFileSync(path.join(binariesPath, 'nodejs-load-from-npm'), npm)
20
+ } else {
21
+ console.log('Using local repo')
22
+
23
+ const packName = execSync(`npm pack ${ddtracePath}`, {
24
+ cwd: binariesPath,
25
+ stdio: [null, null, 'inherit'],
26
+ encoding: 'utf8'
27
+ }).slice(0, -1) // remove trailing newline
28
+
29
+ writeFileSync(path.join(binariesPath, 'nodejs-load-from-npm'), `/binaries/${packName}`)
30
+ }
31
+
32
+ try {
33
+ execSync(`./build.sh ${buildAll ? '' : '-i weblog'} && ./run.sh ${testArgs}`, {
34
+ cwd: testDir,
35
+ stdio: [null, 'inherit', 'inherit']
36
+ })
37
+ } catch (err) {
38
+ process.exit(err.status || 1)
39
+ }
40
+
41
+ function parseArgs () {
42
+ const args = {
43
+ buildAll: false,
44
+ npm: null,
45
+ testDir: defaultTestPath,
46
+ testArgs: ''
47
+ }
48
+
49
+ for (let i = 2; i < process.argv.length; i++) {
50
+ switch (process.argv[i]) {
51
+ case '-b':
52
+ case '--build-all':
53
+ args.buildAll = true
54
+ break
55
+
56
+ case '-h':
57
+ case '--help':
58
+ helpAndExit()
59
+ break
60
+
61
+ case '-n':
62
+ case '--npm': {
63
+ const arg = process.argv[i + 1]
64
+ if (!arg || arg[0] === '-') {
65
+ args.npm = 'dd-trace'
66
+ } else {
67
+ args.npm = arg
68
+ i++
69
+ }
70
+ break
71
+ }
72
+
73
+ case '-t':
74
+ case '--test-dir': {
75
+ const arg = process.argv[++i]
76
+ if (!arg || arg[0] === '-') helpAndExit()
77
+ args.testDir = arg
78
+ break
79
+ }
80
+
81
+ case '--':
82
+ args.testArgs = process.argv.slice(i + 1).join(' ')
83
+ return args
84
+
85
+ default:
86
+ console.log('Unknown option:', process.argv[i], '\n')
87
+ helpAndExit()
88
+ }
89
+ }
90
+
91
+ return args
92
+ }
93
+
94
+ function helpAndExit () {
95
+ console.log('Usage: node st.js [options...] [-- test-args]')
96
+ console.log('Options:')
97
+ console.log(' -b, --build-all Rebuild all images (default: only build weblog)')
98
+ console.log(' -h, --help Print this message')
99
+ console.log(' -n, --npm [package] Build a remote package instead of the local repo (default: "dd-trace")')
100
+ console.log(' Can be a package name (e.g. "dd-trace@4.2.0") or a git URL (e.g.')
101
+ console.log(' "git+https://github.com/DataDog/dd-trace-js.git#mybranch")')
102
+ console.log(' -t, --test-dir <path> Specify the system-tests directory (default: "dd-trace/../system-tests/")')
103
+ console.log(' -- <test-args> Passed to system-tests run.sh (e.g. "-- SCENARIO_NAME tests/path_to_test.py")')
104
+ process.exit()
105
+ }