dd-trace 4.47.1 → 4.49.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 (156) hide show
  1. package/LICENSE-3rdparty.csv +1 -1
  2. package/ext/types.d.ts +1 -0
  3. package/ext/types.js +1 -0
  4. package/index.d.ts +361 -0
  5. package/package.json +18 -13
  6. package/packages/datadog-code-origin/index.js +38 -0
  7. package/packages/datadog-core/index.js +2 -2
  8. package/packages/datadog-core/src/utils/src/parse-tags.js +33 -0
  9. package/packages/datadog-esbuild/index.js +4 -2
  10. package/packages/datadog-instrumentations/src/amqplib.js +65 -5
  11. package/packages/datadog-instrumentations/src/avsc.js +37 -0
  12. package/packages/datadog-instrumentations/src/azure-functions.js +48 -0
  13. package/packages/datadog-instrumentations/src/child_process.js +144 -27
  14. package/packages/datadog-instrumentations/src/express.js +37 -4
  15. package/packages/datadog-instrumentations/src/fastify.js +12 -1
  16. package/packages/datadog-instrumentations/src/fs.js +27 -7
  17. package/packages/datadog-instrumentations/src/helpers/hooks.js +6 -0
  18. package/packages/datadog-instrumentations/src/helpers/register.js +9 -0
  19. package/packages/datadog-instrumentations/src/jest.js +2 -1
  20. package/packages/datadog-instrumentations/src/kafkajs.js +123 -63
  21. package/packages/datadog-instrumentations/src/mocha/common.js +1 -1
  22. package/packages/datadog-instrumentations/src/mocha/utils.js +2 -2
  23. package/packages/datadog-instrumentations/src/multer.js +37 -0
  24. package/packages/datadog-instrumentations/src/mysql2.js +220 -1
  25. package/packages/datadog-instrumentations/src/openai.js +2 -2
  26. package/packages/datadog-instrumentations/src/protobufjs.js +127 -0
  27. package/packages/datadog-instrumentations/src/url.js +84 -0
  28. package/packages/datadog-instrumentations/src/utils/src/extract-package-and-module-path.js +7 -4
  29. package/packages/datadog-instrumentations/src/winston.js +22 -0
  30. package/packages/datadog-plugin-amqplib/src/consumer.js +4 -4
  31. package/packages/datadog-plugin-avsc/src/index.js +9 -0
  32. package/packages/datadog-plugin-avsc/src/schema_iterator.js +169 -0
  33. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -0
  34. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -0
  35. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -0
  36. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -0
  37. package/packages/datadog-plugin-azure-functions/src/index.js +77 -0
  38. package/packages/datadog-plugin-fastify/src/code_origin.js +31 -0
  39. package/packages/datadog-plugin-fastify/src/index.js +10 -12
  40. package/packages/datadog-plugin-fastify/src/tracing.js +19 -0
  41. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +8 -1
  42. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +8 -0
  43. package/packages/datadog-plugin-grpc/src/client.js +3 -0
  44. package/packages/datadog-plugin-grpc/src/server.js +3 -0
  45. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +6 -3
  46. package/packages/datadog-plugin-kafkajs/src/consumer.js +8 -4
  47. package/packages/datadog-plugin-kafkajs/src/producer.js +10 -4
  48. package/packages/datadog-plugin-mocha/src/index.js +4 -1
  49. package/packages/datadog-plugin-openai/src/index.js +9 -1015
  50. package/packages/datadog-plugin-openai/src/tracing.js +1023 -0
  51. package/packages/datadog-plugin-protobufjs/src/index.js +14 -0
  52. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +180 -0
  53. package/packages/dd-trace/src/appsec/addresses.js +8 -1
  54. package/packages/dd-trace/src/appsec/channels.js +7 -1
  55. package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +13 -1
  56. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +8 -1
  57. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +1 -1
  58. package/packages/dd-trace/src/appsec/iast/index.js +3 -0
  59. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +1 -0
  60. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +55 -7
  61. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +15 -0
  62. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -2
  63. package/packages/dd-trace/src/appsec/index.js +61 -43
  64. package/packages/dd-trace/src/appsec/rasp/command_injection.js +49 -0
  65. package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +99 -0
  66. package/packages/dd-trace/src/appsec/rasp/index.js +27 -10
  67. package/packages/dd-trace/src/appsec/rasp/lfi.js +112 -0
  68. package/packages/dd-trace/src/appsec/rasp/sql_injection.js +24 -4
  69. package/packages/dd-trace/src/appsec/rasp/ssrf.js +4 -3
  70. package/packages/dd-trace/src/appsec/rasp/utils.js +4 -2
  71. package/packages/dd-trace/src/appsec/recommended.json +3 -7
  72. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +6 -1
  73. package/packages/dd-trace/src/appsec/remote_config/index.js +10 -0
  74. package/packages/dd-trace/src/appsec/reporter.js +17 -9
  75. package/packages/dd-trace/src/appsec/sdk/track_event.js +10 -3
  76. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +1 -1
  77. package/packages/dd-trace/src/appsec/waf/waf_manager.js +4 -0
  78. package/packages/dd-trace/src/azure_metadata.js +120 -0
  79. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +97 -0
  80. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +90 -0
  81. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +2 -14
  82. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +19 -1
  83. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +53 -0
  84. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +8 -1
  85. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +43 -0
  86. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +53 -0
  87. package/packages/dd-trace/src/config.js +86 -6
  88. package/packages/dd-trace/src/constants.js +3 -1
  89. package/packages/dd-trace/src/datastreams/pathway.js +1 -0
  90. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +25 -17
  91. package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -0
  92. package/packages/dd-trace/src/debugger/devtools_client/index.js +52 -5
  93. package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +4 -4
  94. package/packages/dd-trace/src/debugger/devtools_client/send.js +29 -2
  95. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +187 -0
  96. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +40 -0
  97. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +252 -0
  98. package/packages/dd-trace/src/debugger/devtools_client/snapshot/symbols.js +6 -0
  99. package/packages/dd-trace/src/debugger/devtools_client/state.js +19 -4
  100. package/packages/dd-trace/src/debugger/index.js +10 -3
  101. package/packages/dd-trace/src/exporters/common/request.js +8 -34
  102. package/packages/dd-trace/src/exporters/common/url-to-http-options-polyfill.js +31 -0
  103. package/packages/dd-trace/src/llmobs/constants/tags.js +34 -0
  104. package/packages/dd-trace/src/llmobs/constants/text.js +6 -0
  105. package/packages/dd-trace/src/llmobs/constants/writers.js +13 -0
  106. package/packages/dd-trace/src/llmobs/index.js +103 -0
  107. package/packages/dd-trace/src/llmobs/noop.js +82 -0
  108. package/packages/dd-trace/src/llmobs/plugins/base.js +65 -0
  109. package/packages/dd-trace/src/llmobs/plugins/openai.js +205 -0
  110. package/packages/dd-trace/src/llmobs/sdk.js +377 -0
  111. package/packages/dd-trace/src/llmobs/span_processor.js +195 -0
  112. package/packages/dd-trace/src/llmobs/storage.js +7 -0
  113. package/packages/dd-trace/src/llmobs/tagger.js +322 -0
  114. package/packages/dd-trace/src/llmobs/util.js +176 -0
  115. package/packages/dd-trace/src/llmobs/writers/base.js +111 -0
  116. package/packages/dd-trace/src/llmobs/writers/evaluations.js +29 -0
  117. package/packages/dd-trace/src/llmobs/writers/spans/agentProxy.js +23 -0
  118. package/packages/dd-trace/src/llmobs/writers/spans/agentless.js +17 -0
  119. package/packages/dd-trace/src/llmobs/writers/spans/base.js +49 -0
  120. package/packages/dd-trace/src/noop/proxy.js +3 -0
  121. package/packages/dd-trace/src/noop/span.js +3 -0
  122. package/packages/dd-trace/src/opentelemetry/span.js +1 -1
  123. package/packages/dd-trace/src/opentelemetry/tracer.js +1 -0
  124. package/packages/dd-trace/src/opentracing/propagation/text_map.js +73 -12
  125. package/packages/dd-trace/src/opentracing/span.js +12 -0
  126. package/packages/dd-trace/src/opentracing/tracer.js +8 -1
  127. package/packages/dd-trace/src/payload-tagging/config/aws.json +71 -3
  128. package/packages/dd-trace/src/payload-tagging/index.js +1 -1
  129. package/packages/dd-trace/src/payload-tagging/jsonpath-plus.js +2094 -0
  130. package/packages/dd-trace/src/plugin_manager.js +4 -2
  131. package/packages/dd-trace/src/plugins/ci_plugin.js +2 -0
  132. package/packages/dd-trace/src/plugins/index.js +3 -0
  133. package/packages/dd-trace/src/plugins/log_plugin.js +1 -1
  134. package/packages/dd-trace/src/plugins/outbound.js +9 -0
  135. package/packages/dd-trace/src/plugins/schema.js +35 -0
  136. package/packages/dd-trace/src/plugins/util/ci.js +23 -1
  137. package/packages/dd-trace/src/plugins/util/serverless.js +7 -0
  138. package/packages/dd-trace/src/plugins/util/stacktrace.js +94 -0
  139. package/packages/dd-trace/src/plugins/util/tags.js +7 -0
  140. package/packages/dd-trace/src/plugins/util/test.js +20 -22
  141. package/packages/dd-trace/src/plugins/util/web.js +6 -4
  142. package/packages/dd-trace/src/priority_sampler.js +16 -0
  143. package/packages/dd-trace/src/profiling/config.js +3 -1
  144. package/packages/dd-trace/src/profiling/exporters/agent.js +7 -5
  145. package/packages/dd-trace/src/profiling/profiler.js +24 -14
  146. package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
  147. package/packages/dd-trace/src/profiling/profilers/wall.js +95 -66
  148. package/packages/dd-trace/src/proxy.js +20 -1
  149. package/packages/dd-trace/src/service-naming/schemas/v0/index.js +2 -1
  150. package/packages/dd-trace/src/service-naming/schemas/v0/serverless.js +12 -0
  151. package/packages/dd-trace/src/service-naming/schemas/v1/index.js +2 -1
  152. package/packages/dd-trace/src/service-naming/schemas/v1/serverless.js +12 -0
  153. package/packages/dd-trace/src/span_processor.js +5 -0
  154. package/packages/dd-trace/src/telemetry/index.js +11 -1
  155. package/packages/datadog-core/src/storage/async_resource.js +0 -108
  156. package/packages/datadog-core/src/storage/index.js +0 -5
@@ -0,0 +1,49 @@
1
+ 'use strict'
2
+
3
+ const { childProcessExecutionTracingChannel } = require('../channels')
4
+ const { RULE_TYPES, handleResult } = require('./utils')
5
+ const { storage } = require('../../../../datadog-core')
6
+ const addresses = require('../addresses')
7
+ const waf = require('../waf')
8
+
9
+ let config
10
+
11
+ function enable (_config) {
12
+ config = _config
13
+
14
+ childProcessExecutionTracingChannel.subscribe({
15
+ start: analyzeCommandInjection
16
+ })
17
+ }
18
+
19
+ function disable () {
20
+ if (childProcessExecutionTracingChannel.start.hasSubscribers) {
21
+ childProcessExecutionTracingChannel.unsubscribe({
22
+ start: analyzeCommandInjection
23
+ })
24
+ }
25
+ }
26
+
27
+ function analyzeCommandInjection ({ file, fileArgs, shell, abortController }) {
28
+ if (!file || !shell) return
29
+
30
+ const store = storage.getStore()
31
+ const req = store?.req
32
+ if (!req) return
33
+
34
+ const commandParams = fileArgs ? [file, ...fileArgs] : file
35
+
36
+ const persistent = {
37
+ [addresses.SHELL_COMMAND]: commandParams
38
+ }
39
+
40
+ const result = waf.run({ persistent }, req, RULE_TYPES.COMMAND_INJECTION)
41
+
42
+ const res = store?.res
43
+ handleResult(result, req, res, abortController, config)
44
+ }
45
+
46
+ module.exports = {
47
+ enable,
48
+ disable
49
+ }
@@ -0,0 +1,99 @@
1
+ 'use strict'
2
+
3
+ const Plugin = require('../../plugins/plugin')
4
+ const { storage } = require('../../../../datadog-core')
5
+ const log = require('../../log')
6
+
7
+ const RASP_MODULE = 'rasp'
8
+ const IAST_MODULE = 'iast'
9
+
10
+ const enabledFor = {
11
+ [RASP_MODULE]: false,
12
+ [IAST_MODULE]: false
13
+ }
14
+
15
+ let fsPlugin
16
+
17
+ function enterWith (fsProps, store = storage.getStore()) {
18
+ if (store && !store.fs?.opExcluded) {
19
+ storage.enterWith({
20
+ ...store,
21
+ fs: {
22
+ ...store.fs,
23
+ ...fsProps,
24
+ parentStore: store
25
+ }
26
+ })
27
+ }
28
+ }
29
+
30
+ class AppsecFsPlugin extends Plugin {
31
+ enable () {
32
+ this.addSub('apm:fs:operation:start', this._onFsOperationStart)
33
+ this.addSub('apm:fs:operation:finish', this._onFsOperationFinishOrRenderEnd)
34
+ this.addSub('tracing:datadog:express:response:render:start', this._onResponseRenderStart)
35
+ this.addSub('tracing:datadog:express:response:render:end', this._onFsOperationFinishOrRenderEnd)
36
+
37
+ super.configure(true)
38
+ }
39
+
40
+ disable () {
41
+ super.configure(false)
42
+ }
43
+
44
+ _onFsOperationStart () {
45
+ const store = storage.getStore()
46
+ if (store) {
47
+ enterWith({ root: store.fs?.root === undefined }, store)
48
+ }
49
+ }
50
+
51
+ _onResponseRenderStart () {
52
+ enterWith({ opExcluded: true })
53
+ }
54
+
55
+ _onFsOperationFinishOrRenderEnd () {
56
+ const store = storage.getStore()
57
+ if (store?.fs?.parentStore) {
58
+ storage.enterWith(store.fs.parentStore)
59
+ }
60
+ }
61
+ }
62
+
63
+ function enable (mod) {
64
+ if (enabledFor[mod] !== false) return
65
+
66
+ enabledFor[mod] = true
67
+
68
+ if (!fsPlugin) {
69
+ fsPlugin = new AppsecFsPlugin()
70
+ fsPlugin.enable()
71
+ }
72
+
73
+ log.info(`Enabled AppsecFsPlugin for ${mod}`)
74
+ }
75
+
76
+ function disable (mod) {
77
+ if (!mod || !enabledFor[mod]) return
78
+
79
+ enabledFor[mod] = false
80
+
81
+ const allDisabled = Object.values(enabledFor).every(val => val === false)
82
+ if (allDisabled) {
83
+ fsPlugin?.disable()
84
+
85
+ fsPlugin = undefined
86
+ }
87
+
88
+ log.info(`Disabled AppsecFsPlugin for ${mod}`)
89
+ }
90
+
91
+ module.exports = {
92
+ enable,
93
+ disable,
94
+
95
+ AppsecFsPlugin,
96
+
97
+ RASP_MODULE,
98
+ IAST_MODULE
99
+ }
@@ -1,10 +1,12 @@
1
1
  'use strict'
2
2
 
3
3
  const web = require('../../plugins/util/web')
4
- const { setUncaughtExceptionCaptureCallbackStart } = require('../channels')
5
- const { block } = require('../blocking')
4
+ const { setUncaughtExceptionCaptureCallbackStart, expressMiddlewareError } = require('../channels')
5
+ const { block, isBlocked } = require('../blocking')
6
6
  const ssrf = require('./ssrf')
7
7
  const sqli = require('./sql_injection')
8
+ const lfi = require('./lfi')
9
+ const cmdi = require('./command_injection')
8
10
 
9
11
  const { DatadogRaspAbortError } = require('./utils')
10
12
 
@@ -30,17 +32,13 @@ function findDatadogRaspAbortError (err, deep = 10) {
30
32
  return err
31
33
  }
32
34
 
33
- if (err.cause && deep > 0) {
35
+ if (err?.cause && deep > 0) {
34
36
  return findDatadogRaspAbortError(err.cause, deep - 1)
35
37
  }
36
38
  }
37
39
 
38
- function handleUncaughtExceptionMonitor (err) {
39
- const abortError = findDatadogRaspAbortError(err)
40
- if (!abortError) return
41
-
42
- const { req, res, blockingAction } = abortError
43
- block(req, res, web.root(req), null, blockingAction)
40
+ function handleUncaughtExceptionMonitor (error) {
41
+ if (!blockOnDatadogRaspAbortError({ error })) return
44
42
 
45
43
  if (!process.hasUncaughtExceptionCaptureCallback()) {
46
44
  const cleanUp = removeAllListeners(process, 'uncaughtException')
@@ -82,22 +80,41 @@ function handleUncaughtExceptionMonitor (err) {
82
80
  }
83
81
  }
84
82
 
83
+ function blockOnDatadogRaspAbortError ({ error }) {
84
+ const abortError = findDatadogRaspAbortError(error)
85
+ if (!abortError) return false
86
+
87
+ const { req, res, blockingAction } = abortError
88
+ if (!isBlocked(res)) {
89
+ block(req, res, web.root(req), null, blockingAction)
90
+ }
91
+
92
+ return true
93
+ }
94
+
85
95
  function enable (config) {
86
96
  ssrf.enable(config)
87
97
  sqli.enable(config)
98
+ lfi.enable(config)
99
+ cmdi.enable(config)
88
100
 
89
101
  process.on('uncaughtExceptionMonitor', handleUncaughtExceptionMonitor)
102
+ expressMiddlewareError.subscribe(blockOnDatadogRaspAbortError)
90
103
  }
91
104
 
92
105
  function disable () {
93
106
  ssrf.disable()
94
107
  sqli.disable()
108
+ lfi.disable()
109
+ cmdi.disable()
95
110
 
96
111
  process.off('uncaughtExceptionMonitor', handleUncaughtExceptionMonitor)
112
+ if (expressMiddlewareError.hasSubscribers) expressMiddlewareError.unsubscribe(blockOnDatadogRaspAbortError)
97
113
  }
98
114
 
99
115
  module.exports = {
100
116
  enable,
101
117
  disable,
102
- handleUncaughtExceptionMonitor // exported only for testing purpose
118
+ handleUncaughtExceptionMonitor, // exported only for testing purpose
119
+ blockOnDatadogRaspAbortError // exported only for testing purpose
103
120
  }
@@ -0,0 +1,112 @@
1
+ 'use strict'
2
+
3
+ const { fsOperationStart, incomingHttpRequestStart } = require('../channels')
4
+ const { storage } = require('../../../../datadog-core')
5
+ const { enable: enableFsPlugin, disable: disableFsPlugin, RASP_MODULE } = require('./fs-plugin')
6
+ const { FS_OPERATION_PATH } = require('../addresses')
7
+ const waf = require('../waf')
8
+ const { RULE_TYPES, handleResult } = require('./utils')
9
+ const { isAbsolute } = require('path')
10
+
11
+ let config
12
+ let enabled
13
+ let analyzeSubscribed
14
+
15
+ function enable (_config) {
16
+ config = _config
17
+
18
+ if (enabled) return
19
+
20
+ enabled = true
21
+
22
+ incomingHttpRequestStart.subscribe(onFirstReceivedRequest)
23
+ }
24
+
25
+ function disable () {
26
+ if (fsOperationStart.hasSubscribers) fsOperationStart.unsubscribe(analyzeLfi)
27
+ if (incomingHttpRequestStart.hasSubscribers) incomingHttpRequestStart.unsubscribe(onFirstReceivedRequest)
28
+
29
+ disableFsPlugin(RASP_MODULE)
30
+
31
+ enabled = false
32
+ analyzeSubscribed = false
33
+ }
34
+
35
+ function onFirstReceivedRequest () {
36
+ // nodejs unsubscribe during publish bug: https://github.com/nodejs/node/pull/55116
37
+ process.nextTick(() => {
38
+ incomingHttpRequestStart.unsubscribe(onFirstReceivedRequest)
39
+ })
40
+
41
+ enableFsPlugin(RASP_MODULE)
42
+
43
+ if (!analyzeSubscribed) {
44
+ fsOperationStart.subscribe(analyzeLfi)
45
+ analyzeSubscribed = true
46
+ }
47
+ }
48
+
49
+ function analyzeLfi (ctx) {
50
+ const store = storage.getStore()
51
+ if (!store) return
52
+
53
+ const { req, fs, res } = store
54
+ if (!req || !fs) return
55
+
56
+ getPaths(ctx, fs).forEach(path => {
57
+ const persistent = {
58
+ [FS_OPERATION_PATH]: path
59
+ }
60
+
61
+ const result = waf.run({ persistent }, req, RULE_TYPES.LFI)
62
+ handleResult(result, req, res, ctx.abortController, config)
63
+ })
64
+ }
65
+
66
+ function getPaths (ctx, fs) {
67
+ // these properties could have String, Buffer, URL, Integer or FileHandle types
68
+ const pathArguments = [
69
+ ctx.dest,
70
+ ctx.existingPath,
71
+ ctx.file,
72
+ ctx.newPath,
73
+ ctx.oldPath,
74
+ ctx.path,
75
+ ctx.prefix,
76
+ ctx.src,
77
+ ctx.target
78
+ ]
79
+
80
+ return pathArguments
81
+ .map(path => pathToStr(path))
82
+ .filter(path => shouldAnalyze(path, fs))
83
+ }
84
+
85
+ function pathToStr (path) {
86
+ if (!path) return
87
+
88
+ if (typeof path === 'string' ||
89
+ path instanceof String ||
90
+ path instanceof Buffer ||
91
+ path instanceof URL) {
92
+ return path.toString()
93
+ }
94
+ }
95
+
96
+ function shouldAnalyze (path, fs) {
97
+ if (!path) return
98
+
99
+ const notExcludedRootOp = !fs.opExcluded && fs.root
100
+ return notExcludedRootOp && (isAbsolute(path) || path.includes('../') || shouldAnalyzeURLFile(path, fs))
101
+ }
102
+
103
+ function shouldAnalyzeURLFile (path, fs) {
104
+ if (path.startsWith('file://')) {
105
+ return shouldAnalyze(path.substring(7), fs)
106
+ }
107
+ }
108
+
109
+ module.exports = {
110
+ enable,
111
+ disable
112
+ }
@@ -1,12 +1,18 @@
1
1
  'use strict'
2
2
 
3
- const { pgQueryStart, pgPoolQueryStart, wafRunFinished } = require('../channels')
3
+ const {
4
+ pgQueryStart,
5
+ pgPoolQueryStart,
6
+ wafRunFinished,
7
+ mysql2OuterQueryStart
8
+ } = require('../channels')
4
9
  const { storage } = require('../../../../datadog-core')
5
10
  const addresses = require('../addresses')
6
11
  const waf = require('../waf')
7
12
  const { RULE_TYPES, handleResult } = require('./utils')
8
13
 
9
14
  const DB_SYSTEM_POSTGRES = 'postgresql'
15
+ const DB_SYSTEM_MYSQL = 'mysql'
10
16
  const reqQueryMap = new WeakMap() // WeakMap<Request, Set<querytext>>
11
17
 
12
18
  let config
@@ -17,18 +23,32 @@ function enable (_config) {
17
23
  pgQueryStart.subscribe(analyzePgSqlInjection)
18
24
  pgPoolQueryStart.subscribe(analyzePgSqlInjection)
19
25
  wafRunFinished.subscribe(clearQuerySet)
26
+
27
+ mysql2OuterQueryStart.subscribe(analyzeMysql2SqlInjection)
20
28
  }
21
29
 
22
30
  function disable () {
23
31
  if (pgQueryStart.hasSubscribers) pgQueryStart.unsubscribe(analyzePgSqlInjection)
24
32
  if (pgPoolQueryStart.hasSubscribers) pgPoolQueryStart.unsubscribe(analyzePgSqlInjection)
25
33
  if (wafRunFinished.hasSubscribers) wafRunFinished.unsubscribe(clearQuerySet)
34
+ if (mysql2OuterQueryStart.hasSubscribers) mysql2OuterQueryStart.unsubscribe(analyzeMysql2SqlInjection)
35
+ }
36
+
37
+ function analyzeMysql2SqlInjection (ctx) {
38
+ const query = ctx.sql
39
+ if (!query) return
40
+
41
+ analyzeSqlInjection(query, DB_SYSTEM_MYSQL, ctx.abortController)
26
42
  }
27
43
 
28
44
  function analyzePgSqlInjection (ctx) {
29
45
  const query = ctx.query?.text
30
46
  if (!query) return
31
47
 
48
+ analyzeSqlInjection(query, DB_SYSTEM_POSTGRES, ctx.abortController)
49
+ }
50
+
51
+ function analyzeSqlInjection (query, dbSystem, abortController) {
32
52
  const store = storage.getStore()
33
53
  if (!store) return
34
54
 
@@ -39,7 +59,7 @@ function analyzePgSqlInjection (ctx) {
39
59
  let executedQueries = reqQueryMap.get(req)
40
60
  if (executedQueries?.has(query)) return
41
61
 
42
- // Do not waste time executing same query twice
62
+ // Do not waste time checking same query twice
43
63
  // This also will prevent double calls in pg.Pool internal queries
44
64
  if (!executedQueries) {
45
65
  executedQueries = new Set()
@@ -49,12 +69,12 @@ function analyzePgSqlInjection (ctx) {
49
69
 
50
70
  const persistent = {
51
71
  [addresses.DB_STATEMENT]: query,
52
- [addresses.DB_SYSTEM]: DB_SYSTEM_POSTGRES
72
+ [addresses.DB_SYSTEM]: dbSystem
53
73
  }
54
74
 
55
75
  const result = waf.run({ persistent }, req, RULE_TYPES.SQL_INJECTION)
56
76
 
57
- handleResult(result, req, res, ctx.abortController, config)
77
+ handleResult(result, req, res, abortController, config)
58
78
  }
59
79
 
60
80
  function hasInputAddress (payload) {
@@ -1,5 +1,6 @@
1
1
  'use strict'
2
2
 
3
+ const { format } = require('url')
3
4
  const { httpClientRequestStart } = require('../channels')
4
5
  const { storage } = require('../../../../datadog-core')
5
6
  const addresses = require('../addresses')
@@ -20,12 +21,12 @@ function disable () {
20
21
  function analyzeSsrf (ctx) {
21
22
  const store = storage.getStore()
22
23
  const req = store?.req
23
- const url = ctx.args.uri
24
+ const outgoingUrl = (ctx.args.options?.uri && format(ctx.args.options.uri)) ?? ctx.args.uri
24
25
 
25
- if (!req || !url) return
26
+ if (!req || !outgoingUrl) return
26
27
 
27
28
  const persistent = {
28
- [addresses.HTTP_OUTGOING_URL]: url
29
+ [addresses.HTTP_OUTGOING_URL]: outgoingUrl
29
30
  }
30
31
 
31
32
  const result = waf.run({ persistent }, req, RULE_TYPES.SSRF)
@@ -12,8 +12,10 @@ if (abortOnUncaughtException) {
12
12
  }
13
13
 
14
14
  const RULE_TYPES = {
15
- SSRF: 'ssrf',
16
- SQL_INJECTION: 'sql_injection'
15
+ COMMAND_INJECTION: 'command_injection',
16
+ LFI: 'lfi',
17
+ SQL_INJECTION: 'sql_injection',
18
+ SSRF: 'ssrf'
17
19
  }
18
20
 
19
21
  class DatadogRaspAbortError extends Error {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": "2.2",
3
3
  "metadata": {
4
- "rules_version": "1.13.0"
4
+ "rules_version": "1.13.2"
5
5
  },
6
6
  "rules": [
7
7
  {
@@ -6239,7 +6239,6 @@
6239
6239
  {
6240
6240
  "id": "rasp-930-100",
6241
6241
  "name": "Local file inclusion exploit",
6242
- "enabled": false,
6243
6242
  "tags": {
6244
6243
  "type": "lfi",
6245
6244
  "category": "vulnerability_trigger",
@@ -6287,8 +6286,7 @@
6287
6286
  },
6288
6287
  {
6289
6288
  "id": "rasp-932-100",
6290
- "name": "Shell injection exploit",
6291
- "enabled": false,
6289
+ "name": "Command injection exploit",
6292
6290
  "tags": {
6293
6291
  "type": "command_injection",
6294
6292
  "category": "vulnerability_trigger",
@@ -6337,7 +6335,6 @@
6337
6335
  {
6338
6336
  "id": "rasp-934-100",
6339
6337
  "name": "Server-side request forgery exploit",
6340
- "enabled": false,
6341
6338
  "tags": {
6342
6339
  "type": "ssrf",
6343
6340
  "category": "vulnerability_trigger",
@@ -6386,7 +6383,6 @@
6386
6383
  {
6387
6384
  "id": "rasp-942-100",
6388
6385
  "name": "SQL injection exploit",
6389
- "enabled": false,
6390
6386
  "tags": {
6391
6387
  "type": "sql_injection",
6392
6388
  "category": "vulnerability_trigger",
@@ -6426,7 +6422,7 @@
6426
6422
  }
6427
6423
  ]
6428
6424
  },
6429
- "operator": "sqli_detector"
6425
+ "operator": "sqli_detector@v2"
6430
6426
  }
6431
6427
  ],
6432
6428
  "transformers": [],
@@ -18,6 +18,11 @@ module.exports = {
18
18
  APM_TRACING_CUSTOM_TAGS: 1n << 15n,
19
19
  APM_TRACING_ENABLED: 1n << 19n,
20
20
  ASM_RASP_SQLI: 1n << 21n,
21
+ ASM_RASP_LFI: 1n << 22n,
21
22
  ASM_RASP_SSRF: 1n << 23n,
22
- APM_TRACING_SAMPLE_RULES: 1n << 29n
23
+ ASM_RASP_SHI: 1n << 24n,
24
+ APM_TRACING_SAMPLE_RULES: 1n << 29n,
25
+ ASM_ENDPOINT_FINGERPRINT: 1n << 32n,
26
+ ASM_NETWORK_FINGERPRINT: 1n << 34n,
27
+ ASM_HEADER_FINGERPRINT: 1n << 35n
23
28
  }
@@ -75,10 +75,15 @@ function enableWafUpdate (appsecConfig) {
75
75
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_RULES, true)
76
76
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_BLOCKING_RESPONSE, true)
77
77
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_TRUSTED_IPS, true)
78
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_ENDPOINT_FINGERPRINT, true)
79
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_NETWORK_FINGERPRINT, true)
80
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_HEADER_FINGERPRINT, true)
78
81
 
79
82
  if (appsecConfig.rasp?.enabled) {
80
83
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SQLI, true)
81
84
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SSRF, true)
85
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_LFI, true)
86
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SHI, true)
82
87
  }
83
88
 
84
89
  // TODO: delete noop handlers and kPreUpdate and replace with batched handlers
@@ -103,9 +108,14 @@ function disableWafUpdate () {
103
108
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_RULES, false)
104
109
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_BLOCKING_RESPONSE, false)
105
110
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_TRUSTED_IPS, false)
111
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_ENDPOINT_FINGERPRINT, false)
112
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_NETWORK_FINGERPRINT, false)
113
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_HEADER_FINGERPRINT, false)
106
114
 
107
115
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SQLI, false)
108
116
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SSRF, false)
117
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_LFI, false)
118
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SHI, false)
109
119
 
110
120
  rc.removeProductHandler('ASM_DATA')
111
121
  rc.removeProductHandler('ASM_DD')
@@ -13,8 +13,9 @@ const {
13
13
  getRequestMetrics
14
14
  } = require('./telemetry')
15
15
  const zlib = require('zlib')
16
- const { MANUAL_KEEP } = require('../../../../ext/tags')
17
16
  const standalone = require('./standalone')
17
+ const { SAMPLING_MECHANISM_APPSEC } = require('../constants')
18
+ const { keepTrace } = require('../priority_sampler')
18
19
 
19
20
  // default limiter, configurable with setRateLimit()
20
21
  let limiter = new Limiter(100)
@@ -96,8 +97,6 @@ function reportWafInit (wafVersion, rulesVersion, diagnosticsRules = {}) {
96
97
  metricsQueue.set('_dd.appsec.event_rules.errors', JSON.stringify(diagnosticsRules.errors))
97
98
  }
98
99
 
99
- metricsQueue.set(MANUAL_KEEP, 'true')
100
-
101
100
  incrementWafInitMetric(wafVersion, rulesVersion)
102
101
  }
103
102
 
@@ -129,7 +128,7 @@ function reportAttack (attackData) {
129
128
  }
130
129
 
131
130
  if (limiter.isAllowed()) {
132
- newTags[MANUAL_KEEP] = 'true'
131
+ keepTrace(rootSpan, SAMPLING_MECHANISM_APPSEC)
133
132
 
134
133
  standalone.sample(rootSpan)
135
134
  }
@@ -153,7 +152,11 @@ function reportAttack (attackData) {
153
152
  rootSpan.addTags(newTags)
154
153
  }
155
154
 
156
- function reportSchemas (derivatives) {
155
+ function isFingerprintDerivative (derivative) {
156
+ return derivative.startsWith('_dd.appsec.fp')
157
+ }
158
+
159
+ function reportDerivatives (derivatives) {
157
160
  if (!derivatives) return
158
161
 
159
162
  const req = storage.getStore()?.req
@@ -162,9 +165,12 @@ function reportSchemas (derivatives) {
162
165
  if (!rootSpan) return
163
166
 
164
167
  const tags = {}
165
- for (const [address, value] of Object.entries(derivatives)) {
166
- const gzippedValue = zlib.gzipSync(JSON.stringify(value))
167
- tags[address] = gzippedValue.toString('base64')
168
+ for (let [tag, value] of Object.entries(derivatives)) {
169
+ if (!isFingerprintDerivative(tag)) {
170
+ const gzippedValue = zlib.gzipSync(JSON.stringify(value))
171
+ value = gzippedValue.toString('base64')
172
+ }
173
+ tags[tag] = value
168
174
  }
169
175
 
170
176
  rootSpan.addTags(tags)
@@ -177,6 +183,8 @@ function finishRequest (req, res) {
177
183
  if (metricsQueue.size) {
178
184
  rootSpan.addTags(Object.fromEntries(metricsQueue))
179
185
 
186
+ keepTrace(rootSpan, SAMPLING_MECHANISM_APPSEC)
187
+
180
188
  standalone.sample(rootSpan)
181
189
 
182
190
  metricsQueue.clear()
@@ -248,7 +256,7 @@ module.exports = {
248
256
  reportMetrics,
249
257
  reportAttack,
250
258
  reportWafUpdate: incrementWafUpdatesMetric,
251
- reportSchemas,
259
+ reportDerivatives,
252
260
  finishRequest,
253
261
  setRateLimit,
254
262
  mapHeaderAndTags
@@ -2,9 +2,11 @@
2
2
 
3
3
  const log = require('../../log')
4
4
  const { getRootSpan } = require('./utils')
5
- const { MANUAL_KEEP } = require('../../../../../ext/tags')
6
5
  const { setUserTags } = require('./set_user')
7
6
  const standalone = require('../standalone')
7
+ const waf = require('../waf')
8
+ const { SAMPLING_MECHANISM_APPSEC } = require('../../constants')
9
+ const { keepTrace } = require('../../priority_sampler')
8
10
 
9
11
  function trackUserLoginSuccessEvent (tracer, user, metadata) {
10
12
  // TODO: better user check here and in _setUser() ?
@@ -54,9 +56,10 @@ function trackEvent (eventName, fields, sdkMethodName, rootSpan, mode) {
54
56
  return
55
57
  }
56
58
 
59
+ keepTrace(rootSpan, SAMPLING_MECHANISM_APPSEC)
60
+
57
61
  const tags = {
58
- [`appsec.events.${eventName}.track`]: 'true',
59
- [MANUAL_KEEP]: 'true'
62
+ [`appsec.events.${eventName}.track`]: 'true'
60
63
  }
61
64
 
62
65
  if (mode === 'sdk') {
@@ -76,6 +79,10 @@ function trackEvent (eventName, fields, sdkMethodName, rootSpan, mode) {
76
79
  rootSpan.addTags(tags)
77
80
 
78
81
  standalone.sample(rootSpan)
82
+
83
+ if (['users.login.success', 'users.login.failure'].includes(eventName)) {
84
+ waf.run({ persistent: { [`server.business_logic.${eventName}`]: null } })
85
+ }
79
86
  }
80
87
 
81
88
  module.exports = {
@@ -93,7 +93,7 @@ class WAFContextWrapper {
93
93
  Reporter.reportAttack(JSON.stringify(result.events))
94
94
  }
95
95
 
96
- Reporter.reportSchemas(result.derivatives)
96
+ Reporter.reportDerivatives(result.derivatives)
97
97
 
98
98
  if (wafRunFinished.hasSubscribers) {
99
99
  wafRunFinished.publish({ payload })
@@ -51,6 +51,10 @@ class WAFManager {
51
51
  update (newRules) {
52
52
  this.ddwaf.update(newRules)
53
53
 
54
+ if (this.ddwaf.diagnostics.ruleset_version) {
55
+ this.rulesVersion = this.ddwaf.diagnostics.ruleset_version
56
+ }
57
+
54
58
  Reporter.reportWafUpdate(this.ddwafVersion, this.rulesVersion)
55
59
  }
56
60