dd-trace 5.86.0 → 5.88.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 (105) hide show
  1. package/LICENSE-3rdparty.csv +60 -32
  2. package/ext/exporters.d.ts +1 -0
  3. package/ext/exporters.js +1 -0
  4. package/index.d.ts +243 -7
  5. package/package.json +9 -6
  6. package/packages/datadog-instrumentations/src/ai.js +54 -90
  7. package/packages/datadog-instrumentations/src/cucumber.js +14 -0
  8. package/packages/datadog-instrumentations/src/helpers/hook.js +17 -11
  9. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  10. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +55 -14
  11. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +15 -13
  12. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +103 -0
  13. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/bullmq.js +108 -0
  14. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -1
  15. package/packages/datadog-instrumentations/src/helpers/rewriter/transformer.js +21 -0
  16. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +138 -12
  17. package/packages/datadog-instrumentations/src/http/client.js +119 -1
  18. package/packages/datadog-instrumentations/src/jest.js +179 -15
  19. package/packages/datadog-instrumentations/src/kafkajs.js +20 -17
  20. package/packages/datadog-instrumentations/src/mocha/utils.js +6 -0
  21. package/packages/datadog-instrumentations/src/mysql2.js +131 -64
  22. package/packages/datadog-instrumentations/src/playwright.js +9 -1
  23. package/packages/datadog-instrumentations/src/stripe.js +92 -0
  24. package/packages/datadog-instrumentations/src/vitest.js +11 -0
  25. package/packages/datadog-plugin-amqplib/src/consumer.js +14 -10
  26. package/packages/datadog-plugin-amqplib/src/producer.js +23 -19
  27. package/packages/datadog-plugin-azure-functions/src/index.js +53 -37
  28. package/packages/datadog-plugin-bullmq/src/consumer.js +33 -11
  29. package/packages/datadog-plugin-bullmq/src/producer.js +60 -31
  30. package/packages/datadog-plugin-cucumber/src/index.js +9 -6
  31. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +33 -0
  32. package/packages/datadog-plugin-cypress/src/support.js +48 -8
  33. package/packages/datadog-plugin-jest/src/index.js +12 -2
  34. package/packages/datadog-plugin-jest/src/util.js +2 -1
  35. package/packages/datadog-plugin-kafkajs/src/consumer.js +22 -12
  36. package/packages/datadog-plugin-kafkajs/src/producer.js +33 -22
  37. package/packages/datadog-plugin-mocha/src/index.js +9 -6
  38. package/packages/datadog-plugin-playwright/src/index.js +10 -6
  39. package/packages/datadog-plugin-vitest/src/index.js +13 -8
  40. package/packages/dd-trace/src/appsec/addresses.js +11 -0
  41. package/packages/dd-trace/src/appsec/channels.js +5 -1
  42. package/packages/dd-trace/src/appsec/downstream_requests.js +302 -0
  43. package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +1 -1
  44. package/packages/dd-trace/src/appsec/iast/analyzers/ssrf-analyzer.js +1 -1
  45. package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +1 -1
  46. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +4 -5
  47. package/packages/dd-trace/src/appsec/iast/path-line.js +36 -25
  48. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +1 -1
  49. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +3 -4
  50. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +3 -2
  51. package/packages/dd-trace/src/appsec/index.js +103 -0
  52. package/packages/dd-trace/src/appsec/rasp/ssrf.js +66 -4
  53. package/packages/dd-trace/src/azure_metadata.js +0 -2
  54. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +14 -1
  55. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -1
  56. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +2 -0
  57. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +1 -1
  58. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -1
  59. package/packages/dd-trace/src/ci-visibility/requests/request.js +236 -0
  60. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +1 -1
  61. package/packages/dd-trace/src/config/defaults.js +148 -195
  62. package/packages/dd-trace/src/config/helper.js +43 -1
  63. package/packages/dd-trace/src/config/index.js +42 -14
  64. package/packages/dd-trace/src/config/supported-configurations.json +4115 -510
  65. package/packages/dd-trace/src/constants.js +0 -2
  66. package/packages/dd-trace/src/crashtracking/crashtracker.js +10 -3
  67. package/packages/dd-trace/src/datastreams/pathway.js +22 -3
  68. package/packages/dd-trace/src/datastreams/processor.js +14 -1
  69. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +47 -2
  70. package/packages/dd-trace/src/debugger/devtools_client/index.js +75 -23
  71. package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +23 -1
  72. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +3 -3
  73. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +168 -36
  74. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +18 -0
  75. package/packages/dd-trace/src/encode/agentless-json.js +141 -0
  76. package/packages/dd-trace/src/exporter.js +2 -0
  77. package/packages/dd-trace/src/exporters/agent/writer.js +22 -8
  78. package/packages/dd-trace/src/exporters/agentless/index.js +89 -0
  79. package/packages/dd-trace/src/exporters/agentless/writer.js +184 -0
  80. package/packages/dd-trace/src/exporters/common/agents.js +1 -1
  81. package/packages/dd-trace/src/exporters/common/request.js +4 -4
  82. package/packages/dd-trace/src/llmobs/constants/writers.js +1 -1
  83. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +5 -3
  84. package/packages/dd-trace/src/llmobs/sdk.js +34 -5
  85. package/packages/dd-trace/src/opentelemetry/context_manager.js +19 -46
  86. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +3 -4
  87. package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -5
  88. package/packages/dd-trace/src/opentracing/span.js +6 -4
  89. package/packages/dd-trace/src/plugins/ci_plugin.js +57 -5
  90. package/packages/dd-trace/src/plugins/database.js +57 -45
  91. package/packages/dd-trace/src/plugins/outbound.js +27 -2
  92. package/packages/dd-trace/src/plugins/tracing.js +39 -4
  93. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +7 -0
  94. package/packages/dd-trace/src/plugins/util/test.js +48 -0
  95. package/packages/dd-trace/src/plugins/util/web.js +8 -7
  96. package/packages/dd-trace/src/profiling/exporter_cli.js +1 -0
  97. package/packages/dd-trace/src/propagation-hash/index.js +145 -0
  98. package/packages/dd-trace/src/proxy.js +4 -0
  99. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +1 -1
  100. package/packages/dd-trace/src/startup-log.js +3 -3
  101. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/bullmq.json +0 -106
  102. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +0 -741
  103. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +0 -11
  104. package/packages/dd-trace/src/plugins/util/serverless.js +0 -8
  105. package/packages/dd-trace/src/scope/noop/scope.js +0 -21
@@ -8,29 +8,40 @@ const { getOriginalPathAndLineFromSourceMap } = require('./taint-tracking/rewrit
8
8
  const pathLine = {
9
9
  getNodeModulesPaths,
10
10
  getRelativePath,
11
- getNonDDCallSiteFrames,
11
+ getCallSiteFramesForLocation,
12
12
  ddBasePath, // Exported only for test purposes
13
13
  }
14
14
 
15
15
  const EXCLUDED_PATHS = [
16
16
  path.join(path.sep, 'node_modules', 'dc-polyfill'),
17
17
  ]
18
- const EXCLUDED_PATH_PREFIXES = [
19
- 'node:diagnostics_channel',
20
- 'diagnostics_channel',
21
- 'node:child_process',
22
- 'child_process',
23
- 'node:async_hooks',
24
- 'async_hooks',
25
- 'node:internal/async_local_storage',
26
- ]
27
18
 
28
- function getNonDDCallSiteFrames (callSiteFrames, externallyExcludedPaths) {
19
+ /**
20
+ * Processes and filters call site frames to find the best location for a vulnerability.
21
+ * Returns client frames if available, otherwise falls back to all processed frames.
22
+ * Excludes dd-trace frames and all Node.js built-in/internal modules (node:*).
23
+ * @param {CallSiteFrame[]} callSiteFrames
24
+ * @param {string[]} externallyExcludedPaths
25
+ * @returns {CallSiteFrame[]} Client frames if available, otherwise all processed frames
26
+ *
27
+ * @typedef {object} CallSiteFrame
28
+ * @property {number} id
29
+ * @property {string} file - Original file path
30
+ * @property {number} line
31
+ * @property {number} column
32
+ * @property {string} function
33
+ * @property {string} class_name
34
+ * @property {boolean} isNative
35
+ * @property {string} [path] - Relative path, added during processing
36
+ * @property {boolean} [isInternal] - Whether the frame is internal, added during processing
37
+ */
38
+ function getCallSiteFramesForLocation (callSiteFrames, externallyExcludedPaths) {
29
39
  if (!callSiteFrames) {
30
40
  return []
31
41
  }
32
42
 
33
- const result = []
43
+ const allFrames = []
44
+ const clientFrames = []
34
45
 
35
46
  for (const callsite of callSiteFrames) {
36
47
  let filepath = callsite.file?.startsWith('file://') ? fileURLToPath(callsite.file) : callsite.file
@@ -47,18 +58,22 @@ function getNonDDCallSiteFrames (callSiteFrames, externallyExcludedPaths) {
47
58
  callsite.column = column
48
59
  }
49
60
 
50
- if (
51
- !isExcluded(callsite, externallyExcludedPaths) &&
52
- (!filepath.includes(pathLine.ddBasePath) || globalThis.__DD_ESBUILD_IAST_WITH_NO_SM)
53
- ) {
61
+ if (filepath) {
54
62
  callsite.path = getRelativePath(filepath)
55
63
  callsite.isInternal = !path.isAbsolute(filepath)
56
64
 
57
- result.push(callsite)
65
+ allFrames.push(callsite)
66
+
67
+ if (
68
+ !isExcluded(callsite, externallyExcludedPaths) &&
69
+ (!filepath.includes(pathLine.ddBasePath) || globalThis.__DD_ESBUILD_IAST_WITH_NO_SM)
70
+ ) {
71
+ clientFrames.push(callsite)
72
+ }
58
73
  }
59
74
  }
60
75
 
61
- return result
76
+ return clientFrames.length > 0 ? clientFrames : allFrames
62
77
  }
63
78
 
64
79
  function getRelativePath (filepath) {
@@ -67,10 +82,12 @@ function getRelativePath (filepath) {
67
82
 
68
83
  function isExcluded (callsite, externallyExcludedPaths) {
69
84
  if (callsite.isNative) return true
85
+
70
86
  const filename = globalThis.__DD_ESBUILD_IAST_WITH_SM ? callsite.path : callsite.file
71
- if (!filename) {
87
+ if (!filename || filename.startsWith('node:')) {
72
88
  return true
73
89
  }
90
+
74
91
  let excludedPaths = EXCLUDED_PATHS
75
92
  if (externallyExcludedPaths) {
76
93
  excludedPaths = [...excludedPaths, ...externallyExcludedPaths]
@@ -82,12 +99,6 @@ function isExcluded (callsite, externallyExcludedPaths) {
82
99
  }
83
100
  }
84
101
 
85
- for (const EXCLUDED_PATH_PREFIX of EXCLUDED_PATH_PREFIXES) {
86
- if (filename.indexOf(EXCLUDED_PATH_PREFIX) === 0) {
87
- return true
88
- }
89
- }
90
-
91
102
  return false
92
103
  }
93
104
 
@@ -10,7 +10,7 @@ module.exports = function extractSensitiveRanges (evidence) {
10
10
  pattern.lastIndex = 0
11
11
 
12
12
  const regexResult = pattern.exec(evidence.value)
13
- if (regexResult && regexResult.length > 1) {
13
+ if (regexResult?.length > 1) {
14
14
  const start = regexResult.index + (regexResult[0].length - regexResult[1].length)
15
15
  const end = start + regexResult[1].length
16
16
  return [{ start, end }]
@@ -3,6 +3,7 @@
3
3
 
4
4
  const log = require('../../../../log')
5
5
  const vulnerabilities = require('../../vulnerabilities')
6
+ const defaults = require('../../../../config/defaults')
6
7
 
7
8
  const { contains, intersects, remove } = require('./range-utils')
8
9
 
@@ -14,14 +15,12 @@ const sqlSensitiveAnalyzer = require('./sensitive-analyzers/sql-sensitive-analyz
14
15
  const taintedRangeBasedSensitiveAnalyzer = require('./sensitive-analyzers/tainted-range-based-sensitive-analyzer')
15
16
  const urlSensitiveAnalyzer = require('./sensitive-analyzers/url-sensitive-analyzer')
16
17
 
17
- const { DEFAULT_IAST_REDACTION_NAME_PATTERN, DEFAULT_IAST_REDACTION_VALUE_PATTERN } = require('./sensitive-regex')
18
-
19
18
  const REDACTED_SOURCE_BUFFER = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
20
19
 
21
20
  class SensitiveHandler {
22
21
  constructor () {
23
- this._namePattern = new RegExp(DEFAULT_IAST_REDACTION_NAME_PATTERN, 'gmi')
24
- this._valuePattern = new RegExp(DEFAULT_IAST_REDACTION_VALUE_PATTERN, 'gmi')
22
+ this._namePattern = new RegExp(/** @type {string} */ (defaults['iast.redactionNamePattern']), 'gmi')
23
+ this._valuePattern = new RegExp(/** @type {string} */ (defaults['iast.redactionValuePattern']), 'gmi')
25
24
 
26
25
  this._sensitiveAnalyzers = new Map()
27
26
  this._sensitiveAnalyzers.set(vulnerabilities.CODE_INJECTION, taintedRangeBasedSensitiveAnalyzer)
@@ -1,7 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  const crypto = require('crypto')
4
- const { DEFAULT_IAST_REDACTION_VALUE_PATTERN } = require('./evidence-redaction/sensitive-regex')
4
+
5
+ const defaults = require('../../../config/defaults')
5
6
 
6
7
  const STRINGIFY_RANGE_KEY = 'DD_' + crypto.randomBytes(20).toString('hex')
7
8
  const STRINGIFY_SENSITIVE_KEY = STRINGIFY_RANGE_KEY + 'SENSITIVE'
@@ -11,7 +12,7 @@ const STRINGIFY_SENSITIVE_NOT_STRING_KEY = STRINGIFY_SENSITIVE_KEY + 'NOTSTRING'
11
12
  const KEYS_REGEX_WITH_SENSITIVE_RANGES = new RegExp(String.raw`(?:"(${STRINGIFY_RANGE_KEY}_\d+_))|(?:"(${STRINGIFY_SENSITIVE_KEY}_\d+_(\d+)_))|("${STRINGIFY_SENSITIVE_NOT_STRING_KEY}_\d+_([\s0-9.a-zA-Z]*)")`, 'gm')
12
13
  const KEYS_REGEX_WITHOUT_SENSITIVE_RANGES = new RegExp(String.raw`"(${STRINGIFY_RANGE_KEY}_\d+_)`, 'gm')
13
14
 
14
- const sensitiveValueRegex = new RegExp(DEFAULT_IAST_REDACTION_VALUE_PATTERN, 'gmi')
15
+ const sensitiveValueRegex = new RegExp(/** @type {string} */ (defaults['iast.redactionValuePattern']), 'gmi')
15
16
 
16
17
  function iterateObject (target, fn, levelKeys = [], depth = 10, visited = new Set()) {
17
18
  for (const key of Object.keys(target)) {
@@ -30,6 +30,9 @@ const {
30
30
  routerParam,
31
31
  fastifyResponseChannel,
32
32
  fastifyPathParams,
33
+ stripeCheckoutSessionCreate,
34
+ stripePaymentIntentCreate,
35
+ stripeConstructEvent,
33
36
  } = require('./channels')
34
37
  const waf = require('./waf')
35
38
  const addresses = require('./addresses')
@@ -92,6 +95,9 @@ function enable (_config) {
92
95
  fastifyResponseChannel.subscribe(onResponseBody)
93
96
  responseWriteHead.subscribe(onResponseWriteHead)
94
97
  responseSetHeader.subscribe(onResponseSetHeader)
98
+ stripeCheckoutSessionCreate.subscribe(onStripeCheckoutSessionCreate)
99
+ stripePaymentIntentCreate.subscribe(onStripePaymentIntentCreate)
100
+ stripeConstructEvent.subscribe(onStripeConstructEvent)
95
101
 
96
102
  isEnabled = true
97
103
  config = _config
@@ -382,6 +388,100 @@ function onResponseSetHeader ({ res, abortController }) {
382
388
  }
383
389
  }
384
390
 
391
+ function onStripeCheckoutSessionCreate (payload) {
392
+ if (payload?.mode !== 'payment') return
393
+
394
+ waf.run({
395
+ persistent: {
396
+ [addresses.PAYMENT_CREATION]: {
397
+ integration: 'stripe',
398
+ id: payload.id,
399
+ amount_total: payload.amount_total,
400
+ client_reference_id: payload.client_reference_id,
401
+ currency: payload.currency,
402
+ 'discounts.coupon': payload.discounts?.[0]?.coupon,
403
+ 'discounts.promotion_code': payload.discounts?.[0]?.promotion_code,
404
+ livemode: payload.livemode,
405
+ 'total_details.amount_discount': payload.total_details?.amount_discount,
406
+ 'total_details.amount_shipping': payload.total_details?.amount_shipping,
407
+ },
408
+ },
409
+ })
410
+ }
411
+
412
+ function onStripePaymentIntentCreate (payload) {
413
+ if (payload === null || typeof payload !== 'object') return
414
+
415
+ waf.run({
416
+ persistent: {
417
+ [addresses.PAYMENT_CREATION]: {
418
+ integration: 'stripe',
419
+ id: payload.id,
420
+ amount: payload.amount,
421
+ currency: payload.currency,
422
+ livemode: payload.livemode,
423
+ payment_method: payload.payment_method,
424
+ },
425
+ },
426
+ })
427
+ }
428
+
429
+ function onStripeConstructEvent (payload) {
430
+ const object = payload?.data?.object
431
+ if (object === null || typeof object !== 'object') return
432
+
433
+ let persistent
434
+
435
+ switch (payload.type) {
436
+ case 'payment_intent.succeeded':
437
+ persistent = {
438
+ [addresses.PAYMENT_SUCCESS]: {
439
+ integration: 'stripe',
440
+ id: object.id,
441
+ amount: object.amount,
442
+ currency: object.currency,
443
+ livemode: object.livemode,
444
+ payment_method: object.payment_method,
445
+ },
446
+ }
447
+ break
448
+
449
+ case 'payment_intent.payment_failed':
450
+ persistent = {
451
+ [addresses.PAYMENT_FAILURE]: {
452
+ integration: 'stripe',
453
+ id: object.id,
454
+ amount: object.amount,
455
+ currency: object.currency,
456
+ 'last_payment_error.code': object.last_payment_error?.code,
457
+ 'last_payment_error.decline_code': object.last_payment_error?.decline_code,
458
+ 'last_payment_error.payment_method.id': object.last_payment_error?.payment_method?.id,
459
+ 'last_payment_error.payment_method.type': object.last_payment_error?.payment_method?.type,
460
+ livemode: object.livemode,
461
+ },
462
+ }
463
+ break
464
+
465
+ case 'payment_intent.canceled':
466
+ persistent = {
467
+ [addresses.PAYMENT_CANCELLATION]: {
468
+ integration: 'stripe',
469
+ id: object.id,
470
+ amount: object.amount,
471
+ cancellation_reason: object.cancellation_reason,
472
+ currency: object.currency,
473
+ livemode: object.livemode,
474
+ },
475
+ }
476
+ break
477
+
478
+ default:
479
+ return
480
+ }
481
+
482
+ waf.run({ persistent })
483
+ }
484
+
385
485
  function handleResults (actions, req, res, rootSpan, abortController) {
386
486
  if (!actions || !req || !res || !rootSpan || !abortController) return
387
487
 
@@ -427,6 +527,9 @@ function disable () {
427
527
  if (fastifyResponseChannel.hasSubscribers) fastifyResponseChannel.unsubscribe(onResponseBody)
428
528
  if (responseWriteHead.hasSubscribers) responseWriteHead.unsubscribe(onResponseWriteHead)
429
529
  if (responseSetHeader.hasSubscribers) responseSetHeader.unsubscribe(onResponseSetHeader)
530
+ if (stripeCheckoutSessionCreate.hasSubscribers) stripeCheckoutSessionCreate.unsubscribe(onStripeCheckoutSessionCreate)
531
+ if (stripePaymentIntentCreate.hasSubscribers) stripePaymentIntentCreate.unsubscribe(onStripePaymentIntentCreate)
532
+ if (stripeConstructEvent.hasSubscribers) stripeConstructEvent.unsubscribe(onStripeConstructEvent)
430
533
  }
431
534
 
432
535
  // this is faster than Object.keys().length === 0
@@ -1,21 +1,32 @@
1
1
  'use strict'
2
2
 
3
3
  const { format } = require('url')
4
- const { httpClientRequestStart } = require('../channels')
4
+ const {
5
+ httpClientRequestStart,
6
+ httpClientResponseFinish,
7
+ } = require('../channels')
5
8
  const { storage } = require('../../../../datadog-core')
6
9
  const addresses = require('../addresses')
7
10
  const waf = require('../waf')
11
+ const downstream = require('../downstream_requests')
12
+ const { updateRaspRuleMatchMetricTags } = require('../telemetry')
8
13
  const { RULE_TYPES, handleResult } = require('./utils')
9
14
 
10
15
  let config
11
16
 
12
17
  function enable (_config) {
13
18
  config = _config
19
+ downstream.enable(_config)
20
+
14
21
  httpClientRequestStart.subscribe(analyzeSsrf)
22
+ httpClientResponseFinish.subscribe(handleResponseFinish)
15
23
  }
16
24
 
17
25
  function disable () {
26
+ downstream.disable()
27
+
18
28
  if (httpClientRequestStart.hasSubscribers) httpClientRequestStart.unsubscribe(analyzeSsrf)
29
+ if (httpClientResponseFinish.hasSubscribers) httpClientResponseFinish.unsubscribe(handleResponseFinish)
19
30
  }
20
31
 
21
32
  function analyzeSsrf (ctx) {
@@ -25,16 +36,67 @@ function analyzeSsrf (ctx) {
25
36
 
26
37
  if (!req || !outgoingUrl) return
27
38
 
39
+ // Determine if we should collect the response body based on sampling rate and redirect URL
40
+ ctx.shouldCollectBody = downstream.shouldSampleBody(req, outgoingUrl)
41
+
42
+ const requestAddresses = downstream.extractRequestData(ctx)
43
+
28
44
  const ephemeral = {
29
45
  [addresses.HTTP_OUTGOING_URL]: outgoingUrl,
46
+ ...requestAddresses,
30
47
  }
31
48
 
32
- const raspRule = { type: RULE_TYPES.SSRF }
49
+ const raspRule = { type: RULE_TYPES.SSRF, variant: 'request' }
33
50
 
34
51
  const result = waf.run({ ephemeral }, req, raspRule)
35
52
 
36
- const res = store?.res
37
- handleResult(result, req, res, ctx.abortController, config, raspRule)
53
+ handleResult(result, req, store?.res, ctx.abortController, config, raspRule)
54
+
55
+ downstream.incrementDownstreamAnalysisCount(req)
56
+ }
57
+
58
+ /**
59
+ * Finalizes body collection for the response and triggers RASP analysis.
60
+ * @param {{
61
+ * ctx: object,
62
+ * res: import('http').IncomingMessage,
63
+ * body: string|Buffer|null
64
+ * }} payload event payload from the channel.
65
+ */
66
+ function handleResponseFinish ({ ctx, res, body }) {
67
+ // downstream response object
68
+ if (!res) return
69
+
70
+ const store = storage('legacy').getStore()
71
+ const originatingRequest = store?.req
72
+ if (!originatingRequest) return
73
+
74
+ // Skip body analysis for redirect responses
75
+ const evaluateBody = ctx.shouldCollectBody && !downstream.handleRedirectResponse(originatingRequest, res)
76
+ const responseBody = evaluateBody ? body : null
77
+ runResponseEvaluation(res, originatingRequest, responseBody)
78
+ }
79
+
80
+ /**
81
+ * Evaluates the downstream response and records telemetry.
82
+ * @param {import('http').IncomingMessage} res incoming response from downstream service.
83
+ * @param {import('http').IncomingMessage} req originating request.
84
+ * @param {string|Buffer|null} responseBody collected downstream response body.
85
+ */
86
+ function runResponseEvaluation (res, req, responseBody) {
87
+ const responseAddresses = downstream.extractResponseData(res, responseBody)
88
+
89
+ if (!Object.keys(responseAddresses).length) return
90
+
91
+ const raspRule = { type: RULE_TYPES.SSRF, variant: 'response' }
92
+ const result = waf.run({ ephemeral: responseAddresses }, req, raspRule)
93
+
94
+ // TODO: this should be done in the waf functions directly instead of calling it everywhere
95
+ const ruleTriggered = !!result?.events?.length
96
+
97
+ if (ruleTriggered) {
98
+ updateRaspRuleMatchMetricTags(req, raspRule, false, false)
99
+ }
38
100
  }
39
101
 
40
102
  module.exports = { enable, disable }
@@ -58,7 +58,6 @@ function buildMetadata () {
58
58
  WEBSITE_SITE_NAME,
59
59
  } = getEnvironmentVariables()
60
60
 
61
- const DD_AAS_DOTNET_EXTENSION_VERSION = getValueFromEnvSources('DD_AAS_DOTNET_EXTENSION_VERSION')
62
61
  const DD_AZURE_RESOURCE_GROUP = getValueFromEnvSources('DD_AZURE_RESOURCE_GROUP')
63
62
 
64
63
  const subscriptionID = extractSubscriptionID(WEBSITE_OWNER_NAME)
@@ -77,7 +76,6 @@ function buildMetadata () {
77
76
  : (WEBSITE_RESOURCE_GROUP ?? extractResourceGroup(WEBSITE_OWNER_NAME))
78
77
 
79
78
  return trimObject({
80
- extensionVersion: DD_AAS_DOTNET_EXTENSION_VERSION,
81
79
  functionRuntimeVersion: FUNCTIONS_EXTENSION_VERSION,
82
80
  instanceID: WEBSITE_INSTANCE_ID,
83
81
  instanceName: COMPUTERNAME,
@@ -16,6 +16,12 @@ const session = require('../../../debugger/devtools_client/session')
16
16
  const { getGeneratedPosition } = require('../../../debugger/devtools_client/source-maps')
17
17
  // TODO: move debugger/devtools_client/snapshot to common place
18
18
  const { getLocalStateForCallFrame } = require('../../../debugger/devtools_client/snapshot')
19
+ const {
20
+ DEFAULT_MAX_REFERENCE_DEPTH,
21
+ DEFAULT_MAX_COLLECTION_SIZE,
22
+ DEFAULT_MAX_FIELD_COUNT,
23
+ DEFAULT_MAX_LENGTH,
24
+ } = require('../../../debugger/devtools_client/snapshot/constants')
19
25
  // TODO: move debugger/devtools_client/state to common place
20
26
  const {
21
27
  findScriptFromPartialPath,
@@ -29,6 +35,13 @@ let sessionStarted = false
29
35
  const breakpointIdToProbe = new Map()
30
36
  const probeIdToBreakpointId = new Map()
31
37
 
38
+ const limits = {
39
+ maxReferenceDepth: DEFAULT_MAX_REFERENCE_DEPTH,
40
+ maxCollectionSize: DEFAULT_MAX_COLLECTION_SIZE,
41
+ maxFieldCount: DEFAULT_MAX_FIELD_COUNT,
42
+ maxLength: DEFAULT_MAX_LENGTH,
43
+ }
44
+
32
45
  session.on('Debugger.paused', async ({ params: { hitBreakpoints: [hitBreakpoint], callFrames } }) => {
33
46
  const probe = breakpointIdToProbe.get(hitBreakpoint)
34
47
  if (!probe) {
@@ -38,7 +51,7 @@ session.on('Debugger.paused', async ({ params: { hitBreakpoints: [hitBreakpoint]
38
51
 
39
52
  const stack = await getStackFromCallFrames(callFrames)
40
53
 
41
- const { processLocalState } = await getLocalStateForCallFrame(callFrames[0])
54
+ const { processLocalState } = await getLocalStateForCallFrame(callFrames[0], limits)
42
55
 
43
56
  await session.post('Debugger.resume')
44
57
 
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const request = require('../../exporters/common/request')
3
+ const request = require('../requests/request')
4
4
  const id = require('../../id')
5
5
  const log = require('../../log')
6
6
  const { getValueFromEnvSources } = require('../../config/helper')
@@ -206,6 +206,7 @@ class CiVisibilityExporter extends BufferingExporter {
206
206
  requireGit,
207
207
  isEarlyFlakeDetectionEnabled,
208
208
  earlyFlakeDetectionNumRetries,
209
+ earlyFlakeDetectionSlowTestRetries,
209
210
  earlyFlakeDetectionFaultyThreshold,
210
211
  isFlakyTestRetriesEnabled,
211
212
  isDiEnabled,
@@ -222,6 +223,7 @@ class CiVisibilityExporter extends BufferingExporter {
222
223
  requireGit,
223
224
  isEarlyFlakeDetectionEnabled: isEarlyFlakeDetectionEnabled && this._config.isEarlyFlakeDetectionEnabled,
224
225
  earlyFlakeDetectionNumRetries,
226
+ earlyFlakeDetectionSlowTestRetries,
225
227
  earlyFlakeDetectionFaultyThreshold,
226
228
  isFlakyTestRetriesEnabled: isFlakyTestRetriesEnabled && this._config.isFlakyTestRetriesEnabled,
227
229
  flakyTestRetriesCount: this._config.flakyTestRetriesCount,
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const request = require('../../exporters/common/request')
3
+ const request = require('../requests/request')
4
4
  const log = require('../../log')
5
5
  const { getValueFromEnvSources } = require('../../config/helper')
6
6
  const {
@@ -1,6 +1,5 @@
1
1
  'use strict'
2
2
 
3
- const request = require('../../exporters/common/request')
4
3
  const id = require('../../id')
5
4
  const log = require('../../log')
6
5
  const { getValueFromEnvSources } = require('../../config/helper')
@@ -13,8 +12,10 @@ const {
13
12
  TELEMETRY_GIT_REQUESTS_SETTINGS_RESPONSE,
14
13
  } = require('../telemetry')
15
14
  const { writeSettingsToCache } = require('../test-optimization-cache')
15
+ const request = require('./request')
16
16
 
17
17
  const DEFAULT_EARLY_FLAKE_DETECTION_NUM_RETRIES = 2
18
+ const DEFAULT_EARLY_FLAKE_DETECTION_SLOW_TEST_RETRIES = { '5s': 10, '10s': 5, '30s': 3, '5m': 2 }
18
19
  const DEFAULT_EARLY_FLAKE_DETECTION_ERROR_THRESHOLD = 30
19
20
 
20
21
  function getLibraryConfiguration ({
@@ -115,6 +116,8 @@ function getLibraryConfiguration ({
115
116
  isEarlyFlakeDetectionEnabled: isKnownTestsEnabled && (earlyFlakeDetectionConfig?.enabled ?? false),
116
117
  earlyFlakeDetectionNumRetries:
117
118
  earlyFlakeDetectionConfig?.slow_test_retries?.['5s'] || DEFAULT_EARLY_FLAKE_DETECTION_NUM_RETRIES,
119
+ earlyFlakeDetectionSlowTestRetries:
120
+ earlyFlakeDetectionConfig?.slow_test_retries ?? DEFAULT_EARLY_FLAKE_DETECTION_SLOW_TEST_RETRIES,
118
121
  earlyFlakeDetectionFaultyThreshold:
119
122
  earlyFlakeDetectionConfig?.faulty_session_threshold ?? DEFAULT_EARLY_FLAKE_DETECTION_ERROR_THRESHOLD,
120
123
  isFlakyTestRetriesEnabled,