dd-trace 5.18.0 → 5.20.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 (75) hide show
  1. package/LICENSE-3rdparty.csv +0 -2
  2. package/ext/formats.d.ts +1 -0
  3. package/ext/formats.js +2 -1
  4. package/index.d.ts +61 -39
  5. package/init.js +3 -15
  6. package/package.json +9 -12
  7. package/packages/datadog-instrumentations/src/child_process.js +2 -2
  8. package/packages/datadog-instrumentations/src/fs.js +1 -1
  9. package/packages/datadog-instrumentations/src/hapi.js +1 -1
  10. package/packages/datadog-instrumentations/src/helpers/register.js +13 -11
  11. package/packages/datadog-instrumentations/src/http/client.js +8 -2
  12. package/packages/datadog-instrumentations/src/http/server.js +50 -13
  13. package/packages/datadog-instrumentations/src/jest.js +17 -2
  14. package/packages/datadog-instrumentations/src/kafkajs.js +1 -1
  15. package/packages/datadog-instrumentations/src/ldapjs.js +2 -2
  16. package/packages/datadog-instrumentations/src/mocha/main.js +21 -8
  17. package/packages/datadog-instrumentations/src/mquery.js +2 -2
  18. package/packages/datadog-instrumentations/src/next.js +1 -1
  19. package/packages/datadog-instrumentations/src/pg.js +2 -2
  20. package/packages/datadog-instrumentations/src/playwright.js +46 -32
  21. package/packages/datadog-instrumentations/src/process.js +29 -0
  22. package/packages/datadog-instrumentations/src/restify.js +1 -1
  23. package/packages/datadog-instrumentations/src/vitest.js +98 -28
  24. package/packages/datadog-plugin-aws-sdk/src/base.js +16 -2
  25. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -1
  26. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
  27. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +1 -1
  28. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +3 -3
  29. package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +1 -1
  30. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -4
  31. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +114 -48
  32. package/packages/datadog-plugin-cypress/src/plugin.js +4 -3
  33. package/packages/datadog-plugin-fs/src/index.js +1 -1
  34. package/packages/datadog-plugin-jest/src/index.js +7 -1
  35. package/packages/datadog-plugin-kafkajs/src/producer.js +1 -1
  36. package/packages/datadog-plugin-mongodb-core/src/index.js +1 -1
  37. package/packages/datadog-plugin-openai/src/index.js +5 -5
  38. package/packages/datadog-plugin-playwright/src/index.js +4 -1
  39. package/packages/datadog-plugin-sharedb/src/index.js +1 -1
  40. package/packages/datadog-plugin-vitest/src/index.js +19 -7
  41. package/packages/dd-trace/src/analytics_sampler.js +1 -1
  42. package/packages/dd-trace/src/appsec/blocking.js +10 -1
  43. package/packages/dd-trace/src/appsec/channels.js +4 -1
  44. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  45. package/packages/dd-trace/src/appsec/iast/analyzers/code-injection-analyzer.js +16 -0
  46. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +1 -1
  47. package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +2 -0
  48. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +2 -1
  49. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +2 -2
  50. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +2 -2
  51. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +11 -0
  52. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/code-injection-sensitive-analyzer.js +25 -0
  53. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +2 -0
  54. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +2 -2
  55. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +3 -1
  56. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
  57. package/packages/dd-trace/src/appsec/index.js +15 -10
  58. package/packages/dd-trace/src/appsec/passport.js +1 -1
  59. package/packages/dd-trace/src/appsec/rasp.js +121 -7
  60. package/packages/dd-trace/src/appsec/recommended.json +220 -2
  61. package/packages/dd-trace/src/appsec/reporter.js +0 -4
  62. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +1 -1
  63. package/packages/dd-trace/src/config.js +68 -66
  64. package/packages/dd-trace/src/data_streams.js +44 -0
  65. package/packages/dd-trace/src/datastreams/pathway.js +4 -2
  66. package/packages/dd-trace/src/datastreams/processor.js +1 -1
  67. package/packages/dd-trace/src/log/index.js +32 -0
  68. package/packages/dd-trace/src/opentelemetry/span.js +1 -1
  69. package/packages/dd-trace/src/opentelemetry/tracer.js +6 -0
  70. package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +43 -0
  71. package/packages/dd-trace/src/opentracing/tracer.js +10 -6
  72. package/packages/dd-trace/src/plugins/ci_plugin.js +9 -2
  73. package/packages/dd-trace/src/plugins/plugin.js +12 -1
  74. package/packages/dd-trace/src/proxy.js +1 -0
  75. package/packages/dd-trace/src/tracer.js +2 -0
@@ -5,6 +5,7 @@ const vulnerabilities = require('../../vulnerabilities')
5
5
 
6
6
  const { contains, intersects, remove } = require('./range-utils')
7
7
 
8
+ const codeInjectionSensitiveAnalyzer = require('./sensitive-analyzers/code-injection-sensitive-analyzer')
8
9
  const commandSensitiveAnalyzer = require('./sensitive-analyzers/command-sensitive-analyzer')
9
10
  const hardcodedPasswordAnalyzer = require('./sensitive-analyzers/hardcoded-password-analyzer')
10
11
  const headerSensitiveAnalyzer = require('./sensitive-analyzers/header-sensitive-analyzer')
@@ -23,6 +24,7 @@ class SensitiveHandler {
23
24
  this._valuePattern = new RegExp(DEFAULT_IAST_REDACTION_VALUE_PATTERN, 'gmi')
24
25
 
25
26
  this._sensitiveAnalyzers = new Map()
27
+ this._sensitiveAnalyzers.set(vulnerabilities.CODE_INJECTION, codeInjectionSensitiveAnalyzer)
26
28
  this._sensitiveAnalyzers.set(vulnerabilities.COMMAND_INJECTION, commandSensitiveAnalyzer)
27
29
  this._sensitiveAnalyzers.set(vulnerabilities.NOSQL_MONGODB_INJECTION, jsonSensitiveAnalyzer)
28
30
  this._sensitiveAnalyzers.set(vulnerabilities.LDAP_INJECTION, ldapSensitiveAnalyzer)
@@ -1,7 +1,7 @@
1
1
  // eslint-disable-next-line max-len
2
- const DEFAULT_IAST_REDACTION_NAME_PATTERN = '(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?|access_?|secret_?)key(?:_?id)?|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)'
2
+ const DEFAULT_IAST_REDACTION_NAME_PATTERN = '(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?|access_?|secret_?)key(?:_?id)?|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?|(?:sur|last)name|user(?:name)?|address|e?mail)'
3
3
  // eslint-disable-next-line max-len
4
- const DEFAULT_IAST_REDACTION_VALUE_PATTERN = '(?:bearer\\s+[a-z0-9\\._\\-]+|glpat-[\\w\\-]{20}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L][\\w=\\-]+\\.ey[I-L][\\w=\\-]+(?:\\.[\\w.+/=\\-]+)?|(?:[\\-]{5}BEGIN[a-z\\s]+PRIVATE\\sKEY[\\-]{5}[^\\-]+[\\-]{5}END[a-z\\s]+PRIVATE\\sKEY[\\-]{5}|ssh-rsa\\s*[a-z0-9/\\.+]{100,}))'
4
+ const DEFAULT_IAST_REDACTION_VALUE_PATTERN = '(?:bearer\\s+[a-z0-9\\._\\-]+|glpat-[\\w\\-]{20}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L][\\w=\\-]+\\.ey[I-L][\\w=\\-]+(?:\\.[\\w.+/=\\-]+)?|(?:[\\-]{5}BEGIN[a-z\\s]+PRIVATE\\sKEY[\\-]{5}[^\\-]+[\\-]{5}END[a-z\\s]+PRIVATE\\sKEY[\\-]{5}|ssh-rsa\\s*[a-z0-9/\\.+]{100,})|[\\w\\.-]+@[a-zA-Z\\d\\.-]+\\.[a-zA-Z]{2,})'
5
5
 
6
6
  module.exports = {
7
7
  DEFAULT_IAST_REDACTION_NAME_PATTERN,
@@ -43,6 +43,8 @@ class VulnerabilityFormatter {
43
43
  const valueParts = []
44
44
  let fromIndex = 0
45
45
 
46
+ if (evidence.value == null) return { valueParts }
47
+
46
48
  if (typeof evidence.value === 'object' && evidence.rangesToApply) {
47
49
  const { value, ranges } = stringifyWithRanges(evidence.value, evidence.rangesToApply)
48
50
  evidence.value = value
@@ -69,7 +71,7 @@ class VulnerabilityFormatter {
69
71
  }
70
72
 
71
73
  formatEvidence (type, evidence, sourcesIndexes, sources) {
72
- if (typeof evidence.value === 'undefined') {
74
+ if (evidence.value === undefined) {
73
75
  return undefined
74
76
  }
75
77
 
@@ -1,5 +1,6 @@
1
1
  module.exports = {
2
2
  COMMAND_INJECTION: 'COMMAND_INJECTION',
3
+ CODE_INJECTION: 'CODE_INJECTION',
3
4
  HARDCODED_PASSWORD: 'HARDCODED_PASSWORD',
4
5
  HARDCODED_SECRET: 'HARDCODED_SECRET',
5
6
  HEADER_INJECTION: 'HEADER_INJECTION',
@@ -13,7 +13,8 @@ const {
13
13
  nextBodyParsed,
14
14
  nextQueryParsed,
15
15
  responseBody,
16
- responseWriteHead
16
+ responseWriteHead,
17
+ responseSetHeader
17
18
  } = require('./channels')
18
19
  const waf = require('./waf')
19
20
  const addresses = require('./addresses')
@@ -23,7 +24,7 @@ const apiSecuritySampler = require('./api_security_sampler')
23
24
  const web = require('../plugins/util/web')
24
25
  const { extractIp } = require('../plugins/util/ip_extractor')
25
26
  const { HTTP_CLIENT_IP } = require('../../../../ext/tags')
26
- const { block, setTemplates, getBlockingAction } = require('./blocking')
27
+ const { isBlocked, block, setTemplates, getBlockingAction } = require('./blocking')
27
28
  const { passportTrackEvent } = require('./passport')
28
29
  const { storage } = require('../../../datadog-core')
29
30
  const graphql = require('./graphql')
@@ -62,6 +63,7 @@ function enable (_config) {
62
63
  cookieParser.subscribe(onRequestCookieParser)
63
64
  responseBody.subscribe(onResponseBody)
64
65
  responseWriteHead.subscribe(onResponseWriteHead)
66
+ responseSetHeader.subscribe(onResponseSetHeader)
65
67
 
66
68
  if (_config.appsec.eventTracking.enabled) {
67
69
  passportVerify.subscribe(onPassportVerify)
@@ -121,16 +123,16 @@ function incomingHttpEndTranslator ({ req, res }) {
121
123
  }
122
124
 
123
125
  // TODO: temporary express instrumentation, will use express plugin later
124
- if (req.params && typeof req.params === 'object') {
126
+ if (req.params !== null && typeof req.params === 'object') {
125
127
  persistent[addresses.HTTP_INCOMING_PARAMS] = req.params
126
128
  }
127
129
 
128
130
  // we need to keep this to support other cookie parsers
129
- if (req.cookies && typeof req.cookies === 'object') {
131
+ if (req.cookies !== null && typeof req.cookies === 'object') {
130
132
  persistent[addresses.HTTP_INCOMING_COOKIES] = req.cookies
131
133
  }
132
134
 
133
- if (req.query && typeof req.query === 'object') {
135
+ if (req.query !== null && typeof req.query === 'object') {
134
136
  persistent[addresses.HTTP_INCOMING_QUERY] = req.query
135
137
  }
136
138
 
@@ -223,11 +225,10 @@ function onPassportVerify ({ credentials, user }) {
223
225
  }
224
226
 
225
227
  const responseAnalyzedSet = new WeakSet()
226
- const responseBlockedSet = new WeakSet()
227
228
 
228
229
  function onResponseWriteHead ({ req, res, abortController, statusCode, responseHeaders }) {
229
230
  // avoid "write after end" error
230
- if (responseBlockedSet.has(res)) {
231
+ if (isBlocked(res)) {
231
232
  abortController?.abort()
232
233
  return
233
234
  }
@@ -255,15 +256,18 @@ function onResponseWriteHead ({ req, res, abortController, statusCode, responseH
255
256
  handleResults(results, req, res, rootSpan, abortController)
256
257
  }
257
258
 
259
+ function onResponseSetHeader ({ res, abortController }) {
260
+ if (isBlocked(res)) {
261
+ abortController?.abort()
262
+ }
263
+ }
264
+
258
265
  function handleResults (actions, req, res, rootSpan, abortController) {
259
266
  if (!actions || !req || !res || !rootSpan || !abortController) return
260
267
 
261
268
  const blockingAction = getBlockingAction(actions)
262
269
  if (blockingAction) {
263
270
  block(req, res, rootSpan, abortController, blockingAction)
264
- if (!abortController.signal || abortController.signal.aborted) {
265
- responseBlockedSet.add(res)
266
- }
267
271
  }
268
272
  }
269
273
 
@@ -290,6 +294,7 @@ function disable () {
290
294
  if (responseBody.hasSubscribers) responseBody.unsubscribe(onResponseBody)
291
295
  if (passportVerify.hasSubscribers) passportVerify.unsubscribe(onPassportVerify)
292
296
  if (responseWriteHead.hasSubscribers) responseWriteHead.unsubscribe(onResponseWriteHead)
297
+ if (responseSetHeader.hasSubscribers) responseSetHeader.unsubscribe(onResponseSetHeader)
293
298
  }
294
299
 
295
300
  module.exports = {
@@ -13,7 +13,7 @@ const regexSdkEvent = new RegExp(SDK_USER_EVENT_PATTERN, 'i')
13
13
  function isSdkCalled (tags) {
14
14
  let called = false
15
15
 
16
- if (tags && typeof tags === 'object') {
16
+ if (tags !== null && typeof tags === 'object') {
17
17
  called = Object.entries(tags).some(([key, value]) => regexSdkEvent.test(key) && value === 'true')
18
18
  }
19
19
 
@@ -3,23 +3,118 @@
3
3
  const { storage } = require('../../../datadog-core')
4
4
  const web = require('./../plugins/util/web')
5
5
  const addresses = require('./addresses')
6
- const { httpClientRequestStart } = require('./channels')
6
+ const { httpClientRequestStart, setUncaughtExceptionCaptureCallbackStart } = require('./channels')
7
7
  const { reportStackTrace } = require('./stack_trace')
8
8
  const waf = require('./waf')
9
+ const { getBlockingAction, block } = require('./blocking')
10
+ const log = require('../log')
9
11
 
10
12
  const RULE_TYPES = {
11
13
  SSRF: 'ssrf'
12
14
  }
13
15
 
14
- let config
16
+ class DatadogRaspAbortError extends Error {
17
+ constructor (req, res, blockingAction) {
18
+ super('DatadogRaspAbortError')
19
+ this.name = 'DatadogRaspAbortError'
20
+ this.req = req
21
+ this.res = res
22
+ this.blockingAction = blockingAction
23
+ }
24
+ }
25
+
26
+ let config, abortOnUncaughtException
27
+
28
+ function removeAllListeners (emitter, event) {
29
+ const listeners = emitter.listeners(event)
30
+ emitter.removeAllListeners(event)
31
+
32
+ let cleaned = false
33
+ return function () {
34
+ if (cleaned === true) {
35
+ return
36
+ }
37
+ cleaned = true
38
+
39
+ for (let i = 0; i < listeners.length; ++i) {
40
+ emitter.on(event, listeners[i])
41
+ }
42
+ }
43
+ }
44
+
45
+ function findDatadogRaspAbortError (err, deep = 10) {
46
+ if (err instanceof DatadogRaspAbortError) {
47
+ return err
48
+ }
49
+
50
+ if (err.cause && deep > 0) {
51
+ return findDatadogRaspAbortError(err.cause, deep - 1)
52
+ }
53
+ }
54
+
55
+ function handleUncaughtExceptionMonitor (err) {
56
+ const abortError = findDatadogRaspAbortError(err)
57
+ if (!abortError) return
58
+
59
+ const { req, res, blockingAction } = abortError
60
+ block(req, res, web.root(req), null, blockingAction)
61
+
62
+ if (!process.hasUncaughtExceptionCaptureCallback()) {
63
+ const cleanUp = removeAllListeners(process, 'uncaughtException')
64
+ const handler = () => {
65
+ process.removeListener('uncaughtException', handler)
66
+ }
67
+
68
+ setTimeout(() => {
69
+ process.removeListener('uncaughtException', handler)
70
+ cleanUp()
71
+ })
72
+
73
+ process.on('uncaughtException', handler)
74
+ } else {
75
+ // uncaughtException event is not executed when hasUncaughtExceptionCaptureCallback is true
76
+ let previousCb
77
+ const cb = ({ currentCallback, abortController }) => {
78
+ setUncaughtExceptionCaptureCallbackStart.unsubscribe(cb)
79
+ if (!currentCallback) {
80
+ abortController.abort()
81
+ return
82
+ }
83
+
84
+ previousCb = currentCallback
85
+ }
86
+
87
+ setUncaughtExceptionCaptureCallbackStart.subscribe(cb)
88
+
89
+ process.setUncaughtExceptionCaptureCallback(null)
90
+
91
+ // For some reason, previous callback was defined before the instrumentation
92
+ // We can not restore it, so we let the app decide
93
+ if (previousCb) {
94
+ process.setUncaughtExceptionCaptureCallback(() => {
95
+ process.setUncaughtExceptionCaptureCallback(null)
96
+ process.setUncaughtExceptionCaptureCallback(previousCb)
97
+ })
98
+ }
99
+ }
100
+ }
15
101
 
16
102
  function enable (_config) {
17
103
  config = _config
18
104
  httpClientRequestStart.subscribe(analyzeSsrf)
105
+
106
+ process.on('uncaughtExceptionMonitor', handleUncaughtExceptionMonitor)
107
+ abortOnUncaughtException = process.execArgv?.includes('--abort-on-uncaught-exception')
108
+
109
+ if (abortOnUncaughtException) {
110
+ log.warn('The --abort-on-uncaught-exception flag is enabled. The RASP module will not block operations.')
111
+ }
19
112
  }
20
113
 
21
114
  function disable () {
22
115
  if (httpClientRequestStart.hasSubscribers) httpClientRequestStart.unsubscribe(analyzeSsrf)
116
+
117
+ process.off('uncaughtExceptionMonitor', handleUncaughtExceptionMonitor)
23
118
  }
24
119
 
25
120
  function analyzeSsrf (ctx) {
@@ -32,17 +127,18 @@ function analyzeSsrf (ctx) {
32
127
  const persistent = {
33
128
  [addresses.HTTP_OUTGOING_URL]: url
34
129
  }
35
- // TODO: Currently this is only monitoring, we should
36
- // block the request if SSRF attempt
130
+
37
131
  const result = waf.run({ persistent }, req, RULE_TYPES.SSRF)
38
- handleResult(result, req)
132
+
133
+ const res = store?.res
134
+ handleResult(result, req, res, ctx.abortController)
39
135
  }
40
136
 
41
137
  function getGenerateStackTraceAction (actions) {
42
138
  return actions?.generate_stack
43
139
  }
44
140
 
45
- function handleResult (actions, req) {
141
+ function handleResult (actions, req, res, abortController) {
46
142
  const generateStackTraceAction = getGenerateStackTraceAction(actions)
47
143
  if (generateStackTraceAction && config.appsec.stackTrace.enabled) {
48
144
  const rootSpan = web.root(req)
@@ -53,10 +149,28 @@ function handleResult (actions, req) {
53
149
  config.appsec.stackTrace.maxStackTraces
54
150
  )
55
151
  }
152
+
153
+ if (!abortController || abortOnUncaughtException) return
154
+
155
+ const blockingAction = getBlockingAction(actions)
156
+ if (blockingAction) {
157
+ const rootSpan = web.root(req)
158
+ // Should block only in express
159
+ if (rootSpan?.context()._name === 'express.request') {
160
+ const abortError = new DatadogRaspAbortError(req, res, blockingAction)
161
+ abortController.abort(abortError)
162
+
163
+ // TODO Delete this when support for node 16 is removed
164
+ if (!abortController.signal.reason) {
165
+ abortController.signal.reason = abortError
166
+ }
167
+ }
168
+ }
56
169
  }
57
170
 
58
171
  module.exports = {
59
172
  enable,
60
173
  disable,
61
- handleResult
174
+ handleResult,
175
+ handleUncaughtExceptionMonitor // exported only for testing purpose
62
176
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": "2.2",
3
3
  "metadata": {
4
- "rules_version": "1.12.0"
4
+ "rules_version": "1.13.0"
5
5
  },
6
6
  "rules": [
7
7
  {
@@ -6285,6 +6285,55 @@
6285
6285
  "stack_trace"
6286
6286
  ]
6287
6287
  },
6288
+ {
6289
+ "id": "rasp-932-100",
6290
+ "name": "Shell injection exploit",
6291
+ "enabled": false,
6292
+ "tags": {
6293
+ "type": "command_injection",
6294
+ "category": "vulnerability_trigger",
6295
+ "cwe": "77",
6296
+ "capec": "1000/152/248/88",
6297
+ "confidence": "0",
6298
+ "module": "rasp"
6299
+ },
6300
+ "conditions": [
6301
+ {
6302
+ "parameters": {
6303
+ "resource": [
6304
+ {
6305
+ "address": "server.sys.shell.cmd"
6306
+ }
6307
+ ],
6308
+ "params": [
6309
+ {
6310
+ "address": "server.request.query"
6311
+ },
6312
+ {
6313
+ "address": "server.request.body"
6314
+ },
6315
+ {
6316
+ "address": "server.request.path_params"
6317
+ },
6318
+ {
6319
+ "address": "grpc.server.request.message"
6320
+ },
6321
+ {
6322
+ "address": "graphql.server.all_resolvers"
6323
+ },
6324
+ {
6325
+ "address": "graphql.server.resolver"
6326
+ }
6327
+ ]
6328
+ },
6329
+ "operator": "shi_detector"
6330
+ }
6331
+ ],
6332
+ "transformers": [],
6333
+ "on_match": [
6334
+ "stack_trace"
6335
+ ]
6336
+ },
6288
6337
  {
6289
6338
  "id": "rasp-934-100",
6290
6339
  "name": "Server-side request forgery exploit",
@@ -8388,6 +8437,57 @@
8388
8437
  }
8389
8438
  ],
8390
8439
  "processors": [
8440
+ {
8441
+ "id": "http-endpoint-fingerprint",
8442
+ "generator": "http_endpoint_fingerprint",
8443
+ "conditions": [
8444
+ {
8445
+ "operator": "exists",
8446
+ "parameters": {
8447
+ "inputs": [
8448
+ {
8449
+ "address": "waf.context.event"
8450
+ },
8451
+ {
8452
+ "address": "server.business_logic.users.login.failure"
8453
+ },
8454
+ {
8455
+ "address": "server.business_logic.users.login.success"
8456
+ }
8457
+ ]
8458
+ }
8459
+ }
8460
+ ],
8461
+ "parameters": {
8462
+ "mappings": [
8463
+ {
8464
+ "method": [
8465
+ {
8466
+ "address": "server.request.method"
8467
+ }
8468
+ ],
8469
+ "uri_raw": [
8470
+ {
8471
+ "address": "server.request.uri.raw"
8472
+ }
8473
+ ],
8474
+ "body": [
8475
+ {
8476
+ "address": "server.request.body"
8477
+ }
8478
+ ],
8479
+ "query": [
8480
+ {
8481
+ "address": "server.request.query"
8482
+ }
8483
+ ],
8484
+ "output": "_dd.appsec.fp.http.endpoint"
8485
+ }
8486
+ ]
8487
+ },
8488
+ "evaluate": false,
8489
+ "output": true
8490
+ },
8391
8491
  {
8392
8492
  "id": "extract-content",
8393
8493
  "generator": "extract_schema",
@@ -8537,6 +8637,124 @@
8537
8637
  },
8538
8638
  "evaluate": false,
8539
8639
  "output": true
8640
+ },
8641
+ {
8642
+ "id": "http-header-fingerprint",
8643
+ "generator": "http_header_fingerprint",
8644
+ "conditions": [
8645
+ {
8646
+ "operator": "exists",
8647
+ "parameters": {
8648
+ "inputs": [
8649
+ {
8650
+ "address": "waf.context.event"
8651
+ },
8652
+ {
8653
+ "address": "server.business_logic.users.login.failure"
8654
+ },
8655
+ {
8656
+ "address": "server.business_logic.users.login.success"
8657
+ }
8658
+ ]
8659
+ }
8660
+ }
8661
+ ],
8662
+ "parameters": {
8663
+ "mappings": [
8664
+ {
8665
+ "headers": [
8666
+ {
8667
+ "address": "server.request.headers.no_cookies"
8668
+ }
8669
+ ],
8670
+ "output": "_dd.appsec.fp.http.header"
8671
+ }
8672
+ ]
8673
+ },
8674
+ "evaluate": false,
8675
+ "output": true
8676
+ },
8677
+ {
8678
+ "id": "http-network-fingerprint",
8679
+ "generator": "http_network_fingerprint",
8680
+ "conditions": [
8681
+ {
8682
+ "operator": "exists",
8683
+ "parameters": {
8684
+ "inputs": [
8685
+ {
8686
+ "address": "waf.context.event"
8687
+ },
8688
+ {
8689
+ "address": "server.business_logic.users.login.failure"
8690
+ },
8691
+ {
8692
+ "address": "server.business_logic.users.login.success"
8693
+ }
8694
+ ]
8695
+ }
8696
+ }
8697
+ ],
8698
+ "parameters": {
8699
+ "mappings": [
8700
+ {
8701
+ "headers": [
8702
+ {
8703
+ "address": "server.request.headers.no_cookies"
8704
+ }
8705
+ ],
8706
+ "output": "_dd.appsec.fp.http.network"
8707
+ }
8708
+ ]
8709
+ },
8710
+ "evaluate": false,
8711
+ "output": true
8712
+ },
8713
+ {
8714
+ "id": "session-fingerprint",
8715
+ "generator": "session_fingerprint",
8716
+ "conditions": [
8717
+ {
8718
+ "operator": "exists",
8719
+ "parameters": {
8720
+ "inputs": [
8721
+ {
8722
+ "address": "waf.context.event"
8723
+ },
8724
+ {
8725
+ "address": "server.business_logic.users.login.failure"
8726
+ },
8727
+ {
8728
+ "address": "server.business_logic.users.login.success"
8729
+ }
8730
+ ]
8731
+ }
8732
+ }
8733
+ ],
8734
+ "parameters": {
8735
+ "mappings": [
8736
+ {
8737
+ "cookies": [
8738
+ {
8739
+ "address": "server.request.cookies"
8740
+ }
8741
+ ],
8742
+ "session_id": [
8743
+ {
8744
+ "address": "usr.session_id"
8745
+ }
8746
+ ],
8747
+ "user_id": [
8748
+ {
8749
+ "address": "usr.id"
8750
+ }
8751
+ ],
8752
+ "output": "_dd.appsec.fp.session"
8753
+ }
8754
+ ]
8755
+ },
8756
+ "evaluate": false,
8757
+ "output": true
8540
8758
  }
8541
8759
  ],
8542
8760
  "scanners": [
@@ -9562,4 +9780,4 @@
9562
9780
  }
9563
9781
  }
9564
9782
  ]
9565
- }
9783
+ }
@@ -207,10 +207,6 @@ function finishRequest (req, res) {
207
207
 
208
208
  // collect some headers even when no attack is detected
209
209
  const mandatoryTags = filterHeaders(req.headers, REQUEST_HEADERS_MAP)
210
- const ua = mandatoryTags['http.request.headers.user-agent']
211
- if (ua) {
212
- mandatoryTags['http.useragent'] = ua
213
- }
214
210
  rootSpan.addTags(mandatoryTags)
215
211
 
216
212
  const tags = rootSpan.context()._tags
@@ -25,7 +25,7 @@ class WAFContextWrapper {
25
25
  const inputs = {}
26
26
  const newAddressesToSkip = new Set(this.addressesToSkip)
27
27
 
28
- if (persistent && typeof persistent === 'object') {
28
+ if (persistent !== null && typeof persistent === 'object') {
29
29
  // TODO: possible optimization: only send params that haven't already been sent with same value to this wafContext
30
30
  for (const key of Object.keys(persistent)) {
31
31
  // TODO: requiredAddresses is no longer used due to processor addresses are not included in the list. Check on