dd-trace 5.45.0 → 5.47.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 (86) hide show
  1. package/LICENSE-3rdparty.csv +1 -2
  2. package/ci/init.js +8 -0
  3. package/ext/exporters.d.ts +2 -1
  4. package/ext/exporters.js +2 -1
  5. package/package.json +8 -9
  6. package/packages/datadog-instrumentations/orchestrion.yml +52 -0
  7. package/packages/datadog-instrumentations/src/cucumber.js +2 -1
  8. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  9. package/packages/datadog-instrumentations/src/helpers/register.js +41 -1
  10. package/packages/datadog-instrumentations/src/jest.js +11 -2
  11. package/packages/datadog-instrumentations/src/langchain.js +49 -53
  12. package/packages/datadog-instrumentations/src/mariadb.js +19 -0
  13. package/packages/datadog-instrumentations/src/mocha/main.js +1 -1
  14. package/packages/datadog-instrumentations/src/mocha/utils.js +11 -3
  15. package/packages/datadog-instrumentations/src/orchestrion-config/index.js +5 -0
  16. package/packages/datadog-instrumentations/src/playwright.js +333 -46
  17. package/packages/datadog-instrumentations/src/router.js +1 -7
  18. package/packages/datadog-instrumentations/src/vitest.js +11 -3
  19. package/packages/datadog-plugin-cucumber/src/index.js +11 -4
  20. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +17 -5
  21. package/packages/datadog-plugin-jest/src/index.js +11 -4
  22. package/packages/datadog-plugin-langchain/src/index.js +18 -12
  23. package/packages/datadog-plugin-langchain/src/tracing.js +66 -6
  24. package/packages/datadog-plugin-mocha/src/index.js +17 -5
  25. package/packages/datadog-plugin-mongodb-core/src/index.js +24 -0
  26. package/packages/datadog-plugin-playwright/src/index.js +124 -10
  27. package/packages/datadog-plugin-vitest/src/index.js +13 -8
  28. package/packages/datadog-shimmer/src/shimmer.js +3 -42
  29. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +39 -15
  30. package/packages/dd-trace/src/appsec/iast/taint-tracking/filter.js +3 -3
  31. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +0 -3
  32. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +25 -12
  33. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-telemetry.js +3 -32
  34. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +99 -57
  35. package/packages/dd-trace/src/appsec/rasp/command_injection.js +1 -1
  36. package/packages/dd-trace/src/appsec/rasp/index.js +4 -2
  37. package/packages/dd-trace/src/appsec/rasp/lfi.js +1 -1
  38. package/packages/dd-trace/src/appsec/rasp/sql_injection.js +1 -1
  39. package/packages/dd-trace/src/appsec/rasp/ssrf.js +1 -1
  40. package/packages/dd-trace/src/appsec/rasp/utils.js +12 -7
  41. package/packages/dd-trace/src/appsec/recommended.json +256 -84
  42. package/packages/dd-trace/src/appsec/reporter.js +6 -4
  43. package/packages/dd-trace/src/appsec/telemetry/index.js +27 -3
  44. package/packages/dd-trace/src/appsec/telemetry/rasp.js +70 -6
  45. package/packages/dd-trace/src/appsec/telemetry/waf.js +0 -30
  46. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -0
  47. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +8 -3
  48. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +6 -4
  49. package/packages/dd-trace/src/config.js +9 -0
  50. package/packages/dd-trace/src/constants.js +1 -0
  51. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +102 -22
  52. package/packages/dd-trace/src/debugger/devtools_client/condition.js +263 -0
  53. package/packages/dd-trace/src/debugger/devtools_client/index.js +69 -36
  54. package/packages/dd-trace/src/debugger/devtools_client/lock.js +8 -0
  55. package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +1 -7
  56. package/packages/dd-trace/src/debugger/devtools_client/send.js +2 -2
  57. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +15 -10
  58. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +3 -3
  59. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +69 -62
  60. package/packages/dd-trace/src/debugger/devtools_client/state.js +3 -2
  61. package/packages/dd-trace/src/debugger/index.js +3 -0
  62. package/packages/dd-trace/src/encode/0.4.js +24 -17
  63. package/packages/dd-trace/src/exporter.js +1 -0
  64. package/packages/dd-trace/src/exporters/common/docker.js +37 -7
  65. package/packages/dd-trace/src/exporters/common/request.js +1 -4
  66. package/packages/dd-trace/src/format.js +58 -60
  67. package/packages/dd-trace/src/llmobs/plugins/base.js +2 -2
  68. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +62 -3
  69. package/packages/dd-trace/src/llmobs/plugins/openai.js +1 -0
  70. package/packages/dd-trace/src/llmobs/plugins/vertexai.js +2 -1
  71. package/packages/dd-trace/src/llmobs/writers/spans/base.js +3 -3
  72. package/packages/dd-trace/src/log/index.js +2 -0
  73. package/packages/dd-trace/src/log/writer.js +19 -2
  74. package/packages/dd-trace/src/opentelemetry/span.js +4 -4
  75. package/packages/dd-trace/src/opentracing/propagation/text_map.js +17 -3
  76. package/packages/dd-trace/src/opentracing/span.js +10 -0
  77. package/packages/dd-trace/src/plugin_manager.js +2 -0
  78. package/packages/dd-trace/src/plugins/util/test.js +11 -0
  79. package/packages/dd-trace/src/profiler.js +1 -1
  80. package/packages/dd-trace/src/profiling/config.js +6 -0
  81. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -5
  82. package/packages/dd-trace/src/profiling/profiler.js +4 -3
  83. package/packages/dd-trace/src/profiling/profilers/wall.js +12 -8
  84. package/packages/dd-trace/src/proxy.js +5 -1
  85. package/packages/dd-trace/src/tagger.js +38 -26
  86. package/packages/dd-trace/src/util.js +1 -7
@@ -12,6 +12,9 @@ const EXCLUDED_PATHS_FROM_STACK = getNodeModulesPaths('mongodb', 'mongoose', 'mq
12
12
  const { NOSQL_MONGODB_INJECTION_MARK } = require('../taint-tracking/secure-marks')
13
13
  const { iterateObjectStrings } = require('../utils')
14
14
 
15
+ const SAFE_OPERATORS = new Set(['$eq', '$gt', '$gte', '$in', '$lt', '$lte', '$ne', '$nin',
16
+ '$exists', '$type', '$mod', '$bitsAllClear', '$bitsAllSet', '$bitsAnyClear', '$bitsAnySet'])
17
+
15
18
  class NosqlInjectionMongodbAnalyzer extends InjectionAnalyzer {
16
19
  constructor () {
17
20
  super(NOSQL_MONGODB_INJECTION)
@@ -103,7 +106,11 @@ class NosqlInjectionMongodbAnalyzer extends InjectionAnalyzer {
103
106
  })
104
107
  }
105
108
 
106
- _isVulnerableRange (range) {
109
+ _isVulnerableRange (range, value) {
110
+ const rangeIsWholeValue = range.start === 0 && range.end === value?.length
111
+
112
+ if (!rangeIsWholeValue) return false
113
+
107
114
  const rangeType = range?.iinfo?.type
108
115
  return rangeType === HTTP_REQUEST_PARAMETER || rangeType === HTTP_REQUEST_BODY
109
116
  }
@@ -119,29 +126,25 @@ class NosqlInjectionMongodbAnalyzer extends InjectionAnalyzer {
119
126
  const rangesByKey = {}
120
127
  const allRanges = []
121
128
 
122
- iterateObjectStrings(value.filter, (val, nextLevelKeys) => {
129
+ iterateMongodbQueryStrings(value.filter, (val, nextLevelKeys) => {
123
130
  let ranges = getRanges(iastContext, val)
124
- if (ranges?.length) {
125
- const filteredRanges = []
126
-
131
+ if (ranges?.length === 1) {
127
132
  ranges = this._filterSecureRanges(ranges)
128
133
  if (!ranges.length) {
129
134
  this._incrementSuppressedMetric(iastContext)
135
+ return
130
136
  }
131
137
 
132
- for (const range of ranges) {
133
- if (this._isVulnerableRange(range)) {
134
- isVulnerable = true
135
- filteredRanges.push(range)
136
- }
138
+ const range = ranges[0]
139
+ if (!this._isVulnerableRange(range, val)) {
140
+ return
137
141
  }
142
+ isVulnerable = true
138
143
 
139
- if (filteredRanges.length > 0) {
140
- rangesByKey[nextLevelKeys.join('.')] = filteredRanges
141
- allRanges.push(...filteredRanges)
142
- }
144
+ rangesByKey[nextLevelKeys.join('.')] = ranges
145
+ allRanges.push(range)
143
146
  }
144
- }, [], 4)
147
+ })
145
148
 
146
149
  if (isVulnerable) {
147
150
  value.rangesToApply = rangesByKey
@@ -162,4 +165,25 @@ class NosqlInjectionMongodbAnalyzer extends InjectionAnalyzer {
162
165
  }
163
166
  }
164
167
 
168
+ function iterateMongodbQueryStrings (target, fn, levelKeys = [], depth = 10, visited = new Set()) {
169
+ if (target !== null && typeof target === 'object') {
170
+ if (visited.has(target)) return
171
+
172
+ visited.add(target)
173
+
174
+ Object.keys(target).forEach((key) => {
175
+ if (SAFE_OPERATORS.has(key)) return
176
+
177
+ const nextLevelKeys = [...levelKeys, key]
178
+ const val = target[key]
179
+
180
+ if (typeof val === 'string') {
181
+ fn(val, nextLevelKeys, target, key)
182
+ } else if (depth > 0) {
183
+ iterateMongodbQueryStrings(val, fn, nextLevelKeys, depth - 1, visited)
184
+ }
185
+ })
186
+ }
187
+ }
188
+
165
189
  module.exports = new NosqlInjectionMongodbAnalyzer()
@@ -6,11 +6,11 @@ const isPrivateModule = function (file) {
6
6
  return file && file.indexOf(NODE_MODULES) === -1
7
7
  }
8
8
 
9
- const isNotLibraryFile = function (file) {
10
- return file && file.indexOf('dd-trace-js') === -1 && file.indexOf('dd-trace') === -1
9
+ const isDdTrace = function (file) {
10
+ return file && (file.indexOf('dd-trace-js') !== -1 || file.indexOf('dd-trace') !== -1)
11
11
  }
12
12
 
13
13
  module.exports = {
14
14
  isPrivateModule,
15
- isNotLibraryFile
15
+ isDdTrace
16
16
  }
@@ -1,6 +1,5 @@
1
1
  'use strict'
2
2
 
3
- const { enableRewriter, disableRewriter } = require('./rewriter')
4
3
  const {
5
4
  createTransaction,
6
5
  removeTransaction,
@@ -16,7 +15,6 @@ const kafkaContextPlugin = require('../context/kafka-ctx-plugin')
16
15
 
17
16
  module.exports = {
18
17
  enableTaintTracking (config, telemetryVerbosity) {
19
- enableRewriter(telemetryVerbosity)
20
18
  enableTaintOperations(telemetryVerbosity)
21
19
  taintTrackingPlugin.enable(config)
22
20
 
@@ -26,7 +24,6 @@ module.exports = {
26
24
  setMaxTransactions(config.maxConcurrentRequests)
27
25
  },
28
26
  disableTaintTracking () {
29
- disableRewriter()
30
27
  disableTaintOperations()
31
28
  taintTrackingPlugin.disable()
32
29
 
@@ -3,28 +3,30 @@
3
3
  import path from 'path'
4
4
  import { URL } from 'url'
5
5
  import { getName } from '../telemetry/verbosity.js'
6
- import { isNotLibraryFile, isPrivateModule } from './filter.js'
6
+ import { isDdTrace, isPrivateModule } from './filter.js'
7
7
  import constants from './constants.js'
8
8
 
9
9
  const currentUrl = new URL(import.meta.url)
10
10
  const ddTraceDir = path.join(currentUrl.pathname, '..', '..', '..', '..', '..', '..')
11
11
 
12
- let port, rewriter
12
+ let port, rewriter, iastEnabled
13
13
 
14
14
  export async function initialize (data) {
15
15
  if (rewriter) return Promise.reject(new Error('ALREADY INITIALIZED'))
16
16
 
17
- const { csiMethods, telemetryVerbosity, chainSourceMap } = data
17
+ const { csiMethods, telemetryVerbosity, chainSourceMap, orchestrionConfig } = data
18
18
  port = data.port
19
+ iastEnabled = data.iastEnabled
19
20
 
20
- const iastRewriter = await import('@datadog/native-iast-rewriter')
21
+ const iastRewriter = await import('@datadog/wasm-js-rewriter')
21
22
 
22
23
  const { NonCacheRewriter } = iastRewriter.default
23
24
 
24
25
  rewriter = new NonCacheRewriter({
25
26
  csiMethods,
26
27
  telemetryVerbosity: getName(telemetryVerbosity),
27
- chainSourceMap
28
+ chainSourceMap,
29
+ orchestrion: orchestrionConfig
28
30
  })
29
31
  }
30
32
 
@@ -35,15 +37,26 @@ export async function load (url, context, nextLoad) {
35
37
  if (!result.source) return result
36
38
  if (url.includes(ddTraceDir) || url.includes('iitm=true')) return result
37
39
 
40
+ let passes
38
41
  try {
39
- if (isPrivateModule(url) && isNotLibraryFile(url)) {
40
- const rewritten = rewriter.rewrite(result.source.toString(), url)
41
-
42
- if (rewritten?.content) {
43
- result.source = rewritten.content || result.source
44
- const data = { url, rewritten }
45
- port.postMessage({ type: constants.REWRITTEN_MESSAGE, data })
42
+ if (isDdTrace(url)) {
43
+ return result
44
+ }
45
+ if (isPrivateModule(url)) {
46
+ // TODO error tracking needs to be added based on config
47
+ passes = ['error_tracking']
48
+ if (iastEnabled) {
49
+ passes.push('iast')
46
50
  }
51
+ } else {
52
+ passes = ['orchestrion']
53
+ }
54
+ const rewritten = rewriter.rewrite(result.source.toString(), url, passes)
55
+
56
+ if (rewritten?.content) {
57
+ result.source = rewritten.content || result.source
58
+ const data = { url, rewritten }
59
+ port.postMessage({ type: constants.REWRITTEN_MESSAGE, data })
47
60
  }
48
61
  } catch (e) {
49
62
  const newErrObject = {
@@ -4,39 +4,10 @@ const iastTelemetry = require('../telemetry')
4
4
  const { Verbosity } = require('../telemetry/verbosity')
5
5
  const { INSTRUMENTED_PROPAGATION } = require('../telemetry/iast-metric')
6
6
 
7
- const telemetryRewriter = {
8
- off (content, filename, rewriter) {
9
- return rewriter.rewrite(content, filename)
10
- },
11
-
12
- information (content, filename, rewriter) {
13
- const response = this.off(content, filename, rewriter)
14
-
15
- incrementTelemetry(response.metrics)
16
-
17
- return response
18
- }
19
- }
20
-
21
- function getRewriteFunction (rewriter) {
22
- switch (iastTelemetry.verbosity) {
23
- case Verbosity.OFF:
24
- return (content, filename) => telemetryRewriter.off(content, filename, rewriter)
25
- default:
26
- return (content, filename) => telemetryRewriter.information(content, filename, rewriter)
27
- }
28
- }
29
-
30
- function incrementTelemetry (metrics) {
31
- if (metrics?.instrumentedPropagation) {
32
- INSTRUMENTED_PROPAGATION.inc(undefined, metrics.instrumentedPropagation)
33
- }
34
- }
35
-
36
7
  function incrementTelemetryIfNeeded (metrics) {
37
- if (iastTelemetry.verbosity !== Verbosity.OFF) {
38
- incrementTelemetry(metrics)
8
+ if (iastTelemetry.verbosity !== Verbosity.OFF && metrics?.instrumentedPropagation) {
9
+ INSTRUMENTED_PROPAGATION.inc(undefined, metrics.instrumentedPropagation)
39
10
  }
40
11
  }
41
12
 
42
- module.exports = { getRewriteFunction, incrementTelemetryIfNeeded }
13
+ module.exports = { incrementTelemetryIfNeeded }
@@ -4,69 +4,70 @@ const Module = require('module')
4
4
  const { pathToFileURL } = require('url')
5
5
  const { MessageChannel } = require('worker_threads')
6
6
  const shimmer = require('../../../../../datadog-shimmer')
7
- const { isPrivateModule, isNotLibraryFile } = require('./filter')
7
+ const { isPrivateModule, isDdTrace } = require('./filter')
8
8
  const { csiMethods } = require('./csi-methods')
9
9
  const { getName } = require('../telemetry/verbosity')
10
- const { getRewriteFunction, incrementTelemetryIfNeeded } = require('./rewriter-telemetry')
10
+ const telemetry = require('../telemetry')
11
+ const { incrementTelemetryIfNeeded } = require('./rewriter-telemetry')
11
12
  const dc = require('dc-polyfill')
12
13
  const log = require('../../../log')
13
14
  const { isMainThread } = require('worker_threads')
14
15
  const { LOG_MESSAGE, REWRITTEN_MESSAGE } = require('./constants')
16
+ const orchestrionConfig = require('../../../../../datadog-instrumentations/src/orchestrion-config')
15
17
 
18
+ let config
16
19
  const hardcodedSecretCh = dc.channel('datadog:secrets:result')
17
20
  let rewriter
21
+ let unwrapCompile = () => {}
18
22
  let getPrepareStackTrace, cacheRewrittenSourceMap
19
23
  let kSymbolPrepareStackTrace
20
24
  let esmRewriterEnabled = false
21
25
 
22
- let getRewriterOriginalPathAndLineFromSourceMap = function (path, line, column) {
23
- return { path, line, column }
24
- }
25
-
26
26
  function isFlagPresent (flag) {
27
27
  return process.env.NODE_OPTIONS?.includes(flag) ||
28
28
  process.execArgv?.some(arg => arg.includes(flag))
29
29
  }
30
30
 
31
- function getGetOriginalPathAndLineFromSourceMapFunction (chainSourceMap, getOriginalPathAndLineFromSourceMap) {
32
- if (chainSourceMap) {
33
- return function (path, line, column) {
31
+ let getRewriterOriginalPathAndLineFromSourceMap = function (path, line, column) {
32
+ return { path, line, column }
33
+ }
34
+
35
+ function setGetOriginalPathAndLineFromSourceMapFunction (chainSourceMap, { getOriginalPathAndLineFromSourceMap }) {
36
+ if (!getOriginalPathAndLineFromSourceMap) return
37
+
38
+ getRewriterOriginalPathAndLineFromSourceMap = chainSourceMap
39
+ ? (path, line, column) => {
34
40
  // if --enable-source-maps is present stacktraces of the rewritten files contain the original path, file and
35
41
  // column because the sourcemap chaining is done during the rewriting process so we can skip it
36
- if (isPrivateModule(path) && isNotLibraryFile(path)) {
37
- return { path, line, column }
38
- } else {
39
- return getOriginalPathAndLineFromSourceMap(path, line, column)
42
+ if (isPrivateModule(path) && !isDdTrace(path)) {
43
+ return { path, line, column }
44
+ } else {
45
+ return getOriginalPathAndLineFromSourceMap(path, line, column)
46
+ }
40
47
  }
41
- }
42
- } else {
43
- return getOriginalPathAndLineFromSourceMap
44
- }
48
+ : getOriginalPathAndLineFromSourceMap
45
49
  }
46
50
 
47
51
  function getRewriter (telemetryVerbosity) {
48
52
  if (!rewriter) {
49
53
  try {
50
- const iastRewriter = require('@datadog/native-iast-rewriter')
54
+ const iastRewriter = require('@datadog/wasm-js-rewriter')
51
55
  const Rewriter = iastRewriter.Rewriter
52
56
  getPrepareStackTrace = iastRewriter.getPrepareStackTrace
53
57
  kSymbolPrepareStackTrace = iastRewriter.kSymbolPrepareStackTrace
54
58
  cacheRewrittenSourceMap = iastRewriter.cacheRewrittenSourceMap
55
59
 
56
60
  const chainSourceMap = isFlagPresent('--enable-source-maps')
57
- const getOriginalPathAndLineFromSourceMap = iastRewriter.getOriginalPathAndLineFromSourceMap
58
- if (getOriginalPathAndLineFromSourceMap) {
59
- getRewriterOriginalPathAndLineFromSourceMap =
60
- getGetOriginalPathAndLineFromSourceMapFunction(chainSourceMap, getOriginalPathAndLineFromSourceMap)
61
- }
61
+ setGetOriginalPathAndLineFromSourceMapFunction(chainSourceMap, iastRewriter)
62
62
 
63
63
  rewriter = new Rewriter({
64
64
  csiMethods,
65
65
  telemetryVerbosity: getName(telemetryVerbosity),
66
- chainSourceMap
66
+ chainSourceMap,
67
+ orchestrion: orchestrionConfig
67
68
  })
68
69
  } catch (e) {
69
- log.error('[ASM] Unable to initialize TaintTracking Rewriter', e)
70
+ log.error('Unable to initialize Rewriter', e)
70
71
  }
71
72
  }
72
73
  return rewriter
@@ -74,6 +75,9 @@ function getRewriter (telemetryVerbosity) {
74
75
 
75
76
  let originalPrepareStackTrace
76
77
  function getPrepareStackTraceAccessor () {
78
+ if (!getPrepareStackTrace) {
79
+ getPrepareStackTrace = require('@datadog/wasm-js-rewriter/js/stack-trace').getPrepareStackTrace
80
+ }
77
81
  originalPrepareStackTrace = Error.prepareStackTrace
78
82
  let actual = getPrepareStackTrace(originalPrepareStackTrace)
79
83
  return {
@@ -89,25 +93,42 @@ function getPrepareStackTraceAccessor () {
89
93
  }
90
94
 
91
95
  function getCompileMethodFn (compileMethod) {
92
- const rewriteFn = getRewriteFunction(rewriter)
93
- return function (content, filename) {
96
+ let delegate = function (content, filename) {
94
97
  try {
95
- if (isPrivateModule(filename) && isNotLibraryFile(filename)) {
96
- const rewritten = rewriteFn(content, filename)
98
+ if (isDdTrace(filename)) {
99
+ return compileMethod.apply(this, [content, filename])
100
+ }
101
+ if (!isPrivateModule(filename) || !config.iast?.enabled) {
102
+ return compileMethod.apply(this, [content, filename])
103
+ }
104
+ // TODO when we have CJS support for orchestrion and taint-tracking, add
105
+ // them here as appropriate
106
+ const rewritten = rewriter.rewrite(content, filename, ['iast'])
97
107
 
98
- if (rewritten?.literalsResult && hardcodedSecretCh.hasSubscribers) {
99
- hardcodedSecretCh.publish(rewritten.literalsResult)
100
- }
108
+ incrementTelemetryIfNeeded(rewritten.metrics)
101
109
 
102
- if (rewritten?.content) {
103
- return compileMethod.apply(this, [rewritten.content, filename])
104
- }
110
+ if (rewritten?.literalsResult && hardcodedSecretCh.hasSubscribers) {
111
+ hardcodedSecretCh.publish(rewritten.literalsResult)
112
+ }
113
+
114
+ if (rewritten?.content) {
115
+ return compileMethod.apply(this, [rewritten.content, filename])
105
116
  }
106
117
  } catch (e) {
107
- log.error('[ASM] Error rewriting file %s', filename, e)
118
+ log.error('Error rewriting file %s', filename, e)
108
119
  }
109
120
  return compileMethod.apply(this, [content, filename])
110
121
  }
122
+
123
+ const shim = function () {
124
+ return delegate.apply(this, arguments)
125
+ }
126
+
127
+ unwrapCompile = function () {
128
+ delegate = compileMethod
129
+ }
130
+
131
+ return shim
111
132
  }
112
133
 
113
134
  function esmRewritePostProcess (rewritten, filename) {
@@ -128,20 +149,31 @@ function esmRewritePostProcess (rewritten, filename) {
128
149
  }
129
150
  }
130
151
 
152
+ let shimmedPrepareStackTrace = false
153
+ function shimPrepareStackTrace () {
154
+ if (shimmedPrepareStackTrace) {
155
+ return
156
+ }
157
+ const pstDescriptor = Object.getOwnPropertyDescriptor(global.Error, 'prepareStackTrace')
158
+ if (!pstDescriptor || pstDescriptor.configurable) {
159
+ Object.defineProperty(global.Error, 'prepareStackTrace', getPrepareStackTraceAccessor())
160
+ }
161
+ shimmedPrepareStackTrace = true
162
+ }
163
+
131
164
  function enableRewriter (telemetryVerbosity) {
132
165
  try {
133
- const rewriter = getRewriter(telemetryVerbosity)
134
- if (rewriter) {
135
- const pstDescriptor = Object.getOwnPropertyDescriptor(global.Error, 'prepareStackTrace')
136
- if (!pstDescriptor || pstDescriptor.configurable) {
137
- Object.defineProperty(global.Error, 'prepareStackTrace', getPrepareStackTraceAccessor())
166
+ if (config.iast?.enabled) {
167
+ const rewriter = getRewriter(telemetryVerbosity)
168
+ if (rewriter) {
169
+ shimPrepareStackTrace()
170
+ shimmer.wrap(Module.prototype, '_compile', compileMethod => getCompileMethodFn(compileMethod))
138
171
  }
139
- shimmer.wrap(Module.prototype, '_compile', compileMethod => getCompileMethodFn(compileMethod))
140
172
  }
141
173
 
142
174
  enableEsmRewriter(telemetryVerbosity)
143
175
  } catch (e) {
144
- log.error('[ASM] Error enabling TaintTracking Rewriter', e)
176
+ log.error('Error enabling Rewriter', e)
145
177
  }
146
178
  }
147
179
 
@@ -155,6 +187,8 @@ function isEsmConfigured () {
155
187
 
156
188
  function enableEsmRewriter (telemetryVerbosity) {
157
189
  if (isMainThread && Module.register && !esmRewriterEnabled && isEsmConfigured()) {
190
+ shimPrepareStackTrace()
191
+
158
192
  esmRewriterEnabled = true
159
193
 
160
194
  const { port1, port2 } = new MessageChannel()
@@ -175,30 +209,31 @@ function enableEsmRewriter (telemetryVerbosity) {
175
209
  port1.unref()
176
210
  port2.unref()
177
211
 
178
- const chainSourceMap = isFlagPresent('--enable-source-maps')
179
- const data = {
180
- port: port2,
181
- csiMethods,
182
- telemetryVerbosity,
183
- chainSourceMap
184
- }
185
-
186
212
  try {
187
213
  Module.register('./rewriter-esm.mjs', {
188
214
  parentURL: pathToFileURL(__filename),
189
215
  transferList: [port2],
190
- data
216
+ data: {
217
+ port: port2,
218
+ csiMethods,
219
+ telemetryVerbosity,
220
+ chainSourceMap: isFlagPresent('--enable-source-maps'),
221
+ orchestrionConfig,
222
+ iastEnabled: config?.iast?.enabled
223
+ }
191
224
  })
192
225
  } catch (e) {
193
- log.error('[ASM] Error enabling ESM Rewriter', e)
226
+ log.error('Error enabling ESM Rewriter', e)
194
227
  port1.close()
195
228
  port2.close()
196
229
  }
230
+
231
+ cacheRewrittenSourceMap = require('@datadog/wasm-js-rewriter/js/source-map').cacheRewrittenSourceMap
197
232
  }
198
233
  }
199
234
 
200
- function disableRewriter () {
201
- shimmer.unwrap(Module.prototype, '_compile')
235
+ function disable () {
236
+ unwrapCompile()
202
237
 
203
238
  if (!Error.prepareStackTrace?.[kSymbolPrepareStackTrace]) return
204
239
 
@@ -206,8 +241,10 @@ function disableRewriter () {
206
241
  delete Error.prepareStackTrace
207
242
 
208
243
  Error.prepareStackTrace = originalPrepareStackTrace
244
+
245
+ shimmedPrepareStackTrace = false
209
246
  } catch (e) {
210
- log.warn('[ASM] Error disabling TaintTracking rewriter', e)
247
+ log.warn('Error disabling Rewriter', e)
211
248
  }
212
249
  }
213
250
 
@@ -215,6 +252,11 @@ function getOriginalPathAndLineFromSourceMap ({ path, line, column }) {
215
252
  return getRewriterOriginalPathAndLineFromSourceMap(path, line, column)
216
253
  }
217
254
 
255
+ function enable (configArg) {
256
+ config = configArg
257
+ enableRewriter(telemetry.verbosity || 'OFF')
258
+ }
259
+
218
260
  module.exports = {
219
- enableRewriter, disableRewriter, getOriginalPathAndLineFromSourceMap
261
+ enable, disable, getOriginalPathAndLineFromSourceMap
220
262
  }
@@ -47,7 +47,7 @@ function analyzeCommandInjection ({ file, fileArgs, shell, abortController }) {
47
47
  const result = waf.run({ ephemeral }, req, raspRule)
48
48
 
49
49
  const res = store?.res
50
- handleResult(result, req, res, abortController, config)
50
+ handleResult(result, req, res, abortController, config, raspRule)
51
51
  }
52
52
 
53
53
  module.exports = {
@@ -7,6 +7,7 @@ const ssrf = require('./ssrf')
7
7
  const sqli = require('./sql_injection')
8
8
  const lfi = require('./lfi')
9
9
  const cmdi = require('./command_injection')
10
+ const { updateRaspRuleMatchMetricTags } = require('../telemetry')
10
11
 
11
12
  const { DatadogRaspAbortError } = require('./utils')
12
13
 
@@ -84,9 +85,10 @@ function blockOnDatadogRaspAbortError ({ error }) {
84
85
  const abortError = findDatadogRaspAbortError(error)
85
86
  if (!abortError) return false
86
87
 
87
- const { req, res, blockingAction } = abortError
88
+ const { req, res, blockingAction, raspRule } = abortError
88
89
  if (!isBlocked(res)) {
89
- block(req, res, web.root(req), null, blockingAction)
90
+ const blocked = block(req, res, web.root(req), null, blockingAction)
91
+ updateRaspRuleMatchMetricTags(req, raspRule, true, blocked)
90
92
  }
91
93
 
92
94
  return true
@@ -61,7 +61,7 @@ function analyzeLfi (ctx) {
61
61
  const raspRule = { type: RULE_TYPES.LFI }
62
62
 
63
63
  const result = waf.run({ ephemeral }, req, raspRule)
64
- handleResult(result, req, res, ctx.abortController, config)
64
+ handleResult(result, req, res, ctx.abortController, config, raspRule)
65
65
  })
66
66
  }
67
67
 
@@ -76,7 +76,7 @@ function analyzeSqlInjection (query, dbSystem, abortController) {
76
76
 
77
77
  const result = waf.run({ ephemeral }, req, raspRule)
78
78
 
79
- handleResult(result, req, res, abortController, config)
79
+ handleResult(result, req, res, abortController, config, raspRule)
80
80
  }
81
81
 
82
82
  function hasInputAddress (payload) {
@@ -34,7 +34,7 @@ function analyzeSsrf (ctx) {
34
34
  const result = waf.run({ ephemeral }, req, raspRule)
35
35
 
36
36
  const res = store?.res
37
- handleResult(result, req, res, ctx.abortController, config)
37
+ handleResult(result, req, res, ctx.abortController, config, raspRule)
38
38
  }
39
39
 
40
40
  module.exports = { enable, disable }
@@ -4,6 +4,7 @@ const web = require('../../plugins/util/web')
4
4
  const { getCallsiteFrames, reportStackTrace, canReportStackTrace } = require('../stack_trace')
5
5
  const { getBlockingAction } = require('../blocking')
6
6
  const log = require('../../log')
7
+ const { updateRaspRuleMatchMetricTags } = require('../telemetry')
7
8
 
8
9
  const abortOnUncaughtException = process.execArgv?.includes('--abort-on-uncaught-exception')
9
10
 
@@ -19,16 +20,17 @@ const RULE_TYPES = {
19
20
  }
20
21
 
21
22
  class DatadogRaspAbortError extends Error {
22
- constructor (req, res, blockingAction) {
23
+ constructor (req, res, blockingAction, raspRule) {
23
24
  super('DatadogRaspAbortError')
24
25
  this.name = 'DatadogRaspAbortError'
25
26
  this.req = req
26
27
  this.res = res
27
28
  this.blockingAction = blockingAction
29
+ this.raspRule = raspRule
28
30
  }
29
31
  }
30
32
 
31
- function handleResult (actions, req, res, abortController, config) {
33
+ function handleResult (actions, req, res, abortController, config, raspRule) {
32
34
  const generateStackTraceAction = actions?.generate_stack
33
35
 
34
36
  const { enabled, maxDepth, maxStackTraces } = config.appsec.stackTrace
@@ -45,21 +47,24 @@ function handleResult (actions, req, res, abortController, config) {
45
47
  )
46
48
  }
47
49
 
48
- if (!abortController || abortOnUncaughtException) return
50
+ if (abortController && !abortOnUncaughtException) {
51
+ const blockingAction = getBlockingAction(actions)
49
52
 
50
- const blockingAction = getBlockingAction(actions)
51
- if (blockingAction) {
52
53
  // Should block only in express
53
- if (rootSpan?.context()._name === 'express.request') {
54
- const abortError = new DatadogRaspAbortError(req, res, blockingAction)
54
+ if (blockingAction && rootSpan?.context()._name === 'express.request') {
55
+ const abortError = new DatadogRaspAbortError(req, res, blockingAction, raspRule)
55
56
  abortController.abort(abortError)
56
57
 
57
58
  // TODO Delete this when support for node 16 is removed
58
59
  if (!abortController.signal.reason) {
59
60
  abortController.signal.reason = abortError
60
61
  }
62
+
63
+ return
61
64
  }
62
65
  }
66
+
67
+ updateRaspRuleMatchMetricTags(req, raspRule, false, false)
63
68
  }
64
69
 
65
70
  module.exports = {