dd-trace 4.51.1 → 4.53.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 (172) hide show
  1. package/LICENSE-3rdparty.csv +8 -2
  2. package/ci/init.js +16 -0
  3. package/index.d.ts +31 -13
  4. package/init.js +4 -66
  5. package/initialize.mjs +13 -10
  6. package/loader-hook.mjs +4 -0
  7. package/package.json +16 -11
  8. package/packages/datadog-core/src/storage.js +39 -2
  9. package/packages/datadog-instrumentations/src/aerospike.js +1 -1
  10. package/packages/datadog-instrumentations/src/azure-functions.js +1 -1
  11. package/packages/datadog-instrumentations/src/cucumber.js +29 -3
  12. package/packages/datadog-instrumentations/src/express.js +38 -4
  13. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +3 -3
  14. package/packages/datadog-instrumentations/src/helpers/hooks.js +0 -1
  15. package/packages/datadog-instrumentations/src/helpers/register.js +3 -4
  16. package/packages/datadog-instrumentations/src/http/client.js +1 -1
  17. package/packages/datadog-instrumentations/src/jest.js +27 -8
  18. package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
  19. package/packages/datadog-instrumentations/src/mysql2.js +13 -8
  20. package/packages/datadog-instrumentations/src/next.js +7 -4
  21. package/packages/datadog-instrumentations/src/passport-http.js +2 -14
  22. package/packages/datadog-instrumentations/src/passport-local.js +2 -14
  23. package/packages/datadog-instrumentations/src/passport-utils.js +43 -19
  24. package/packages/datadog-instrumentations/src/pg.js +6 -6
  25. package/packages/datadog-instrumentations/src/playwright.js +17 -4
  26. package/packages/datadog-instrumentations/src/router.js +97 -1
  27. package/packages/datadog-instrumentations/src/sequelize.js +9 -4
  28. package/packages/datadog-instrumentations/src/url.js +4 -0
  29. package/packages/datadog-instrumentations/src/vitest.js +27 -2
  30. package/packages/datadog-plugin-avsc/src/schema_iterator.js +8 -3
  31. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +154 -0
  32. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -1
  33. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
  34. package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +1 -1
  35. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -1
  36. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -1
  37. package/packages/datadog-plugin-aws-sdk/src/util.js +92 -0
  38. package/packages/datadog-plugin-azure-functions/src/index.js +1 -1
  39. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -1
  40. package/packages/datadog-plugin-cucumber/src/index.js +39 -4
  41. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +3 -3
  42. package/packages/datadog-plugin-grpc/src/client.js +2 -2
  43. package/packages/datadog-plugin-grpc/src/util.js +1 -1
  44. package/packages/datadog-plugin-jest/src/index.js +39 -4
  45. package/packages/datadog-plugin-langchain/src/handlers/language_models/chat_model.js +1 -1
  46. package/packages/datadog-plugin-langchain/src/handlers/language_models/llm.js +1 -1
  47. package/packages/datadog-plugin-mocha/src/index.js +36 -2
  48. package/packages/datadog-plugin-oracledb/src/index.js +1 -1
  49. package/packages/datadog-plugin-vitest/src/index.js +34 -2
  50. package/packages/datadog-shimmer/src/shimmer.js +8 -4
  51. package/packages/dd-trace/src/appsec/addresses.js +3 -0
  52. package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
  53. package/packages/dd-trace/src/appsec/blocked_templates.js +1 -1
  54. package/packages/dd-trace/src/appsec/blocking.js +1 -1
  55. package/packages/dd-trace/src/appsec/channels.js +1 -0
  56. package/packages/dd-trace/src/appsec/iast/analyzers/code-injection-analyzer.js +4 -0
  57. package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +2 -2
  58. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-rules.js +1 -1
  59. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-rules.js +1 -1
  60. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +1 -1
  61. package/packages/dd-trace/src/appsec/iast/analyzers/injection-analyzer.js +10 -3
  62. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +4 -0
  63. package/packages/dd-trace/src/appsec/iast/analyzers/template-injection-analyzer.js +4 -0
  64. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +8 -21
  65. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +3 -3
  66. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +2 -2
  67. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +64 -3
  68. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +5 -8
  69. package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +2 -1
  70. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +7 -11
  71. package/packages/dd-trace/src/appsec/iast/telemetry/namespaces.js +2 -3
  72. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +2 -2
  73. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +2 -2
  74. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +2 -2
  75. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +2 -2
  76. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +3 -3
  77. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +2 -2
  78. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
  79. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +32 -37
  80. package/packages/dd-trace/src/appsec/index.js +18 -13
  81. package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +2 -2
  82. package/packages/dd-trace/src/appsec/rasp/utils.js +1 -1
  83. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +1 -0
  84. package/packages/dd-trace/src/appsec/remote_config/index.js +25 -1
  85. package/packages/dd-trace/src/appsec/remote_config/manager.js +2 -2
  86. package/packages/dd-trace/src/appsec/reporter.js +3 -1
  87. package/packages/dd-trace/src/appsec/sdk/set_user.js +2 -2
  88. package/packages/dd-trace/src/appsec/sdk/track_event.js +37 -24
  89. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +4 -4
  90. package/packages/dd-trace/src/appsec/telemetry.js +10 -0
  91. package/packages/dd-trace/src/appsec/user_tracking.js +168 -0
  92. package/packages/dd-trace/src/appsec/waf/index.js +2 -2
  93. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +2 -3
  94. package/packages/dd-trace/src/appsec/waf/waf_manager.js +1 -1
  95. package/packages/dd-trace/src/azure_metadata.js +4 -4
  96. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +5 -4
  97. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +39 -3
  98. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
  99. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
  100. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +1 -1
  101. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
  102. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +29 -9
  103. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
  104. package/packages/dd-trace/src/config.js +24 -32
  105. package/packages/dd-trace/src/constants.js +1 -0
  106. package/packages/dd-trace/src/crashtracking/crashtracker.js +3 -2
  107. package/packages/dd-trace/src/datastreams/processor.js +4 -6
  108. package/packages/dd-trace/src/datastreams/writer.js +6 -5
  109. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +80 -0
  110. package/packages/dd-trace/src/debugger/devtools_client/config.js +3 -1
  111. package/packages/dd-trace/src/debugger/devtools_client/defaults.js +6 -0
  112. package/packages/dd-trace/src/debugger/devtools_client/index.js +63 -8
  113. package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +10 -67
  114. package/packages/dd-trace/src/debugger/devtools_client/send.js +2 -1
  115. package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -1
  116. package/packages/dd-trace/src/debugger/devtools_client/status.js +4 -4
  117. package/packages/dd-trace/src/debugger/index.js +14 -10
  118. package/packages/dd-trace/src/dogstatsd.js +2 -2
  119. package/packages/dd-trace/src/encode/0.4.js +23 -78
  120. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +0 -32
  121. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +1 -2
  122. package/packages/dd-trace/src/encode/span-stats.js +0 -30
  123. package/packages/dd-trace/src/exporters/agent/writer.js +3 -3
  124. package/packages/dd-trace/src/exporters/common/request.js +1 -1
  125. package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -1
  126. package/packages/dd-trace/src/flare/index.js +1 -1
  127. package/packages/dd-trace/src/guardrails/index.js +64 -0
  128. package/packages/dd-trace/src/guardrails/log.js +32 -0
  129. package/packages/dd-trace/src/guardrails/telemetry.js +78 -0
  130. package/packages/dd-trace/src/guardrails/util.js +10 -0
  131. package/packages/dd-trace/src/lambda/runtime/ritm.js +2 -2
  132. package/packages/dd-trace/src/llmobs/storage.js +2 -3
  133. package/packages/dd-trace/src/llmobs/writers/base.js +2 -2
  134. package/packages/dd-trace/src/{encode → msgpack}/chunk.js +8 -5
  135. package/packages/dd-trace/src/msgpack/encoder.js +309 -0
  136. package/packages/dd-trace/src/msgpack/index.js +6 -0
  137. package/packages/dd-trace/src/opentelemetry/context_manager.js +2 -2
  138. package/packages/dd-trace/src/opentracing/propagation/text_map.js +12 -9
  139. package/packages/dd-trace/src/opentracing/span.js +1 -1
  140. package/packages/dd-trace/src/opentracing/tracer.js +2 -2
  141. package/packages/dd-trace/src/plugin_manager.js +4 -2
  142. package/packages/dd-trace/src/plugins/ci_plugin.js +47 -4
  143. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  144. package/packages/dd-trace/src/plugins/tracing.js +1 -1
  145. package/packages/dd-trace/src/plugins/util/git.js +7 -7
  146. package/packages/dd-trace/src/plugins/util/test.js +36 -3
  147. package/packages/dd-trace/src/plugins/util/web.js +2 -2
  148. package/packages/dd-trace/src/profiling/config.js +3 -0
  149. package/packages/dd-trace/src/profiling/exporters/agent.js +9 -68
  150. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +76 -0
  151. package/packages/dd-trace/src/profiling/exporters/file.js +8 -4
  152. package/packages/dd-trace/src/profiling/profiler.js +62 -10
  153. package/packages/dd-trace/src/profiling/profilers/event_plugins/event.js +22 -12
  154. package/packages/dd-trace/src/profiling/profilers/events.js +47 -8
  155. package/packages/dd-trace/src/profiling/profilers/wall.js +2 -17
  156. package/packages/dd-trace/src/profiling/webspan-utils.js +23 -0
  157. package/packages/dd-trace/src/proxy.js +7 -2
  158. package/packages/dd-trace/src/runtime_metrics.js +107 -4
  159. package/packages/dd-trace/src/serverless.js +1 -1
  160. package/packages/dd-trace/src/service-naming/schemas/v0/serverless.js +1 -1
  161. package/packages/dd-trace/src/service-naming/schemas/v1/serverless.js +1 -1
  162. package/packages/dd-trace/src/span_processor.js +10 -10
  163. package/packages/dd-trace/src/tagger.js +1 -1
  164. package/packages/dd-trace/src/telemetry/index.js +1 -0
  165. package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
  166. package/packages/dd-trace/src/telemetry/logs/log-collector.js +10 -2
  167. package/packages/dd-trace/src/telemetry/send-data.js +2 -2
  168. package/packages/dd-trace/src/util.js +5 -16
  169. package/packages/datadog-instrumentations/src/qs.js +0 -24
  170. package/packages/dd-trace/src/appsec/iast/iast-log.js +0 -86
  171. package/packages/dd-trace/src/appsec/passport.js +0 -110
  172. package/packages/dd-trace/src/telemetry/init-telemetry.js +0 -75
@@ -4,10 +4,10 @@ const dc = require('dc-polyfill')
4
4
  const TaintedUtils = require('@datadog/native-iast-taint-tracking')
5
5
  const { storage } = require('../../../../../datadog-core')
6
6
  const iastContextFunctions = require('../iast-context')
7
- const iastLog = require('../iast-log')
8
7
  const { EXECUTED_PROPAGATION } = require('../telemetry/iast-metric')
9
8
  const { isDebugAllowed } = require('../telemetry/verbosity')
10
9
  const { taintObject } = require('./operations-taint-object')
10
+ const log = require('../../../log')
11
11
 
12
12
  const mathRandomCallCh = dc.channel('datadog:random:call')
13
13
  const evalCallCh = dc.channel('datadog:eval:call')
@@ -60,8 +60,7 @@ function getFilteredCsiFn (cb, filter, getContext) {
60
60
  return cb(transactionId, res, target, ...rest)
61
61
  }
62
62
  } catch (e) {
63
- iastLog.error(`Error invoking CSI ${target}`)
64
- .errorAndPublish(e)
63
+ log.error('[ASM] Error invoking CSI %s', target, e)
65
64
  }
66
65
  return res
67
66
  }
@@ -112,8 +111,7 @@ function csiMethodsOverrides (getContext) {
112
111
  return TaintedUtils.concat(transactionId, res, op1, op2)
113
112
  }
114
113
  } catch (e) {
115
- iastLog.error('Error invoking CSI plusOperator')
116
- .errorAndPublish(e)
114
+ log.error('[ASM] Error invoking CSI plusOperator', e)
117
115
  }
118
116
  return res
119
117
  },
@@ -126,8 +124,7 @@ function csiMethodsOverrides (getContext) {
126
124
  return TaintedUtils.concat(transactionId, res, ...rest)
127
125
  }
128
126
  } catch (e) {
129
- iastLog.error('Error invoking CSI tplOperator')
130
- .errorAndPublish(e)
127
+ log.error('[ASM] Error invoking CSI tplOperator', e)
131
128
  }
132
129
  return res
133
130
  },
@@ -178,7 +175,7 @@ function csiMethodsOverrides (getContext) {
178
175
  }
179
176
  }
180
177
  } catch (e) {
181
- iastLog.error(e)
178
+ log.error('[ASM] Error invoking CSI JSON.parse', e)
182
179
  }
183
180
  }
184
181
 
@@ -194,7 +191,7 @@ function csiMethodsOverrides (getContext) {
194
191
  res = TaintedUtils.arrayJoin(transactionId, res, target, separator)
195
192
  }
196
193
  } catch (e) {
197
- iastLog.error(e)
194
+ log.error('[ASM] Error invoking CSI join', e)
198
195
  }
199
196
  }
200
197
 
@@ -250,8 +247,7 @@ function lodashTaintTrackingHandler (message) {
250
247
  message.result = getLodashTaintedUtilFn(message.operation)(transactionId, message.result, ...message.arguments)
251
248
  }
252
249
  } catch (e) {
253
- iastLog.error(`Error invoking CSI lodash ${message.operation}`)
254
- .errorAndPublish(e)
250
+ log.error('[ASM] Error invoking CSI lodash %s', message.operation, e)
255
251
  }
256
252
  }
257
253
 
@@ -4,7 +4,6 @@ const log = require('../../../log')
4
4
  const { Namespace } = require('../../../telemetry/metrics')
5
5
  const { addMetricsToSpan } = require('./span-tags')
6
6
  const { IAST_TRACE_METRIC_PREFIX } = require('../tags')
7
- const iastLog = require('../iast-log')
8
7
 
9
8
  const DD_IAST_METRICS_NAMESPACE = Symbol('_dd.iast.request.metrics.namespace')
10
9
 
@@ -31,7 +30,7 @@ function finalizeRequestNamespace (context, rootSpan) {
31
30
 
32
31
  namespace.clear()
33
32
  } catch (e) {
34
- log.error(e)
33
+ log.error('[ASM] Error merging request metrics', e)
35
34
  } finally {
36
35
  if (context) {
37
36
  delete context[DD_IAST_METRICS_NAMESPACE]
@@ -79,7 +78,7 @@ class IastNamespace extends Namespace {
79
78
 
80
79
  if (metrics.size === this.maxMetricTagsSize) {
81
80
  metrics.clear()
82
- iastLog.warnAndPublish(`Tags cache max size reached for metric ${name}`)
81
+ log.error('[ASM] Tags cache max size reached for metric %s', name)
83
82
  }
84
83
 
85
84
  metrics.set(tags, metric)
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const iastLog = require('../../../iast-log')
3
+ const log = require('../../../../../log')
4
4
 
5
5
  const COMMAND_PATTERN = '^(?:\\s*(?:sudo|doas)\\s+)?\\b\\S+\\b\\s(.*)'
6
6
  const pattern = new RegExp(COMMAND_PATTERN, 'gmi')
@@ -16,7 +16,7 @@ module.exports = function extractSensitiveRanges (evidence) {
16
16
  return [{ start, end }]
17
17
  }
18
18
  } catch (e) {
19
- iastLog.debug(e)
19
+ log.debug('[ASM] Error extracting sensitive ranges', e)
20
20
  }
21
21
  return []
22
22
  }
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const iastLog = require('../../../iast-log')
3
+ const log = require('../../../../../log')
4
4
 
5
5
  const LDAP_PATTERN = '\\(.*?(?:~=|=|<=|>=)(?<LITERAL>[^)]+)\\)'
6
6
  const pattern = new RegExp(LDAP_PATTERN, 'gmi')
@@ -22,7 +22,7 @@ module.exports = function extractSensitiveRanges (evidence) {
22
22
  }
23
23
  return tokens
24
24
  } catch (e) {
25
- iastLog.debug(e)
25
+ log.debug('[ASM] Error extracting sensitive ranges', e)
26
26
  }
27
27
  return []
28
28
  }
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const iastLog = require('../../../iast-log')
3
+ const log = require('../../../../../log')
4
4
 
5
5
  const STRING_LITERAL = '\'(?:\'\'|[^\'])*\''
6
6
  const POSTGRESQL_ESCAPED_LITERAL = '\\$([^$]*)\\$.*?\\$\\1\\$'
@@ -106,7 +106,7 @@ module.exports = function extractSensitiveRanges (evidence) {
106
106
  }
107
107
  return tokens
108
108
  } catch (e) {
109
- iastLog.debug(e)
109
+ log.debug('[ASM] Error extracting sensitive ranges', e)
110
110
  }
111
111
  return []
112
112
  }
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const iastLog = require('../../../iast-log')
3
+ const log = require('../../../../../log')
4
4
 
5
5
  const AUTHORITY = '^(?:[^:]+:)?//([^@]+)@'
6
6
  const QUERY_FRAGMENT = '[?#&]([^=&;]+)=([^?#&]+)'
@@ -33,7 +33,7 @@ module.exports = function extractSensitiveRanges (evidence) {
33
33
 
34
34
  return ranges
35
35
  } catch (e) {
36
- iastLog.debug(e)
36
+ log.debug('[ASM] Error extracting sensitive ranges', e)
37
37
  }
38
38
 
39
39
  return []
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const iastLog = require('../../iast-log')
3
+ const log = require('../../../../log')
4
4
  const vulnerabilities = require('../../vulnerabilities')
5
5
 
6
6
  const { contains, intersects, remove } = require('./range-utils')
@@ -282,7 +282,7 @@ class SensitiveHandler {
282
282
  try {
283
283
  this._namePattern = new RegExp(redactionNamePattern, 'gmi')
284
284
  } catch (e) {
285
- iastLog.warn('Redaction name pattern is not valid')
285
+ log.warn('[ASM] Redaction name pattern is not valid')
286
286
  }
287
287
  }
288
288
 
@@ -290,7 +290,7 @@ class SensitiveHandler {
290
290
  try {
291
291
  this._valuePattern = new RegExp(redactionValuePattern, 'gmi')
292
292
  } catch (e) {
293
- iastLog.warn('Redaction value pattern is not valid')
293
+ log.warn('[ASM] Redaction value pattern is not valid')
294
294
  }
295
295
  }
296
296
  }
@@ -1,6 +1,6 @@
1
- // eslint-disable-next-line max-len
1
+ // eslint-disable-next-line @stylistic/js/max-len
2
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
- // eslint-disable-next-line max-len
3
+ // eslint-disable-next-line @stylistic/js/max-len
4
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 +7,7 @@ const STRINGIFY_RANGE_KEY = 'DD_' + crypto.randomBytes(20).toString('hex')
7
7
  const STRINGIFY_SENSITIVE_KEY = STRINGIFY_RANGE_KEY + 'SENSITIVE'
8
8
  const STRINGIFY_SENSITIVE_NOT_STRING_KEY = STRINGIFY_SENSITIVE_KEY + 'NOTSTRING'
9
9
 
10
- // eslint-disable-next-line max-len
10
+ // eslint-disable-next-line @stylistic/js/max-len
11
11
  const KEYS_REGEX_WITH_SENSITIVE_RANGES = new RegExp(`(?:"(${STRINGIFY_RANGE_KEY}_\\d+_))|(?:"(${STRINGIFY_SENSITIVE_KEY}_\\d+_(\\d+)_))|("${STRINGIFY_SENSITIVE_NOT_STRING_KEY}_\\d+_([\\s0-9.a-zA-Z]*)")`, 'gm')
12
12
  const KEYS_REGEX_WITHOUT_SENSITIVE_RANGES = new RegExp(`"(${STRINGIFY_RANGE_KEY}_\\d+_)`, 'gm')
13
13
 
@@ -17,13 +17,36 @@ let resetVulnerabilityCacheTimer
17
17
  let deduplicationEnabled = true
18
18
 
19
19
  function addVulnerability (iastContext, vulnerability) {
20
- if (vulnerability && vulnerability.evidence && vulnerability.type &&
21
- vulnerability.location) {
22
- if (iastContext && iastContext.rootSpan) {
20
+ if (vulnerability?.evidence && vulnerability?.type && vulnerability?.location) {
21
+ if (deduplicationEnabled && isDuplicatedVulnerability(vulnerability)) return
22
+
23
+ VULNERABILITY_HASHES.set(`${vulnerability.type}${vulnerability.hash}`, true)
24
+
25
+ let span = iastContext?.rootSpan
26
+
27
+ if (!span && tracer) {
28
+ span = tracer.startSpan('vulnerability', {
29
+ type: 'vulnerability'
30
+ })
31
+
32
+ vulnerability.location.spanId = span.context().toSpanId()
33
+
34
+ span.addTags({
35
+ [IAST_ENABLED_TAG_KEY]: 1
36
+ })
37
+ }
38
+
39
+ if (!span) return
40
+
41
+ keepTrace(span, SAMPLING_MECHANISM_APPSEC)
42
+ standalone.sample(span)
43
+
44
+ if (iastContext?.rootSpan) {
23
45
  iastContext[VULNERABILITIES_KEY] = iastContext[VULNERABILITIES_KEY] || []
24
46
  iastContext[VULNERABILITIES_KEY].push(vulnerability)
25
47
  } else {
26
- sendVulnerabilities([vulnerability])
48
+ sendVulnerabilities([vulnerability], span)
49
+ span.finish()
27
50
  }
28
51
  }
29
52
  }
@@ -34,36 +57,17 @@ function isValidVulnerability (vulnerability) {
34
57
  vulnerability.location && vulnerability.location.spanId
35
58
  }
36
59
 
37
- function sendVulnerabilities (vulnerabilities, rootSpan) {
60
+ function sendVulnerabilities (vulnerabilities, span) {
38
61
  if (vulnerabilities && vulnerabilities.length) {
39
- let span = rootSpan
40
- if (!span && tracer) {
41
- span = tracer.startSpan('vulnerability', {
42
- type: 'vulnerability'
43
- })
44
- vulnerabilities.forEach((vulnerability) => {
45
- vulnerability.location.spanId = span.context().toSpanId()
46
- })
47
- span.addTags({
48
- [IAST_ENABLED_TAG_KEY]: 1
49
- })
50
- }
51
-
52
62
  if (span && span.addTags) {
53
- const validAndDedupVulnerabilities = deduplicateVulnerabilities(vulnerabilities).filter(isValidVulnerability)
54
- const jsonToSend = vulnerabilitiesFormatter.toJson(validAndDedupVulnerabilities)
63
+ const validatedVulnerabilities = vulnerabilities.filter(isValidVulnerability)
64
+ const jsonToSend = vulnerabilitiesFormatter.toJson(validatedVulnerabilities)
55
65
 
56
66
  if (jsonToSend.vulnerabilities.length > 0) {
57
67
  const tags = {}
58
68
  // TODO: Store this outside of the span and set the tag in the exporter.
59
69
  tags[IAST_JSON_TAG_KEY] = JSON.stringify(jsonToSend)
60
70
  span.addTags(tags)
61
-
62
- keepTrace(span, SAMPLING_MECHANISM_APPSEC)
63
-
64
- standalone.sample(span)
65
-
66
- if (!rootSpan) span.finish()
67
71
  }
68
72
  }
69
73
  }
@@ -86,17 +90,8 @@ function stopClearCacheTimer () {
86
90
  }
87
91
  }
88
92
 
89
- function deduplicateVulnerabilities (vulnerabilities) {
90
- if (!deduplicationEnabled) return vulnerabilities
91
- const deduplicated = vulnerabilities.filter((vulnerability) => {
92
- const key = `${vulnerability.type}${vulnerability.hash}`
93
- if (!VULNERABILITY_HASHES.get(key)) {
94
- VULNERABILITY_HASHES.set(key, true)
95
- return true
96
- }
97
- return false
98
- })
99
- return deduplicated
93
+ function isDuplicatedVulnerability (vulnerability) {
94
+ return VULNERABILITY_HASHES.get(`${vulnerability.type}${vulnerability.hash}`)
100
95
  }
101
96
 
102
97
  function start (config, _tracer) {
@@ -16,7 +16,8 @@ const {
16
16
  expressProcessParams,
17
17
  responseBody,
18
18
  responseWriteHead,
19
- responseSetHeader
19
+ responseSetHeader,
20
+ routerParam
20
21
  } = require('./channels')
21
22
  const waf = require('./waf')
22
23
  const addresses = require('./addresses')
@@ -27,7 +28,7 @@ const web = require('../plugins/util/web')
27
28
  const { extractIp } = require('../plugins/util/ip_extractor')
28
29
  const { HTTP_CLIENT_IP } = require('../../../../ext/tags')
29
30
  const { isBlocked, block, setTemplates, getBlockingAction } = require('./blocking')
30
- const { passportTrackEvent } = require('./passport')
31
+ const UserTracking = require('./user_tracking')
31
32
  const { storage } = require('../../../datadog-core')
32
33
  const graphql = require('./graphql')
33
34
  const rasp = require('./rasp')
@@ -58,28 +59,27 @@ function enable (_config) {
58
59
 
59
60
  apiSecuritySampler.configure(_config.appsec)
60
61
 
62
+ UserTracking.setCollectionMode(_config.appsec.eventTracking.mode, false)
63
+
61
64
  bodyParser.subscribe(onRequestBodyParsed)
62
65
  multerParser.subscribe(onRequestBodyParsed)
63
66
  cookieParser.subscribe(onRequestCookieParser)
64
67
  incomingHttpRequestStart.subscribe(incomingHttpStartTranslator)
65
68
  incomingHttpRequestEnd.subscribe(incomingHttpEndTranslator)
69
+ passportVerify.subscribe(onPassportVerify) // possible optimization: only subscribe if collection mode is enabled
66
70
  queryParser.subscribe(onRequestQueryParsed)
67
71
  nextBodyParsed.subscribe(onRequestBodyParsed)
68
72
  nextQueryParsed.subscribe(onRequestQueryParsed)
69
73
  expressProcessParams.subscribe(onRequestProcessParams)
74
+ routerParam.subscribe(onRequestProcessParams)
70
75
  responseBody.subscribe(onResponseBody)
71
76
  responseWriteHead.subscribe(onResponseWriteHead)
72
77
  responseSetHeader.subscribe(onResponseSetHeader)
73
78
 
74
- if (_config.appsec.eventTracking.enabled) {
75
- passportVerify.subscribe(onPassportVerify)
76
- }
77
-
78
79
  isEnabled = true
79
80
  config = _config
80
81
  } catch (err) {
81
- log.error('Unable to start AppSec')
82
- log.error(err)
82
+ log.error('[ASM] Unable to start AppSec', err)
83
83
 
84
84
  disable()
85
85
  }
@@ -164,8 +164,10 @@ function incomingHttpEndTranslator ({ req, res }) {
164
164
  persistent[addresses.HTTP_INCOMING_COOKIES] = req.cookies
165
165
  }
166
166
 
167
- if (req.query !== null && typeof req.query === 'object') {
168
- persistent[addresses.HTTP_INCOMING_QUERY] = req.query
167
+ // we need to keep this to support nextjs
168
+ const query = req.query
169
+ if (query !== null && typeof query === 'object') {
170
+ persistent[addresses.HTTP_INCOMING_QUERY] = query
169
171
  }
170
172
 
171
173
  if (apiSecuritySampler.sampleRequest(req, res, true)) {
@@ -181,16 +183,18 @@ function incomingHttpEndTranslator ({ req, res }) {
181
183
  Reporter.finishRequest(req, res)
182
184
  }
183
185
 
184
- function onPassportVerify ({ credentials, user }) {
186
+ function onPassportVerify ({ framework, login, user, success, abortController }) {
185
187
  const store = storage.getStore()
186
188
  const rootSpan = store?.req && web.root(store.req)
187
189
 
188
190
  if (!rootSpan) {
189
- log.warn('No rootSpan found in onPassportVerify')
191
+ log.warn('[ASM] No rootSpan found in onPassportVerify')
190
192
  return
191
193
  }
192
194
 
193
- passportTrackEvent(credentials, user, rootSpan, config.appsec.eventTracking.mode)
195
+ const results = UserTracking.trackLogin(framework, login, user, success, rootSpan)
196
+
197
+ handleResults(results, store.req, store.req.res, rootSpan, abortController)
194
198
  }
195
199
 
196
200
  function onRequestQueryParsed ({ req, res, query, abortController }) {
@@ -310,6 +314,7 @@ function disable () {
310
314
  if (nextBodyParsed.hasSubscribers) nextBodyParsed.unsubscribe(onRequestBodyParsed)
311
315
  if (nextQueryParsed.hasSubscribers) nextQueryParsed.unsubscribe(onRequestQueryParsed)
312
316
  if (expressProcessParams.hasSubscribers) expressProcessParams.unsubscribe(onRequestProcessParams)
317
+ if (routerParam.hasSubscribers) routerParam.unsubscribe(onRequestProcessParams)
313
318
  if (responseBody.hasSubscribers) responseBody.unsubscribe(onResponseBody)
314
319
  if (responseWriteHead.hasSubscribers) responseWriteHead.unsubscribe(onResponseWriteHead)
315
320
  if (responseSetHeader.hasSubscribers) responseSetHeader.unsubscribe(onResponseSetHeader)
@@ -70,7 +70,7 @@ function enable (mod) {
70
70
  fsPlugin.enable()
71
71
  }
72
72
 
73
- log.info(`Enabled AppsecFsPlugin for ${mod}`)
73
+ log.info('[ASM] Enabled AppsecFsPlugin for %s', mod)
74
74
  }
75
75
 
76
76
  function disable (mod) {
@@ -85,7 +85,7 @@ function disable (mod) {
85
85
  fsPlugin = undefined
86
86
  }
87
87
 
88
- log.info(`Disabled AppsecFsPlugin for ${mod}`)
88
+ log.info('[ASM] Disabled AppsecFsPlugin for %s', mod)
89
89
  }
90
90
 
91
91
  module.exports = {
@@ -8,7 +8,7 @@ const log = require('../../log')
8
8
  const abortOnUncaughtException = process.execArgv?.includes('--abort-on-uncaught-exception')
9
9
 
10
10
  if (abortOnUncaughtException) {
11
- log.warn('The --abort-on-uncaught-exception flag is enabled. The RASP module will not block operations.')
11
+ log.warn('[ASM] The --abort-on-uncaught-exception flag is enabled. The RASP module will not block operations.')
12
12
  }
13
13
 
14
14
  const RULE_TYPES = {
@@ -22,6 +22,7 @@ module.exports = {
22
22
  ASM_RASP_SSRF: 1n << 23n,
23
23
  ASM_RASP_SHI: 1n << 24n,
24
24
  APM_TRACING_SAMPLE_RULES: 1n << 29n,
25
+ ASM_AUTO_USER_INSTRUM_MODE: 1n << 31n,
25
26
  ASM_ENDPOINT_FINGERPRINT: 1n << 32n,
26
27
  ASM_NETWORK_FINGERPRINT: 1n << 34n,
27
28
  ASM_HEADER_FINGERPRINT: 1n << 35n
@@ -4,6 +4,8 @@ const Activation = require('../activation')
4
4
 
5
5
  const RemoteConfigManager = require('./manager')
6
6
  const RemoteConfigCapabilities = require('./capabilities')
7
+ const { setCollectionMode } = require('../user_tracking')
8
+ const log = require('../../log')
7
9
 
8
10
  let rc
9
11
 
@@ -23,9 +25,31 @@ function enable (config, appsec) {
23
25
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_ACTIVATION, true)
24
26
  }
25
27
 
26
- rc.setProductHandler('ASM_FEATURES', (action, rcConfig) => {
28
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_AUTO_USER_INSTRUM_MODE, true)
29
+
30
+ let autoUserInstrumModeId
31
+
32
+ rc.setProductHandler('ASM_FEATURES', (action, rcConfig, configId) => {
27
33
  if (!rcConfig) return
28
34
 
35
+ // this is put before other handlers because it can reject the config
36
+ if (typeof rcConfig.auto_user_instrum?.mode === 'string') {
37
+ if (action === 'apply' || action === 'modify') {
38
+ // check if there is already a config applied with this field
39
+ if (autoUserInstrumModeId && configId !== autoUserInstrumModeId) {
40
+ log.error('[RC] Multiple auto_user_instrum received in ASM_FEATURES. Discarding config')
41
+ // eslint-disable-next-line no-throw-literal
42
+ throw 'Multiple auto_user_instrum.mode received in ASM_FEATURES'
43
+ }
44
+
45
+ setCollectionMode(rcConfig.auto_user_instrum.mode)
46
+ autoUserInstrumModeId = configId
47
+ } else if (configId === autoUserInstrumModeId) {
48
+ setCollectionMode(config.appsec.eventTracking.mode)
49
+ autoUserInstrumModeId = null
50
+ }
51
+ }
52
+
29
53
  if (activation === Activation.ONECLICK) {
30
54
  enableOrDisableAppsec(action, rcConfig, config, appsec)
31
55
  }
@@ -134,7 +134,7 @@ class RemoteConfigManager extends EventEmitter {
134
134
  if (statusCode === 404) return cb()
135
135
 
136
136
  if (err) {
137
- log.error(err)
137
+ log.error('[RC] Error in request', err)
138
138
  return cb()
139
139
  }
140
140
 
@@ -148,7 +148,7 @@ class RemoteConfigManager extends EventEmitter {
148
148
  try {
149
149
  this.parseConfig(JSON.parse(data))
150
150
  } catch (err) {
151
- log.error(`Could not parse remote config response: ${err}`)
151
+ log.error('[RC] Could not parse remote config response', err)
152
152
 
153
153
  this.state.client.state.has_error = true
154
154
  this.state.client.state.error = err.toString()
@@ -148,7 +148,9 @@ function reportAttack (attackData) {
148
148
  newTags['_dd.appsec.json'] = '{"triggers":' + attackData + '}'
149
149
  }
150
150
 
151
- newTags['network.client.ip'] = req.socket.remoteAddress
151
+ if (req.socket) {
152
+ newTags['network.client.ip'] = req.socket.remoteAddress
153
+ }
152
154
 
153
155
  rootSpan.addTags(newTags)
154
156
  }
@@ -11,13 +11,13 @@ function setUserTags (user, rootSpan) {
11
11
 
12
12
  function setUser (tracer, user) {
13
13
  if (!user || !user.id) {
14
- log.warn('Invalid user provided to setUser')
14
+ log.warn('[ASM] Invalid user provided to setUser')
15
15
  return
16
16
  }
17
17
 
18
18
  const rootSpan = getRootSpan(tracer)
19
19
  if (!rootSpan) {
20
- log.warn('Root span not available in setUser')
20
+ log.warn('[ASM] Root span not available in setUser')
21
21
  return
22
22
  }
23
23
 
@@ -7,67 +7,68 @@ const standalone = require('../standalone')
7
7
  const waf = require('../waf')
8
8
  const { SAMPLING_MECHANISM_APPSEC } = require('../../constants')
9
9
  const { keepTrace } = require('../../priority_sampler')
10
+ const addresses = require('../addresses')
10
11
 
11
12
  function trackUserLoginSuccessEvent (tracer, user, metadata) {
12
13
  // TODO: better user check here and in _setUser() ?
13
14
  if (!user || !user.id) {
14
- log.warn('Invalid user provided to trackUserLoginSuccessEvent')
15
+ log.warn('[ASM] Invalid user provided to trackUserLoginSuccessEvent')
15
16
  return
16
17
  }
17
18
 
18
19
  const rootSpan = getRootSpan(tracer)
19
20
  if (!rootSpan) {
20
- log.warn('Root span not available in trackUserLoginSuccessEvent')
21
+ log.warn('[ASM] Root span not available in trackUserLoginSuccessEvent')
21
22
  return
22
23
  }
23
24
 
24
25
  setUserTags(user, rootSpan)
25
26
 
26
- trackEvent('users.login.success', metadata, 'trackUserLoginSuccessEvent', rootSpan, 'sdk')
27
+ const login = user.login ?? user.id
28
+
29
+ metadata = { 'usr.login': login, ...metadata }
30
+
31
+ trackEvent('users.login.success', metadata, 'trackUserLoginSuccessEvent', rootSpan)
32
+
33
+ runWaf('users.login.success', { id: user.id, login })
27
34
  }
28
35
 
29
36
  function trackUserLoginFailureEvent (tracer, userId, exists, metadata) {
30
37
  if (!userId || typeof userId !== 'string') {
31
- log.warn('Invalid userId provided to trackUserLoginFailureEvent')
38
+ log.warn('[ASM] Invalid userId provided to trackUserLoginFailureEvent')
32
39
  return
33
40
  }
34
41
 
35
42
  const fields = {
36
43
  'usr.id': userId,
44
+ 'usr.login': userId,
37
45
  'usr.exists': exists ? 'true' : 'false',
38
46
  ...metadata
39
47
  }
40
48
 
41
- trackEvent('users.login.failure', fields, 'trackUserLoginFailureEvent', getRootSpan(tracer), 'sdk')
49
+ trackEvent('users.login.failure', fields, 'trackUserLoginFailureEvent', getRootSpan(tracer))
50
+
51
+ runWaf('users.login.failure', { login: userId })
42
52
  }
43
53
 
44
54
  function trackCustomEvent (tracer, eventName, metadata) {
45
55
  if (!eventName || typeof eventName !== 'string') {
46
- log.warn('Invalid eventName provided to trackCustomEvent')
56
+ log.warn('[ASM] Invalid eventName provided to trackCustomEvent')
47
57
  return
48
58
  }
49
59
 
50
- trackEvent(eventName, metadata, 'trackCustomEvent', getRootSpan(tracer), 'sdk')
60
+ trackEvent(eventName, metadata, 'trackCustomEvent', getRootSpan(tracer))
51
61
  }
52
62
 
53
- function trackEvent (eventName, fields, sdkMethodName, rootSpan, mode) {
63
+ function trackEvent (eventName, fields, sdkMethodName, rootSpan) {
54
64
  if (!rootSpan) {
55
- log.warn(`Root span not available in ${sdkMethodName}`)
65
+ log.warn('[ASM] Root span not available in %s', sdkMethodName)
56
66
  return
57
67
  }
58
68
 
59
- keepTrace(rootSpan, SAMPLING_MECHANISM_APPSEC)
60
-
61
69
  const tags = {
62
- [`appsec.events.${eventName}.track`]: 'true'
63
- }
64
-
65
- if (mode === 'sdk') {
66
- tags[`_dd.appsec.events.${eventName}.sdk`] = 'true'
67
- }
68
-
69
- if (mode === 'safe' || mode === 'extended') {
70
- tags[`_dd.appsec.events.${eventName}.auto.mode`] = mode
70
+ [`appsec.events.${eventName}.track`]: 'true',
71
+ [`_dd.appsec.events.${eventName}.sdk`]: 'true'
71
72
  }
72
73
 
73
74
  if (fields) {
@@ -78,16 +79,28 @@ function trackEvent (eventName, fields, sdkMethodName, rootSpan, mode) {
78
79
 
79
80
  rootSpan.addTags(tags)
80
81
 
82
+ keepTrace(rootSpan, SAMPLING_MECHANISM_APPSEC)
81
83
  standalone.sample(rootSpan)
84
+ }
85
+
86
+ function runWaf (eventName, user) {
87
+ const persistent = {
88
+ [`server.business_logic.${eventName}`]: null
89
+ }
90
+
91
+ if (user.id) {
92
+ persistent[addresses.USER_ID] = '' + user.id
93
+ }
82
94
 
83
- if (['users.login.success', 'users.login.failure'].includes(eventName)) {
84
- waf.run({ persistent: { [`server.business_logic.${eventName}`]: null } })
95
+ if (user.login) {
96
+ persistent[addresses.USER_LOGIN] = '' + user.login
85
97
  }
98
+
99
+ waf.run({ persistent })
86
100
  }
87
101
 
88
102
  module.exports = {
89
103
  trackUserLoginSuccessEvent,
90
104
  trackUserLoginFailureEvent,
91
- trackCustomEvent,
92
- trackEvent
105
+ trackCustomEvent
93
106
  }