dd-trace 5.35.0 → 5.37.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 (125) hide show
  1. package/LICENSE-3rdparty.csv +2 -1
  2. package/index.d.ts +8 -7
  3. package/loader-hook.mjs +0 -4
  4. package/package.json +15 -14
  5. package/packages/datadog-core/index.js +1 -1
  6. package/packages/datadog-core/src/storage.js +76 -31
  7. package/packages/datadog-instrumentations/src/cucumber.js +54 -1
  8. package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -2
  9. package/packages/datadog-instrumentations/src/helpers/register.js +2 -2
  10. package/packages/datadog-instrumentations/src/jest.js +105 -11
  11. package/packages/datadog-instrumentations/src/mocha/main.js +46 -4
  12. package/packages/datadog-instrumentations/src/mocha/utils.js +35 -2
  13. package/packages/datadog-instrumentations/src/mocha/worker.js +7 -0
  14. package/packages/datadog-instrumentations/src/mysql2.js +3 -3
  15. package/packages/datadog-instrumentations/src/openai.js +8 -0
  16. package/packages/datadog-instrumentations/src/playwright.js +70 -22
  17. package/packages/datadog-instrumentations/src/vitest.js +60 -6
  18. package/packages/datadog-plugin-aerospike/src/index.js +1 -1
  19. package/packages/datadog-plugin-apollo/src/gateway/fetch.js +1 -1
  20. package/packages/datadog-plugin-apollo/src/gateway/index.js +1 -1
  21. package/packages/datadog-plugin-apollo/src/gateway/request.js +1 -1
  22. package/packages/datadog-plugin-aws-sdk/src/base.js +3 -3
  23. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +4 -4
  24. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -2
  25. package/packages/datadog-plugin-azure-functions/src/index.js +1 -1
  26. package/packages/datadog-plugin-couchbase/src/index.js +2 -2
  27. package/packages/datadog-plugin-cucumber/src/index.js +31 -14
  28. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +72 -7
  29. package/packages/datadog-plugin-cypress/src/support.js +36 -29
  30. package/packages/datadog-plugin-dd-trace-api/src/index.js +1 -3
  31. package/packages/datadog-plugin-graphql/src/utils.js +8 -1
  32. package/packages/datadog-plugin-grpc/src/client.js +1 -1
  33. package/packages/datadog-plugin-grpc/src/server.js +1 -1
  34. package/packages/datadog-plugin-hapi/src/index.js +1 -1
  35. package/packages/datadog-plugin-http/src/client.js +1 -1
  36. package/packages/datadog-plugin-http/src/server.js +1 -1
  37. package/packages/datadog-plugin-http2/src/client.js +3 -3
  38. package/packages/datadog-plugin-http2/src/server.js +1 -1
  39. package/packages/datadog-plugin-jest/src/index.js +17 -12
  40. package/packages/datadog-plugin-langchain/src/tracing.js +1 -1
  41. package/packages/datadog-plugin-mariadb/src/index.js +3 -3
  42. package/packages/datadog-plugin-mocha/src/index.js +35 -16
  43. package/packages/datadog-plugin-mongodb-core/src/index.js +28 -2
  44. package/packages/datadog-plugin-next/src/index.js +4 -4
  45. package/packages/datadog-plugin-openai/src/tracing.js +2 -3
  46. package/packages/datadog-plugin-playwright/src/index.js +35 -9
  47. package/packages/datadog-plugin-rhea/src/consumer.js +1 -1
  48. package/packages/datadog-plugin-router/src/index.js +2 -2
  49. package/packages/datadog-plugin-selenium/src/index.js +1 -1
  50. package/packages/datadog-plugin-vitest/src/index.js +36 -12
  51. package/packages/dd-trace/src/appsec/graphql.js +6 -6
  52. package/packages/dd-trace/src/appsec/iast/analyzers/code-injection-analyzer.js +3 -7
  53. package/packages/dd-trace/src/appsec/iast/analyzers/injection-analyzer.js +15 -1
  54. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +17 -30
  55. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +2 -2
  56. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +7 -11
  57. package/packages/dd-trace/src/appsec/iast/analyzers/stored-injection-analyzer.js +11 -0
  58. package/packages/dd-trace/src/appsec/iast/analyzers/template-injection-analyzer.js +2 -6
  59. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +24 -4
  60. package/packages/dd-trace/src/appsec/iast/context/context-plugin.js +2 -2
  61. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +2 -2
  62. package/packages/dd-trace/src/appsec/iast/index.js +4 -2
  63. package/packages/dd-trace/src/appsec/iast/security-controls/index.js +187 -0
  64. package/packages/dd-trace/src/appsec/iast/security-controls/parser.js +96 -0
  65. package/packages/dd-trace/src/appsec/iast/taint-tracking/constants.js +6 -0
  66. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +2 -2
  67. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +8 -8
  68. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +1 -1
  69. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +65 -0
  70. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-telemetry.js +14 -5
  71. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +80 -2
  72. package/packages/dd-trace/src/appsec/iast/taint-tracking/secure-marks-generator.js +1 -1
  73. package/packages/dd-trace/src/appsec/iast/taint-tracking/secure-marks.js +28 -0
  74. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +1 -1
  75. package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +5 -0
  76. package/packages/dd-trace/src/appsec/iast/utils.js +24 -0
  77. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +8 -13
  78. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -0
  79. package/packages/dd-trace/src/appsec/index.js +4 -4
  80. package/packages/dd-trace/src/appsec/rasp/command_injection.js +5 -5
  81. package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +5 -5
  82. package/packages/dd-trace/src/appsec/rasp/lfi.js +3 -3
  83. package/packages/dd-trace/src/appsec/rasp/sql_injection.js +4 -4
  84. package/packages/dd-trace/src/appsec/rasp/ssrf.js +3 -3
  85. package/packages/dd-trace/src/appsec/reporter.js +3 -3
  86. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  87. package/packages/dd-trace/src/appsec/waf/index.js +1 -1
  88. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +2 -0
  89. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +31 -56
  90. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +20 -2
  91. package/packages/dd-trace/src/ci-visibility/quarantined-tests/get-quarantined-tests.js +62 -0
  92. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +5 -2
  93. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +3 -3
  94. package/packages/dd-trace/src/config.js +18 -3
  95. package/packages/dd-trace/src/data_streams_context.js +2 -2
  96. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +14 -7
  97. package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +50 -0
  98. package/packages/dd-trace/src/debugger/devtools_client/state.js +38 -10
  99. package/packages/dd-trace/src/exporters/common/agents.js +1 -1
  100. package/packages/dd-trace/src/exporters/common/request.js +3 -3
  101. package/packages/dd-trace/src/iitm.js +2 -2
  102. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +1 -1
  103. package/packages/dd-trace/src/llmobs/tagger.js +12 -2
  104. package/packages/dd-trace/src/log/writer.js +3 -3
  105. package/packages/dd-trace/src/noop/span.js +1 -1
  106. package/packages/dd-trace/src/opentracing/propagation/text_map.js +5 -4
  107. package/packages/dd-trace/src/opentracing/span.js +3 -3
  108. package/packages/dd-trace/src/plugin_manager.js +3 -1
  109. package/packages/dd-trace/src/plugins/apollo.js +1 -1
  110. package/packages/dd-trace/src/plugins/ci_plugin.js +51 -4
  111. package/packages/dd-trace/src/plugins/database.js +14 -4
  112. package/packages/dd-trace/src/plugins/log_plugin.js +1 -1
  113. package/packages/dd-trace/src/plugins/plugin.js +8 -8
  114. package/packages/dd-trace/src/plugins/tracing.js +3 -3
  115. package/packages/dd-trace/src/plugins/util/git.js +3 -3
  116. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +1 -3
  117. package/packages/dd-trace/src/plugins/util/test.js +10 -4
  118. package/packages/dd-trace/src/profiling/exporters/agent.js +3 -3
  119. package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
  120. package/packages/dd-trace/src/proxy.js +5 -1
  121. package/packages/dd-trace/src/ritm.js +2 -1
  122. package/packages/dd-trace/src/scope.js +5 -5
  123. package/packages/dd-trace/src/spanleak.js +0 -1
  124. package/packages/dd-trace/src/tracer.js +0 -14
  125. package/packages/memwatch/package.json +0 -9
@@ -1,18 +1,23 @@
1
1
  'use strict'
2
2
 
3
3
  const Module = require('module')
4
+ const { pathToFileURL } = require('url')
5
+ const { MessageChannel } = require('worker_threads')
4
6
  const shimmer = require('../../../../../datadog-shimmer')
5
7
  const { isPrivateModule, isNotLibraryFile } = require('./filter')
6
8
  const { csiMethods } = require('./csi-methods')
7
9
  const { getName } = require('../telemetry/verbosity')
8
- const { getRewriteFunction } = require('./rewriter-telemetry')
10
+ const { getRewriteFunction, incrementTelemetryIfNeeded } = require('./rewriter-telemetry')
9
11
  const dc = require('dc-polyfill')
10
12
  const log = require('../../../log')
13
+ const { isMainThread } = require('worker_threads')
14
+ const { LOG_MESSAGE, REWRITTEN_MESSAGE } = require('./constants')
11
15
 
12
16
  const hardcodedSecretCh = dc.channel('datadog:secrets:result')
13
17
  let rewriter
14
- let getPrepareStackTrace
18
+ let getPrepareStackTrace, cacheRewrittenSourceMap
15
19
  let kSymbolPrepareStackTrace
20
+ let esmRewriterEnabled = false
16
21
 
17
22
  let getRewriterOriginalPathAndLineFromSourceMap = function (path, line, column) {
18
23
  return { path, line, column }
@@ -46,6 +51,7 @@ function getRewriter (telemetryVerbosity) {
46
51
  const Rewriter = iastRewriter.Rewriter
47
52
  getPrepareStackTrace = iastRewriter.getPrepareStackTrace
48
53
  kSymbolPrepareStackTrace = iastRewriter.kSymbolPrepareStackTrace
54
+ cacheRewrittenSourceMap = iastRewriter.cacheRewrittenSourceMap
49
55
 
50
56
  const chainSourceMap = isFlagPresent('--enable-source-maps')
51
57
  const getOriginalPathAndLineFromSourceMap = iastRewriter.getOriginalPathAndLineFromSourceMap
@@ -104,6 +110,24 @@ function getCompileMethodFn (compileMethod) {
104
110
  }
105
111
  }
106
112
 
113
+ function esmRewritePostProcess (rewritten, filename) {
114
+ const { literalsResult, metrics } = rewritten
115
+
116
+ if (metrics?.status === 'modified') {
117
+ if (filename.startsWith('file://')) {
118
+ filename = filename.substring(7)
119
+ }
120
+
121
+ cacheRewrittenSourceMap(filename, rewritten.content)
122
+ }
123
+
124
+ incrementTelemetryIfNeeded(metrics)
125
+
126
+ if (literalsResult && hardcodedSecretCh.hasSubscribers) {
127
+ hardcodedSecretCh.publish(literalsResult)
128
+ }
129
+ }
130
+
107
131
  function enableRewriter (telemetryVerbosity) {
108
132
  try {
109
133
  const rewriter = getRewriter(telemetryVerbosity)
@@ -114,11 +138,65 @@ function enableRewriter (telemetryVerbosity) {
114
138
  }
115
139
  shimmer.wrap(Module.prototype, '_compile', compileMethod => getCompileMethodFn(compileMethod))
116
140
  }
141
+
142
+ enableEsmRewriter(telemetryVerbosity)
117
143
  } catch (e) {
118
144
  log.error('[ASM] Error enabling TaintTracking Rewriter', e)
119
145
  }
120
146
  }
121
147
 
148
+ function isEsmConfigured () {
149
+ const hasLoaderArg = isFlagPresent('--loader') || isFlagPresent('--experimental-loader')
150
+ if (hasLoaderArg) return true
151
+
152
+ const initializeLoaded = Object.keys(require.cache).find(file => file.includes('import-in-the-middle/hook.js'))
153
+ return !!initializeLoaded
154
+ }
155
+
156
+ function enableEsmRewriter (telemetryVerbosity) {
157
+ if (isMainThread && Module.register && !esmRewriterEnabled && isEsmConfigured()) {
158
+ esmRewriterEnabled = true
159
+
160
+ const { port1, port2 } = new MessageChannel()
161
+
162
+ port1.on('message', (message) => {
163
+ const { type, data } = message
164
+ switch (type) {
165
+ case LOG_MESSAGE:
166
+ log[data.level]?.(...data.messages)
167
+ break
168
+
169
+ case REWRITTEN_MESSAGE:
170
+ esmRewritePostProcess(data.rewritten, data.url)
171
+ break
172
+ }
173
+ })
174
+
175
+ port1.unref()
176
+ port2.unref()
177
+
178
+ const chainSourceMap = isFlagPresent('--enable-source-maps')
179
+ const data = {
180
+ port: port2,
181
+ csiMethods,
182
+ telemetryVerbosity,
183
+ chainSourceMap
184
+ }
185
+
186
+ try {
187
+ Module.register('./rewriter-esm.mjs', {
188
+ parentURL: pathToFileURL(__filename),
189
+ transferList: [port2],
190
+ data
191
+ })
192
+ } catch (e) {
193
+ log.error('[ASM] Error enabling ESM Rewriter', e)
194
+ port1.close()
195
+ port2.close()
196
+ }
197
+ }
198
+ }
199
+
122
200
  function disableRewriter () {
123
201
  shimmer.unwrap(Module.prototype, '_compile')
124
202
 
@@ -3,7 +3,7 @@
3
3
  let next = 0
4
4
 
5
5
  function getNextSecureMark () {
6
- return 1 << next++
6
+ return (1 << next++) >>> 0
7
7
  }
8
8
 
9
9
  function reset () {
@@ -0,0 +1,28 @@
1
+ 'use strict'
2
+
3
+ const vulnerabilities = require('../vulnerabilities')
4
+ const { getNextSecureMark } = require('./secure-marks-generator')
5
+
6
+ const marks = {}
7
+ Object.keys(vulnerabilities).forEach(vulnerability => {
8
+ marks[vulnerability + '_MARK'] = getNextSecureMark()
9
+ })
10
+
11
+ let asterisk = 0x0
12
+ Object.values(marks).forEach(mark => { asterisk |= mark })
13
+
14
+ marks.ASTERISK_MARK = asterisk
15
+ marks.CUSTOM_SECURE_MARK = getNextSecureMark()
16
+
17
+ function getMarkFromVulnerabilityType (vulnerabilityType) {
18
+ vulnerabilityType = vulnerabilityType?.trim()
19
+ const mark = vulnerabilityType === '*' ? 'ASTERISK_MARK' : vulnerabilityType + '_MARK'
20
+ return marks[mark]
21
+ }
22
+
23
+ module.exports = {
24
+ ...marks,
25
+ getMarkFromVulnerabilityType,
26
+
27
+ ALL: marks
28
+ }
@@ -39,7 +39,7 @@ function getTransactionId (iastContext) {
39
39
  }
40
40
 
41
41
  function getContextDefault () {
42
- const store = storage.getStore()
42
+ const store = storage('legacy').getStore()
43
43
  return iastContextFunctions.getIastContext(store)
44
44
  }
45
45
 
@@ -83,6 +83,9 @@ const REQUEST_TAINTED = new NoTaggedIastMetric('request.tainted', Scope.REQUEST)
83
83
  const EXECUTED_PROPAGATION = new NoTaggedIastMetric('executed.propagation', Scope.REQUEST)
84
84
  const EXECUTED_TAINTED = new NoTaggedIastMetric('executed.tainted', Scope.REQUEST)
85
85
 
86
+ const SUPPRESSED_VULNERABILITIES = new IastMetric('suppressed.vulnerabilities', Scope.REQUEST,
87
+ TagKey.VULNERABILITY_TYPE)
88
+
86
89
  module.exports = {
87
90
  INSTRUMENTED_PROPAGATION,
88
91
  INSTRUMENTED_SOURCE,
@@ -95,6 +98,8 @@ module.exports = {
95
98
 
96
99
  REQUEST_TAINTED,
97
100
 
101
+ SUPPRESSED_VULNERABILITIES,
102
+
98
103
  PropagationType,
99
104
  TagKey,
100
105
 
@@ -0,0 +1,24 @@
1
+ 'use strict'
2
+
3
+ function iterateObjectStrings (target, fn, levelKeys = [], depth = 20, visited = new Set()) {
4
+ if (target !== null && typeof target === 'object') {
5
+ if (visited.has(target)) return
6
+
7
+ visited.add(target)
8
+
9
+ Object.keys(target).forEach((key) => {
10
+ const nextLevelKeys = [...levelKeys, key]
11
+ const val = target[key]
12
+
13
+ if (typeof val === 'string') {
14
+ fn(val, nextLevelKeys, target, key)
15
+ } else if (depth > 0) {
16
+ iterateObjectStrings(val, fn, nextLevelKeys, depth - 1, visited)
17
+ }
18
+ })
19
+ }
20
+ }
21
+
22
+ module.exports = {
23
+ iterateObjectStrings
24
+ }
@@ -81,21 +81,16 @@ class VulnerabilityFormatter {
81
81
  }
82
82
 
83
83
  formatVulnerability (vulnerability, sourcesIndexes, sources) {
84
+ const { type, hash, stackId, evidence, location } = vulnerability
85
+
84
86
  const formattedVulnerability = {
85
- type: vulnerability.type,
86
- hash: vulnerability.hash,
87
- stackId: vulnerability.stackId,
88
- evidence: this.formatEvidence(vulnerability.type, vulnerability.evidence, sourcesIndexes, sources),
89
- location: {
90
- spanId: vulnerability.location.spanId
91
- }
92
- }
93
- if (vulnerability.location.path) {
94
- formattedVulnerability.location.path = vulnerability.location.path
95
- }
96
- if (vulnerability.location.line) {
97
- formattedVulnerability.location.line = vulnerability.location.line
87
+ type,
88
+ hash,
89
+ stackId,
90
+ evidence: this.formatEvidence(type, evidence, sourcesIndexes, sources),
91
+ location
98
92
  }
93
+
99
94
  return formattedVulnerability
100
95
  }
101
96
 
@@ -131,6 +131,7 @@ function replaceCallSiteFromSourceMap (callsite) {
131
131
  if (line) {
132
132
  callsite.line = line
133
133
  }
134
+ // We send the column in the stack trace but not in the vulnerability location
134
135
  if (column) {
135
136
  callsite.column = column
136
137
  }
@@ -91,7 +91,7 @@ function onRequestBodyParsed ({ req, res, body, abortController }) {
91
91
  if (body === undefined || body === null) return
92
92
 
93
93
  if (!req) {
94
- const store = storage.getStore()
94
+ const store = storage('legacy').getStore()
95
95
  req = store?.req
96
96
  }
97
97
 
@@ -186,7 +186,7 @@ function incomingHttpEndTranslator ({ req, res }) {
186
186
  }
187
187
 
188
188
  function onPassportVerify ({ framework, login, user, success, abortController }) {
189
- const store = storage.getStore()
189
+ const store = storage('legacy').getStore()
190
190
  const rootSpan = store?.req && web.root(store.req)
191
191
 
192
192
  if (!rootSpan) {
@@ -200,7 +200,7 @@ function onPassportVerify ({ framework, login, user, success, abortController })
200
200
  }
201
201
 
202
202
  function onPassportDeserializeUser ({ user, abortController }) {
203
- const store = storage.getStore()
203
+ const store = storage('legacy').getStore()
204
204
  const rootSpan = store?.req && web.root(store.req)
205
205
 
206
206
  if (!rootSpan) {
@@ -217,7 +217,7 @@ function onRequestQueryParsed ({ req, res, query, abortController }) {
217
217
  if (!query || typeof query !== 'object') return
218
218
 
219
219
  if (!req) {
220
- const store = storage.getStore()
220
+ const store = storage('legacy').getStore()
221
221
  req = store?.req
222
222
  }
223
223
 
@@ -27,24 +27,24 @@ function disable () {
27
27
  function analyzeCommandInjection ({ file, fileArgs, shell, abortController }) {
28
28
  if (!file) return
29
29
 
30
- const store = storage.getStore()
30
+ const store = storage('legacy').getStore()
31
31
  const req = store?.req
32
32
  if (!req) return
33
33
 
34
- const persistent = {}
34
+ const ephemeral = {}
35
35
  const raspRule = { type: RULE_TYPES.COMMAND_INJECTION }
36
36
  const params = fileArgs ? [file, ...fileArgs] : file
37
37
 
38
38
  if (shell) {
39
- persistent[addresses.SHELL_COMMAND] = params
39
+ ephemeral[addresses.SHELL_COMMAND] = params
40
40
  raspRule.variant = 'shell'
41
41
  } else {
42
42
  const commandParams = Array.isArray(params) ? params : [params]
43
- persistent[addresses.EXEC_COMMAND] = commandParams
43
+ ephemeral[addresses.EXEC_COMMAND] = commandParams
44
44
  raspRule.variant = 'exec'
45
45
  }
46
46
 
47
- const result = waf.run({ persistent }, req, raspRule)
47
+ const result = waf.run({ ephemeral }, req, raspRule)
48
48
 
49
49
  const res = store?.res
50
50
  handleResult(result, req, res, abortController, config)
@@ -14,9 +14,9 @@ const enabledFor = {
14
14
 
15
15
  let fsPlugin
16
16
 
17
- function enterWith (fsProps, store = storage.getStore()) {
17
+ function enterWith (fsProps, store = storage('legacy').getStore()) {
18
18
  if (store && !store.fs?.opExcluded) {
19
- storage.enterWith({
19
+ storage('legacy').enterWith({
20
20
  ...store,
21
21
  fs: {
22
22
  ...store.fs,
@@ -42,7 +42,7 @@ class AppsecFsPlugin extends Plugin {
42
42
  }
43
43
 
44
44
  _onFsOperationStart () {
45
- const store = storage.getStore()
45
+ const store = storage('legacy').getStore()
46
46
  if (store) {
47
47
  enterWith({ root: store.fs?.root === undefined }, store)
48
48
  }
@@ -53,9 +53,9 @@ class AppsecFsPlugin extends Plugin {
53
53
  }
54
54
 
55
55
  _onFsOperationFinishOrRenderEnd () {
56
- const store = storage.getStore()
56
+ const store = storage('legacy').getStore()
57
57
  if (store?.fs?.parentStore) {
58
- storage.enterWith(store.fs.parentStore)
58
+ storage('legacy').enterWith(store.fs.parentStore)
59
59
  }
60
60
  }
61
61
  }
@@ -47,20 +47,20 @@ function onFirstReceivedRequest () {
47
47
  }
48
48
 
49
49
  function analyzeLfi (ctx) {
50
- const store = storage.getStore()
50
+ const store = storage('legacy').getStore()
51
51
  if (!store) return
52
52
 
53
53
  const { req, fs, res } = store
54
54
  if (!req || !fs) return
55
55
 
56
56
  getPaths(ctx, fs).forEach(path => {
57
- const persistent = {
57
+ const ephemeral = {
58
58
  [FS_OPERATION_PATH]: path
59
59
  }
60
60
 
61
61
  const raspRule = { type: RULE_TYPES.LFI }
62
62
 
63
- const result = waf.run({ persistent }, req, raspRule)
63
+ const result = waf.run({ ephemeral }, req, raspRule)
64
64
  handleResult(result, req, res, ctx.abortController, config)
65
65
  })
66
66
  }
@@ -49,7 +49,7 @@ function analyzePgSqlInjection (ctx) {
49
49
  }
50
50
 
51
51
  function analyzeSqlInjection (query, dbSystem, abortController) {
52
- const store = storage.getStore()
52
+ const store = storage('legacy').getStore()
53
53
  if (!store) return
54
54
 
55
55
  const { req, res } = store
@@ -67,14 +67,14 @@ function analyzeSqlInjection (query, dbSystem, abortController) {
67
67
  }
68
68
  executedQueries.add(query)
69
69
 
70
- const persistent = {
70
+ const ephemeral = {
71
71
  [addresses.DB_STATEMENT]: query,
72
72
  [addresses.DB_SYSTEM]: dbSystem
73
73
  }
74
74
 
75
75
  const raspRule = { type: RULE_TYPES.SQL_INJECTION }
76
76
 
77
- const result = waf.run({ persistent }, req, raspRule)
77
+ const result = waf.run({ ephemeral }, req, raspRule)
78
78
 
79
79
  handleResult(result, req, res, abortController, config)
80
80
  }
@@ -91,7 +91,7 @@ function hasAddressesObjectInputAddress (addressesObject) {
91
91
  function clearQuerySet ({ payload }) {
92
92
  if (!payload) return
93
93
 
94
- const store = storage.getStore()
94
+ const store = storage('legacy').getStore()
95
95
  if (!store) return
96
96
 
97
97
  const { req } = store
@@ -19,19 +19,19 @@ function disable () {
19
19
  }
20
20
 
21
21
  function analyzeSsrf (ctx) {
22
- const store = storage.getStore()
22
+ const store = storage('legacy').getStore()
23
23
  const req = store?.req
24
24
  const outgoingUrl = (ctx.args.options?.uri && format(ctx.args.options.uri)) ?? ctx.args.uri
25
25
 
26
26
  if (!req || !outgoingUrl) return
27
27
 
28
- const persistent = {
28
+ const ephemeral = {
29
29
  [addresses.HTTP_OUTGOING_URL]: outgoingUrl
30
30
  }
31
31
 
32
32
  const raspRule = { type: RULE_TYPES.SSRF }
33
33
 
34
- const result = waf.run({ persistent }, req, raspRule)
34
+ const result = waf.run({ ephemeral }, req, raspRule)
35
35
 
36
36
  const res = store?.res
37
37
  handleResult(result, req, res, ctx.abortController, config)
@@ -102,7 +102,7 @@ function reportWafInit (wafVersion, rulesVersion, diagnosticsRules = {}) {
102
102
  }
103
103
 
104
104
  function reportMetrics (metrics, raspRule) {
105
- const store = storage.getStore()
105
+ const store = storage('legacy').getStore()
106
106
  const rootSpan = store?.req && web.root(store.req)
107
107
  if (!rootSpan) return
108
108
 
@@ -117,7 +117,7 @@ function reportMetrics (metrics, raspRule) {
117
117
  }
118
118
 
119
119
  function reportAttack (attackData) {
120
- const store = storage.getStore()
120
+ const store = storage('legacy').getStore()
121
121
  const req = store?.req
122
122
  const rootSpan = web.root(req)
123
123
  if (!rootSpan) return
@@ -162,7 +162,7 @@ function isFingerprintDerivative (derivative) {
162
162
  function reportDerivatives (derivatives) {
163
163
  if (!derivatives) return
164
164
 
165
- const req = storage.getStore()?.req
165
+ const req = storage('legacy').getStore()?.req
166
166
  const rootSpan = web.root(req)
167
167
 
168
168
  if (!rootSpan) return
@@ -34,7 +34,7 @@ function checkUserAndSetUser (tracer, user) {
34
34
 
35
35
  function blockRequest (tracer, req, res) {
36
36
  if (!req || !res) {
37
- const store = storage.getStore()
37
+ const store = storage('legacy').getStore()
38
38
  if (store) {
39
39
  req = req || store.req
40
40
  res = res || store.res
@@ -48,7 +48,7 @@ function update (newRules) {
48
48
 
49
49
  function run (data, req, raspRule) {
50
50
  if (!req) {
51
- const store = storage.getStore()
51
+ const store = storage('legacy').getStore()
52
52
  if (!store || !store.req) {
53
53
  log.warn('[ASM] Request object not available in waf.run')
54
54
  return
@@ -119,6 +119,8 @@ class TestVisDynamicInstrumentation {
119
119
  const onHit = this.onHitBreakpointByProbeId.get(probeId)
120
120
  if (onHit) {
121
121
  onHit({ snapshot })
122
+ } else {
123
+ log.warn('Received a breakpoint hit for an unknown probe')
122
124
  }
123
125
  }).unref()
124
126
 
@@ -1,5 +1,5 @@
1
1
  'use strict'
2
- const path = require('path')
2
+
3
3
  const {
4
4
  workerData: {
5
5
  breakpointSetChannel,
@@ -8,10 +8,11 @@ const {
8
8
  }
9
9
  } = require('worker_threads')
10
10
  const { randomUUID } = require('crypto')
11
- const sourceMap = require('source-map')
12
11
 
13
12
  // TODO: move debugger/devtools_client/session to common place
14
13
  const session = require('../../../debugger/devtools_client/session')
14
+ // TODO: move debugger/devtools_client/source-maps to common place
15
+ const { getGeneratedPosition } = require('../../../debugger/devtools_client/source-maps')
15
16
  // TODO: move debugger/devtools_client/snapshot to common place
16
17
  const { getLocalStateForCallFrame } = require('../../../debugger/devtools_client/snapshot')
17
18
  // TODO: move debugger/devtools_client/state to common place
@@ -93,74 +94,48 @@ async function addBreakpoint (probe) {
93
94
  probe.location = { file, lines: [String(line)] }
94
95
 
95
96
  const script = findScriptFromPartialPath(file)
96
- if (!script) throw new Error(`No loaded script found for ${file}`)
97
+ if (!script) {
98
+ log.error(`No loaded script found for ${file}`)
99
+ throw new Error(`No loaded script found for ${file}`)
100
+ }
97
101
 
98
- const [path, scriptId, sourceMapURL] = script
102
+ const { url, scriptId, sourceMapURL, source } = script
99
103
 
100
- log.debug(`Adding breakpoint at ${path}:${line}`)
104
+ log.warn(`Adding breakpoint at ${url}:${line}`)
101
105
 
102
106
  let lineNumber = line
107
+ let columnNumber = 0
103
108
 
104
- if (sourceMapURL && sourceMapURL.startsWith('data:')) {
109
+ if (sourceMapURL) {
105
110
  try {
106
- lineNumber = await processScriptWithInlineSourceMap({ file, line, sourceMapURL })
111
+ ({ line: lineNumber, column: columnNumber } = await getGeneratedPosition(url, source, line, sourceMapURL))
107
112
  } catch (err) {
108
- log.error('Error processing script with inline source map', err)
113
+ log.error('Error processing script with source map', err)
114
+ }
115
+ if (lineNumber === null) {
116
+ log.error('Could not find generated position for %s:%s', url, line)
117
+ lineNumber = line
118
+ columnNumber = 0
109
119
  }
110
120
  }
111
121
 
112
- const { breakpointId } = await session.post('Debugger.setBreakpoint', {
113
- location: {
114
- scriptId,
115
- lineNumber: lineNumber - 1
116
- }
117
- })
122
+ try {
123
+ const { breakpointId } = await session.post('Debugger.setBreakpoint', {
124
+ location: {
125
+ scriptId,
126
+ lineNumber: lineNumber - 1,
127
+ columnNumber
128
+ }
129
+ })
118
130
 
119
- breakpointIdToProbe.set(breakpointId, probe)
120
- probeIdToBreakpointId.set(probe.id, breakpointId)
131
+ breakpointIdToProbe.set(breakpointId, probe)
132
+ probeIdToBreakpointId.set(probe.id, breakpointId)
133
+ } catch (e) {
134
+ log.error('Error setting breakpoint at %s:%s', url, line, e)
135
+ }
121
136
  }
122
137
 
123
138
  function start () {
124
139
  sessionStarted = true
125
140
  return session.post('Debugger.enable') // return instead of await to reduce number of promises created
126
141
  }
127
-
128
- async function processScriptWithInlineSourceMap (params) {
129
- const { file, line, sourceMapURL } = params
130
-
131
- // Extract the base64-encoded source map
132
- const base64SourceMap = sourceMapURL.split('base64,')[1]
133
-
134
- // Decode the base64 source map
135
- const decodedSourceMap = Buffer.from(base64SourceMap, 'base64').toString('utf8')
136
-
137
- // Parse the source map
138
- const consumer = await new sourceMap.SourceMapConsumer(decodedSourceMap)
139
-
140
- let generatedPosition
141
-
142
- // Map to the generated position. We'll attempt with the full file path first, then with the basename.
143
- // TODO: figure out why sometimes the full path doesn't work
144
- generatedPosition = consumer.generatedPositionFor({
145
- source: file,
146
- line,
147
- column: 0
148
- })
149
- if (generatedPosition.line === null) {
150
- generatedPosition = consumer.generatedPositionFor({
151
- source: path.basename(file),
152
- line,
153
- column: 0
154
- })
155
- }
156
-
157
- consumer.destroy()
158
-
159
- // If we can't find the line, just return the original line
160
- if (generatedPosition.line === null) {
161
- log.error(`Could not find generated position for ${file}:${line}`)
162
- return line
163
- }
164
-
165
- return generatedPosition.line
166
- }
@@ -6,6 +6,7 @@ const { sendGitMetadata: sendGitMetadataRequest } = require('./git/git_metadata'
6
6
  const { getLibraryConfiguration: getLibraryConfigurationRequest } = require('../requests/get-library-configuration')
7
7
  const { getSkippableSuites: getSkippableSuitesRequest } = require('../intelligent-test-runner/get-skippable-suites')
8
8
  const { getKnownTests: getKnownTestsRequest } = require('../early-flake-detection/get-known-tests')
9
+ const { getQuarantinedTests: getQuarantinedTestsRequest } = require('../quarantined-tests/get-quarantined-tests')
9
10
  const log = require('../../log')
10
11
  const AgentInfoExporter = require('../../exporters/common/agent-info-exporter')
11
12
  const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../../plugins/util/tags')
@@ -92,6 +93,14 @@ class CiVisibilityExporter extends AgentInfoExporter {
92
93
  )
93
94
  }
94
95
 
96
+ shouldRequestQuarantinedTests () {
97
+ return !!(
98
+ this._canUseCiVisProtocol &&
99
+ this._config.isTestManagementEnabled &&
100
+ this._libraryConfig?.isQuarantinedTestsEnabled
101
+ )
102
+ }
103
+
95
104
  shouldRequestLibraryConfiguration () {
96
105
  return this._config.isIntelligentTestRunnerEnabled
97
106
  }
@@ -138,6 +147,13 @@ class CiVisibilityExporter extends AgentInfoExporter {
138
147
  getKnownTestsRequest(this.getRequestConfiguration(testConfiguration), callback)
139
148
  }
140
149
 
150
+ getQuarantinedTests (testConfiguration, callback) {
151
+ if (!this.shouldRequestQuarantinedTests()) {
152
+ return callback(null)
153
+ }
154
+ getQuarantinedTestsRequest(this.getRequestConfiguration(testConfiguration), callback)
155
+ }
156
+
141
157
  /**
142
158
  * We can't request library configuration until we know whether we can use the
143
159
  * CI Visibility Protocol, hence the this._canUseCiVisProtocol promise.
@@ -197,7 +213,8 @@ class CiVisibilityExporter extends AgentInfoExporter {
197
213
  earlyFlakeDetectionFaultyThreshold,
198
214
  isFlakyTestRetriesEnabled,
199
215
  isDiEnabled,
200
- isKnownTestsEnabled
216
+ isKnownTestsEnabled,
217
+ isQuarantinedTestsEnabled
201
218
  } = remoteConfiguration
202
219
  return {
203
220
  isCodeCoverageEnabled,
@@ -210,7 +227,8 @@ class CiVisibilityExporter extends AgentInfoExporter {
210
227
  isFlakyTestRetriesEnabled: isFlakyTestRetriesEnabled && this._config.isFlakyTestRetriesEnabled,
211
228
  flakyTestRetriesCount: this._config.flakyTestRetriesCount,
212
229
  isDiEnabled: isDiEnabled && this._config.isTestDynamicInstrumentationEnabled,
213
- isKnownTestsEnabled
230
+ isKnownTestsEnabled,
231
+ isQuarantinedTestsEnabled: isQuarantinedTestsEnabled && this._config.isTestManagementEnabled
214
232
  }
215
233
  }
216
234