dd-trace 5.57.1 → 5.59.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 (206) hide show
  1. package/LICENSE-3rdparty.csv +3 -1
  2. package/ci/cypress/after-run.js +2 -0
  3. package/ci/cypress/after-spec.js +2 -0
  4. package/ci/cypress/plugin.js +2 -0
  5. package/ci/cypress/polyfills.js +2 -0
  6. package/ci/cypress/support.js +2 -0
  7. package/ci/init.js +2 -0
  8. package/index.d.ts +7 -0
  9. package/init.js +1 -6
  10. package/initialize.mjs +2 -0
  11. package/package.json +40 -9
  12. package/packages/datadog-code-origin/index.js +14 -9
  13. package/packages/datadog-instrumentations/src/apollo.js +7 -10
  14. package/packages/datadog-instrumentations/src/avsc.js +2 -0
  15. package/packages/datadog-instrumentations/src/azure-functions.js +1 -1
  16. package/packages/datadog-instrumentations/src/child_process.js +22 -43
  17. package/packages/datadog-instrumentations/src/cucumber.js +10 -8
  18. package/packages/datadog-instrumentations/src/cypress.js +2 -0
  19. package/packages/datadog-instrumentations/src/fastify.js +19 -1
  20. package/packages/datadog-instrumentations/src/graphql.js +9 -0
  21. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  22. package/packages/datadog-instrumentations/src/helpers/register.js +2 -2
  23. package/packages/datadog-instrumentations/src/hono.js +102 -0
  24. package/packages/datadog-instrumentations/src/jest.js +1 -1
  25. package/packages/datadog-instrumentations/src/langchain.js +21 -0
  26. package/packages/datadog-instrumentations/src/mocha/common.js +2 -0
  27. package/packages/datadog-instrumentations/src/mocha.js +2 -0
  28. package/packages/datadog-instrumentations/src/mysql2.js +6 -6
  29. package/packages/datadog-instrumentations/src/next.js +3 -1
  30. package/packages/datadog-instrumentations/src/nyc.js +2 -0
  31. package/packages/datadog-instrumentations/src/oracledb.js +24 -2
  32. package/packages/datadog-instrumentations/src/orchestrion-config/index.js +32 -0
  33. package/packages/datadog-instrumentations/src/playwright.js +5 -1
  34. package/packages/datadog-instrumentations/src/protobufjs.js +2 -0
  35. package/packages/datadog-instrumentations/src/selenium.js +2 -0
  36. package/packages/datadog-instrumentations/src/tedious.js +12 -17
  37. package/packages/datadog-instrumentations/src/vitest.js +2 -0
  38. package/packages/datadog-plugin-avsc/src/index.js +2 -0
  39. package/packages/datadog-plugin-avsc/src/schema_iterator.js +2 -0
  40. package/packages/datadog-plugin-aws-sdk/src/base.js +51 -1
  41. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +2 -0
  42. package/packages/datadog-plugin-child_process/src/index.js +30 -10
  43. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -1
  44. package/packages/datadog-plugin-cypress/src/after-run.js +2 -0
  45. package/packages/datadog-plugin-cypress/src/after-spec.js +2 -0
  46. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +8 -3
  47. package/packages/datadog-plugin-cypress/src/index.js +2 -0
  48. package/packages/datadog-plugin-cypress/src/plugin.js +2 -0
  49. package/packages/datadog-plugin-cypress/src/support.js +21 -25
  50. package/packages/datadog-plugin-google-cloud-vertexai/src/utils.js +2 -0
  51. package/packages/datadog-plugin-graphql/src/tools/index.js +0 -2
  52. package/packages/datadog-plugin-graphql/src/tools/signature.js +0 -2
  53. package/packages/datadog-plugin-graphql/src/tools/transforms.js +0 -2
  54. package/packages/datadog-plugin-graphql/src/utils.js +2 -0
  55. package/packages/datadog-plugin-hono/src/index.js +28 -0
  56. package/packages/datadog-plugin-http/src/client.js +3 -4
  57. package/packages/datadog-plugin-http2/src/client.js +9 -8
  58. package/packages/datadog-plugin-jest/src/index.js +2 -0
  59. package/packages/datadog-plugin-jest/src/util.js +2 -0
  60. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -0
  61. package/packages/datadog-plugin-langchain/src/handlers/chain.js +1 -1
  62. package/packages/datadog-plugin-langchain/src/handlers/language_models/chat_model.js +1 -1
  63. package/packages/datadog-plugin-langchain/src/handlers/language_models/llm.js +1 -1
  64. package/packages/datadog-plugin-langchain/src/tracing.js +36 -4
  65. package/packages/datadog-plugin-nyc/src/index.js +2 -0
  66. package/packages/datadog-plugin-oracledb/src/connection-parser.js +37 -0
  67. package/packages/datadog-plugin-oracledb/src/index.js +15 -17
  68. package/packages/datadog-plugin-protobufjs/src/index.js +2 -0
  69. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +2 -0
  70. package/packages/datadog-plugin-selenium/src/index.js +2 -0
  71. package/packages/datadog-plugin-tedious/src/index.js +10 -9
  72. package/packages/datadog-plugin-vitest/src/index.js +2 -0
  73. package/packages/dd-trace/src/appsec/iast/analyzers/injection-analyzer.js +6 -4
  74. package/packages/dd-trace/src/appsec/iast/analyzers/ssrf-analyzer.js +9 -0
  75. package/packages/dd-trace/src/appsec/iast/iast-context.js +5 -1
  76. package/packages/dd-trace/src/appsec/iast/index.js +2 -0
  77. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +1 -1
  78. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +5 -2
  79. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +0 -2
  80. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +2 -0
  81. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +2 -0
  82. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +1 -0
  83. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +2 -0
  84. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +3 -3
  85. package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +18 -11
  86. package/packages/dd-trace/src/appsec/rasp/utils.js +1 -1
  87. package/packages/dd-trace/src/appsec/recommended.json +88 -2
  88. package/packages/dd-trace/src/appsec/reporter.js +68 -14
  89. package/packages/dd-trace/src/appsec/rule_manager.js +63 -171
  90. package/packages/dd-trace/src/appsec/sdk/track_event.js +3 -5
  91. package/packages/dd-trace/src/appsec/stack_trace.js +11 -11
  92. package/packages/dd-trace/src/appsec/telemetry/common.js +2 -2
  93. package/packages/dd-trace/src/appsec/telemetry/index.js +8 -0
  94. package/packages/dd-trace/src/appsec/telemetry/waf.js +5 -3
  95. package/packages/dd-trace/src/appsec/waf/diagnostics.js +15 -0
  96. package/packages/dd-trace/src/appsec/waf/index.js +47 -6
  97. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +2 -2
  98. package/packages/dd-trace/src/appsec/waf/waf_manager.js +22 -12
  99. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +3 -3
  100. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +2 -0
  101. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +3 -1
  102. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -0
  103. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +2 -0
  104. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +2 -0
  105. package/packages/dd-trace/src/ci-visibility/telemetry.js +2 -0
  106. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +2 -0
  107. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +7 -3
  108. package/packages/dd-trace/src/config.js +15 -4
  109. package/packages/dd-trace/src/config_stable.js +2 -0
  110. package/packages/dd-trace/src/constants.js +1 -2
  111. package/packages/dd-trace/src/datastreams/checkpointer.js +2 -0
  112. package/packages/dd-trace/src/datastreams/context.js +2 -0
  113. package/packages/dd-trace/src/datastreams/encoding.js +2 -0
  114. package/packages/dd-trace/src/datastreams/fnv.js +2 -0
  115. package/packages/dd-trace/src/datastreams/pathway.js +11 -9
  116. package/packages/dd-trace/src/datastreams/processor.js +8 -7
  117. package/packages/dd-trace/src/datastreams/schemas/schema.js +2 -0
  118. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +45 -36
  119. package/packages/dd-trace/src/datastreams/schemas/schema_sampler.js +2 -0
  120. package/packages/dd-trace/src/datastreams/writer.js +2 -0
  121. package/packages/dd-trace/src/debugger/devtools_client/index.js +12 -4
  122. package/packages/dd-trace/src/debugger/devtools_client/inspector_promises_polyfill.js +2 -0
  123. package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +8 -5
  124. package/packages/dd-trace/src/debugger/devtools_client/snapshot/symbols.js +1 -1
  125. package/packages/dd-trace/src/debugger/index.js +36 -9
  126. package/packages/dd-trace/src/encode/tags-processors.js +2 -0
  127. package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +2 -0
  128. package/packages/dd-trace/src/exporters/common/request.js +1 -1
  129. package/packages/dd-trace/src/exporters/common/util.js +2 -0
  130. package/packages/dd-trace/src/exporters/span-stats/index.js +2 -0
  131. package/packages/dd-trace/src/exporters/span-stats/writer.js +2 -0
  132. package/packages/dd-trace/src/external-logger/src/index.js +2 -0
  133. package/packages/dd-trace/src/git_metadata_tagger.js +2 -0
  134. package/packages/dd-trace/src/git_properties.js +2 -0
  135. package/packages/dd-trace/src/guardrails/index.js +3 -4
  136. package/packages/dd-trace/src/guardrails/log.js +2 -2
  137. package/packages/dd-trace/src/guardrails/telemetry.js +16 -14
  138. package/packages/dd-trace/src/guardrails/util.js +0 -2
  139. package/packages/dd-trace/src/heap_snapshots.js +58 -0
  140. package/packages/dd-trace/src/llmobs/noop.js +1 -1
  141. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +2 -0
  142. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +5 -0
  143. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/tool.js +15 -0
  144. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/vectorstore.js +36 -0
  145. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +47 -4
  146. package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
  147. package/packages/dd-trace/src/llmobs/tagger.js +10 -1
  148. package/packages/dd-trace/src/log/log.js +1 -1
  149. package/packages/dd-trace/src/noop/dogstatsd.js +2 -0
  150. package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +2 -0
  151. package/packages/dd-trace/src/opentracing/span.js +1 -1
  152. package/packages/dd-trace/src/payload-tagging/config/index.js +2 -0
  153. package/packages/dd-trace/src/payload-tagging/index.js +3 -1
  154. package/packages/dd-trace/src/payload-tagging/tagging.js +4 -2
  155. package/packages/dd-trace/src/plugins/apollo.js +2 -0
  156. package/packages/dd-trace/src/plugins/ci_plugin.js +8 -3
  157. package/packages/dd-trace/src/plugins/index.js +1 -0
  158. package/packages/dd-trace/src/plugins/outbound.js +7 -0
  159. package/packages/dd-trace/src/plugins/util/ci.js +2 -0
  160. package/packages/dd-trace/src/plugins/util/env.js +2 -0
  161. package/packages/dd-trace/src/plugins/util/git.js +40 -5
  162. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +2 -0
  163. package/packages/dd-trace/src/plugins/util/llm.js +2 -0
  164. package/packages/dd-trace/src/plugins/util/serverless.js +2 -0
  165. package/packages/dd-trace/src/plugins/util/stacktrace.js +178 -50
  166. package/packages/dd-trace/src/plugins/util/tags.js +17 -1
  167. package/packages/dd-trace/src/plugins/util/test.js +9 -4
  168. package/packages/dd-trace/src/plugins/util/url.js +2 -0
  169. package/packages/dd-trace/src/plugins/util/user-provided-git.js +2 -0
  170. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +4 -0
  171. package/packages/dd-trace/src/profiling/profiler.js +89 -70
  172. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns.js +2 -0
  173. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookup.js +2 -0
  174. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookupservice.js +2 -0
  175. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_resolve.js +2 -0
  176. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_reverse.js +2 -0
  177. package/packages/dd-trace/src/profiling/profilers/event_plugins/event.js +2 -0
  178. package/packages/dd-trace/src/profiling/profilers/event_plugins/fs.js +2 -0
  179. package/packages/dd-trace/src/profiling/profilers/event_plugins/net.js +2 -0
  180. package/packages/dd-trace/src/profiling/profilers/events.js +2 -0
  181. package/packages/dd-trace/src/profiling/profilers/wall.js +2 -2
  182. package/packages/dd-trace/src/profiling/webspan-utils.js +2 -0
  183. package/packages/dd-trace/src/proxy.js +4 -0
  184. package/packages/dd-trace/src/remote_config/capabilities.js +3 -1
  185. package/packages/dd-trace/src/remote_config/index.js +4 -0
  186. package/packages/dd-trace/src/service-naming/index.js +2 -0
  187. package/packages/dd-trace/src/service-naming/schemas/definition.js +4 -9
  188. package/packages/dd-trace/src/service-naming/schemas/util.js +2 -0
  189. package/packages/dd-trace/src/service-naming/schemas/v0/graphql.js +2 -0
  190. package/packages/dd-trace/src/service-naming/schemas/v0/index.js +2 -0
  191. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +2 -0
  192. package/packages/dd-trace/src/service-naming/schemas/v0/serverless.js +2 -0
  193. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +2 -0
  194. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +2 -0
  195. package/packages/dd-trace/src/service-naming/schemas/v1/graphql.js +2 -0
  196. package/packages/dd-trace/src/service-naming/schemas/v1/index.js +2 -0
  197. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +2 -0
  198. package/packages/dd-trace/src/service-naming/schemas/v1/serverless.js +2 -0
  199. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -1
  200. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +2 -0
  201. package/packages/dd-trace/src/span_stats.js +2 -0
  202. package/packages/dd-trace/src/supported-configurations.json +5 -0
  203. package/packages/dd-trace/src/telemetry/send-data.js +2 -0
  204. package/register.js +4 -0
  205. package/version.js +0 -3
  206. package/packages/dd-trace/src/payload-tagging/jsonpath-plus.js +0 -2094
@@ -1,3 +1,5 @@
1
+ 'use strict'
2
+
1
3
  module.exports = {
2
4
  COMMAND_INJECTION: 'COMMAND_INJECTION',
3
5
  CODE_INJECTION: 'CODE_INJECTION',
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const LRU = require('lru-cache')
3
+ const { LRUCache } = require('lru-cache')
4
4
  const vulnerabilitiesFormatter = require('./vulnerabilities-formatter')
5
5
  const { IAST_ENABLED_TAG_KEY, IAST_JSON_TAG_KEY } = require('./tags')
6
6
  const { keepTrace } = require('../../priority_sampler')
@@ -10,7 +10,7 @@ const { ASM } = require('../../standalone/product')
10
10
 
11
11
  const VULNERABILITIES_KEY = 'vulnerabilities'
12
12
  const VULNERABILITY_HASHES_MAX_SIZE = 1000
13
- const VULNERABILITY_HASHES = new LRU({ max: VULNERABILITY_HASHES_MAX_SIZE })
13
+ const VULNERABILITY_HASHES = new LRUCache({ max: VULNERABILITY_HASHES_MAX_SIZE })
14
14
  const RESET_VULNERABILITY_CACHE_INTERVAL = 60 * 60 * 1000 // 1 hour
15
15
 
16
16
  let tracer
@@ -114,7 +114,7 @@ function isDuplicatedVulnerability (vulnerability) {
114
114
  }
115
115
 
116
116
  function getVulnerabilityCallSiteFrames () {
117
- return getCallsiteFrames(stackTraceMaxDepth)
117
+ return getCallsiteFrames(stackTraceMaxDepth, getVulnerabilityCallSiteFrames)
118
118
  }
119
119
 
120
120
  function replaceCallSiteFromSourceMap (callsite) {
@@ -14,25 +14,31 @@ const enabledFor = {
14
14
 
15
15
  let fsPlugin
16
16
 
17
- function enterWith (fsProps, store = storage('legacy').getStore()) {
17
+ function getStoreToStart (fsProps, store = storage('legacy').getStore()) {
18
18
  if (store && !store.fs?.opExcluded) {
19
- storage('legacy').enterWith({
19
+ return {
20
20
  ...store,
21
21
  fs: {
22
22
  ...store.fs,
23
23
  ...fsProps,
24
24
  parentStore: store
25
25
  }
26
- })
26
+ }
27
27
  }
28
+
29
+ return store
28
30
  }
29
31
 
30
32
  class AppsecFsPlugin extends Plugin {
31
33
  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)
34
+ this.addBind('apm:fs:operation:start', this._onFsOperationStart)
35
+ this.addBind('apm:fs:operation:finish', this._onFsOperationFinishOrRenderEnd)
36
+ this.addBind('tracing:datadog:express:response:render:start', this._onResponseRenderStart)
37
+ this.addBind('tracing:datadog:express:response:render:end', this._onFsOperationFinishOrRenderEnd)
38
+ // TODO Remove this when dc-polyfill is fixed&updated
39
+ // hack to node 18 and early 20.x
40
+ // with dc-polyfill addBind is not enough to force a channel.hasSubscribers === true
41
+ this.addSub('tracing:datadog:express:response:render:start', () => {})
36
42
 
37
43
  super.configure(true)
38
44
  }
@@ -44,19 +50,20 @@ class AppsecFsPlugin extends Plugin {
44
50
  _onFsOperationStart () {
45
51
  const store = storage('legacy').getStore()
46
52
  if (store) {
47
- enterWith({ root: store.fs?.root === undefined }, store)
53
+ return getStoreToStart({ root: store.fs?.root === undefined }, store)
48
54
  }
49
55
  }
50
56
 
51
57
  _onResponseRenderStart () {
52
- enterWith({ opExcluded: true })
58
+ return getStoreToStart({ opExcluded: true })
53
59
  }
54
60
 
55
61
  _onFsOperationFinishOrRenderEnd () {
56
62
  const store = storage('legacy').getStore()
57
- if (store?.fs?.parentStore) {
58
- storage('legacy').enterWith(store.fs.parentStore)
63
+ if (store?.fs) {
64
+ return store.fs.parentStore
59
65
  }
66
+ return store
60
67
  }
61
68
  }
62
69
 
@@ -41,7 +41,7 @@ function handleResult (result, req, res, abortController, config, raspRule) {
41
41
  const ruleTriggered = !!result?.events?.length
42
42
 
43
43
  if (generateStackTraceAction && enabled && canReportStackTrace(rootSpan, maxStackTraces)) {
44
- const frames = getCallsiteFrames(maxDepth)
44
+ const frames = getCallsiteFrames(maxDepth, handleResult)
45
45
 
46
46
  reportStackTrace(
47
47
  rootSpan,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": "2.2",
3
3
  "metadata": {
4
- "rules_version": "1.14.2"
4
+ "rules_version": "1.15.0"
5
5
  },
6
6
  "rules": [
7
7
  {
@@ -2985,7 +2985,7 @@
2985
2985
  "address": "graphql.server.resolver"
2986
2986
  }
2987
2987
  ],
2988
- "regex": "\\b(?:(?:l(?:(?:utimes|chmod)(?:Sync)?|(?:stat|ink)Sync)|w(?:rite(?:(?:File|v)(?:Sync)?|Sync)|atchFile)|u(?:n(?:watchFile|linkSync)|times(?:Sync)?)|s(?:(?:ymlink|tat)Sync|pawn(?:File|Sync))|ex(?:ec(?:File(?:Sync)?|Sync)|istsSync)|a(?:ppendFile|ccess)(?:Sync)?|(?:Caveat|Inode)s|open(?:dir)?Sync|new\\s+Function|Availability|\\beval)\\s*\\(|m(?:ain(?:Module\\s*(?:\\W*\\s*(?:constructor|require)|\\[)|\\s*(?:\\W*\\s*(?:constructor|require)|\\[))|kd(?:temp(?:Sync)?|irSync)\\s*\\(|odule\\.exports\\s*=)|c(?:(?:(?:h(?:mod|own)|lose)Sync|reate(?:Write|Read)Stream|p(?:Sync)?)\\s*\\(|o(?:nstructor\\s*(?:\\W*\\s*_load|\\[)|pyFile(?:Sync)?\\s*\\())|f(?:(?:(?:s(?:(?:yncS)?|tatS)|datas(?:yncS)?)ync|ch(?:mod|own)(?:Sync)?)\\s*\\(|u(?:nction\\s*\\(\\s*\\)\\s*{|times(?:Sync)?\\s*\\())|r(?:e(?:(?:ad(?:(?:File|link|dir)?Sync|v(?:Sync)?)|nameSync)\\s*\\(|quire\\s*(?:\\W*\\s*main|\\[))|m(?:Sync)?\\s*\\()|process\\s*(?:\\W*\\s*(?:mainModule|binding)|\\[)|t(?:his\\.constructor|runcateSync\\s*\\()|_(?:\\$\\$ND_FUNC\\$\\$_|_js_function)|global\\s*(?:\\W*\\s*process|\\[)|String\\s*\\.\\s*fromCharCode|binding\\s*\\[)",
2988
+ "regex": "\\b(?:(?:l(?:(?:utimes|chmod)(?:Sync)?|(?:stat|ink)Sync)|w(?:rite(?:(?:File|v)(?:Sync)?|Sync)|atchFile)|u(?:n(?:watchFile|linkSync)|times(?:Sync)?)|s(?:(?:ymlink|tat)Sync|pawn(?:File|Sync))|ex(?:ec(?:File(?:Sync)?|Sync)|istsSync)|a(?:ppendFile|ccess)(?:Sync)?|(?:Caveat|Inode)s|open(?:dir)?Sync|new\\s+Function|Availability|\\beval)\\s*\\(|m(?:ain(?:Module\\s*(?:\\W*\\s*(?:constructor|require)|\\[)|\\s*(?:\\W*\\s*(?:constructor|require)|\\[))|kd(?:temp(?:Sync)?|irSync)\\s*\\(|odule\\.exports\\s*=)|c(?:(?:(?:h(?:mod|own)|lose)Sync|reate(?:Write|Read)Stream|p(?:Sync)?)\\s*\\(|o(?:nstructor\\s*(?:\\W*\\s*_load|\\[)|pyFile(?:Sync)?\\s*\\())|f(?:(?:(?:s(?:(?:yncS)?|tatS)|datas(?:yncS)?)ync|ch(?:mod|own)(?:Sync)?)\\s*\\(|u(?:nction\\s*\\(\\s*\\)\\s*{|times(?:Sync)?\\s*\\())|r(?:e(?:(?:ad(?:(?:File|link|dir)?Sync|v(?:Sync)?)|nameSync)\\s*\\(|quire\\s*(?:\\W*\\s*main\\b|\\[))|m(?:Sync)?\\s*\\()|process\\s*(?:\\W*\\s*(?:mainModule|binding)|\\[)|t(?:his\\.constructor|runcateSync\\s*\\()|_(?:\\$\\$ND_FUNC\\$\\$_|_js_function)|global\\s*(?:\\W*\\s*process|\\[)|String\\s*\\.\\s*fromCharCode|binding\\s*\\[)",
2989
2989
  "options": {
2990
2990
  "case_sensitive": true,
2991
2991
  "min_length": 3
@@ -5656,6 +5656,52 @@
5656
5656
  ],
5657
5657
  "transformers": []
5658
5658
  },
5659
+ {
5660
+ "id": "dog-932-110",
5661
+ "name": "Python: Subprocess-based command injection",
5662
+ "tags": {
5663
+ "type": "command_injection",
5664
+ "category": "attack_attempt",
5665
+ "confidence": "0",
5666
+ "module": "waf"
5667
+ },
5668
+ "conditions": [
5669
+ {
5670
+ "parameters": {
5671
+ "inputs": [
5672
+ {
5673
+ "address": "server.request.query"
5674
+ },
5675
+ {
5676
+ "address": "server.request.body"
5677
+ },
5678
+ {
5679
+ "address": "server.request.path_params"
5680
+ },
5681
+ {
5682
+ "address": "server.request.headers.no_cookies"
5683
+ },
5684
+ {
5685
+ "address": "grpc.server.request.message"
5686
+ },
5687
+ {
5688
+ "address": "graphql.server.all_resolvers"
5689
+ },
5690
+ {
5691
+ "address": "graphql.server.resolver"
5692
+ }
5693
+ ],
5694
+ "regex": "(?s)\\bsubprocess\\b.*\\b(?:check_output|run|Popen|call|check_call)\\b",
5695
+ "options": {
5696
+ "case_sensitive": true,
5697
+ "min_length": 14
5698
+ }
5699
+ },
5700
+ "operator": "match_regex"
5701
+ }
5702
+ ],
5703
+ "transformers": []
5704
+ },
5659
5705
  {
5660
5706
  "id": "dog-934-001",
5661
5707
  "name": "XXE - XML file loads external entity",
@@ -9074,6 +9120,28 @@
9074
9120
  "evaluate": true,
9075
9121
  "output": true
9076
9122
  },
9123
+ {
9124
+ "id": "decode-auth-jwt",
9125
+ "generator": "jwt_decode",
9126
+ "min_version": "1.25.0",
9127
+ "parameters": {
9128
+ "mappings": [
9129
+ {
9130
+ "inputs": [
9131
+ {
9132
+ "address": "server.request.headers.no_cookies",
9133
+ "key_path": [
9134
+ "authorization"
9135
+ ]
9136
+ }
9137
+ ],
9138
+ "output": "server.request.jwt"
9139
+ }
9140
+ ]
9141
+ },
9142
+ "evaluate": true,
9143
+ "output": false
9144
+ },
9077
9145
  {
9078
9146
  "id": "http-network-fingerprint",
9079
9147
  "generator": "http_network_fingerprint",
@@ -9918,6 +9986,24 @@
9918
9986
  "category": "payment"
9919
9987
  }
9920
9988
  },
9989
+ {
9990
+ "id": "c542c147-3883-43d6-a067-178e4a7bd65d",
9991
+ "name": "Password",
9992
+ "key": {
9993
+ "operator": "match_regex",
9994
+ "parameters": {
9995
+ "regex": "\\bpass(?:[_-]?word|wd)?\\b|\\bpwd\\b",
9996
+ "options": {
9997
+ "case_sensitive": false,
9998
+ "min_length": 3
9999
+ }
10000
+ }
10001
+ },
10002
+ "tags": {
10003
+ "type": "password",
10004
+ "category": "credentials"
10005
+ }
10006
+ },
9921
10007
  {
9922
10008
  "id": "18b608bd7a764bff5b2344c0",
9923
10009
  "name": "Phone number",
@@ -1,5 +1,8 @@
1
1
  'use strict'
2
2
 
3
+ const dc = require('dc-polyfill')
4
+ const zlib = require('zlib')
5
+
3
6
  const Limiter = require('../rate_limiter')
4
7
  const { storage } = require('../../../datadog-core')
5
8
  const web = require('../plugins/util/web')
@@ -7,6 +10,7 @@ const { ipHeaderList } = require('../plugins/util/ip_extractor')
7
10
  const {
8
11
  incrementWafInitMetric,
9
12
  incrementWafUpdatesMetric,
13
+ incrementWafConfigErrorsMetric,
10
14
  incrementWafRequestsMetric,
11
15
  updateWafRequestsMetricTags,
12
16
  updateRaspRequestsMetricTags,
@@ -14,9 +18,9 @@ const {
14
18
  updateRateLimitedMetric,
15
19
  getRequestMetrics
16
20
  } = require('./telemetry')
17
- const zlib = require('zlib')
18
21
  const { keepTrace } = require('../priority_sampler')
19
22
  const { ASM } = require('../standalone/product')
23
+ const { DIAGNOSTIC_KEYS } = require('./waf/diagnostics')
20
24
 
21
25
  const REQUEST_HEADER_TAG_PREFIX = 'http.request.headers.'
22
26
  const RESPONSE_HEADER_TAG_PREFIX = 'http.response.headers.'
@@ -25,6 +29,8 @@ const COLLECTED_REQUEST_BODY_MAX_STRING_LENGTH = 4096
25
29
  const COLLECTED_REQUEST_BODY_MAX_DEPTH = 20
26
30
  const COLLECTED_REQUEST_BODY_MAX_ELEMENTS_PER_NODE = 256
27
31
 
32
+ const telemetryLogCh = dc.channel('datadog:telemetry:log')
33
+
28
34
  // default limiter, configurable with setRateLimit()
29
35
  let limiter = new Limiter(100)
30
36
 
@@ -216,17 +222,64 @@ function getCollectedHeaders (req, res, shouldCollectEventHeaders, storedRespons
216
222
  function reportWafInit (wafVersion, rulesVersion, diagnosticsRules = {}, success = false) {
217
223
  if (success) {
218
224
  metricsQueue.set('_dd.appsec.waf.version', wafVersion)
219
-
220
- metricsQueue.set('_dd.appsec.event_rules.loaded', diagnosticsRules.loaded?.length || 0)
221
- metricsQueue.set('_dd.appsec.event_rules.error_count', diagnosticsRules.failed?.length || 0)
222
- if (diagnosticsRules.failed?.length) {
223
- metricsQueue.set('_dd.appsec.event_rules.errors', JSON.stringify(diagnosticsRules.errors))
224
- }
225
225
  }
226
226
 
227
227
  incrementWafInitMetric(wafVersion, rulesVersion, success)
228
228
  }
229
229
 
230
+ function logWafDiagnosticMessage (product, rcConfigId, configKey, message, level) {
231
+ const tags =
232
+ `log_type:rc::${product.toLowerCase()}::diagnostic,appsec_config_key:${configKey},rc_config_id:${rcConfigId}`
233
+ telemetryLogCh.publish({
234
+ message,
235
+ level,
236
+ tags
237
+ })
238
+ }
239
+
240
+ function reportWafConfigUpdate (product, rcConfigId, diagnostics, wafVersion) {
241
+ if (diagnostics.error) {
242
+ logWafDiagnosticMessage(product, rcConfigId, '', diagnostics.error, 'ERROR')
243
+ incrementWafConfigErrorsMetric(wafVersion, diagnostics.ruleset_version)
244
+ }
245
+
246
+ for (const configKey of DIAGNOSTIC_KEYS) {
247
+ const configDiagnostics = diagnostics[configKey]
248
+ if (!configDiagnostics) continue
249
+
250
+ if (configDiagnostics.error) {
251
+ logWafDiagnosticMessage(product, rcConfigId, configKey, configDiagnostics.error, 'ERROR')
252
+ incrementWafConfigErrorsMetric(wafVersion, diagnostics.ruleset_version)
253
+ continue
254
+ }
255
+
256
+ if (configDiagnostics.errors) {
257
+ for (const [errorMessage, errorIds] of Object.entries(configDiagnostics.errors)) {
258
+ logWafDiagnosticMessage(
259
+ product,
260
+ rcConfigId,
261
+ configKey,
262
+ `"${errorMessage}": ${JSON.stringify(errorIds)}`,
263
+ 'ERROR'
264
+ )
265
+ incrementWafConfigErrorsMetric(wafVersion, diagnostics.ruleset_version)
266
+ }
267
+ }
268
+
269
+ if (configDiagnostics.warnings) {
270
+ for (const [warningMessage, warningIds] of Object.entries(configDiagnostics.warnings)) {
271
+ logWafDiagnosticMessage(
272
+ product,
273
+ rcConfigId,
274
+ configKey,
275
+ `"${warningMessage}": ${JSON.stringify(warningIds)}`,
276
+ 'WARN'
277
+ )
278
+ }
279
+ }
280
+ }
281
+ }
282
+
230
283
  function reportMetrics (metrics, raspRule) {
231
284
  const store = storage('legacy').getStore()
232
285
  const rootSpan = store?.req && web.root(store.req)
@@ -377,12 +430,12 @@ function isRaspAttack (events) {
377
430
  return events.some(e => e.rule?.tags?.module === 'rasp')
378
431
  }
379
432
 
380
- function isFingerprintDerivative (derivative) {
381
- return derivative.startsWith('_dd.appsec.fp')
433
+ function isFingerprintAttribute (attribute) {
434
+ return attribute.startsWith('_dd.appsec.fp')
382
435
  }
383
436
 
384
- function reportDerivatives (derivatives) {
385
- if (!derivatives) return
437
+ function reportAttributes (attributes) {
438
+ if (!attributes) return
386
439
 
387
440
  const req = storage('legacy').getStore()?.req
388
441
  const rootSpan = web.root(req)
@@ -390,8 +443,8 @@ function reportDerivatives (derivatives) {
390
443
  if (!rootSpan) return
391
444
 
392
445
  const tags = {}
393
- for (let [tag, value] of Object.entries(derivatives)) {
394
- if (!isFingerprintDerivative(tag)) {
446
+ for (let [tag, value] of Object.entries(attributes)) {
447
+ if (!isFingerprintAttribute(tag)) {
395
448
  const gzippedValue = zlib.gzipSync(JSON.stringify(value))
396
449
  value = gzippedValue.toString('base64')
397
450
  }
@@ -485,11 +538,12 @@ module.exports = {
485
538
  filterExtendedHeaders,
486
539
  formatHeaderName,
487
540
  reportWafInit,
541
+ reportWafConfigUpdate,
488
542
  reportMetrics,
489
543
  reportAttack,
490
544
  reportWafUpdate: incrementWafUpdatesMetric,
491
545
  reportRaspRuleSkipped: updateRaspRuleSkippedMetricTags,
492
- reportDerivatives,
546
+ reportAttributes,
493
547
  finishRequest,
494
548
  mapHeaderAndTags,
495
549
  truncateRequestBody
@@ -2,21 +2,22 @@
2
2
 
3
3
  const fs = require('fs')
4
4
  const waf = require('./waf')
5
+ const { DIAGNOSTIC_KEYS } = require('./waf/diagnostics')
5
6
  const { ACKNOWLEDGED, ERROR } = require('../remote_config/apply_states')
7
+ const Reporter = require('./reporter')
6
8
 
7
9
  const blocking = require('./blocking')
8
10
 
9
- let defaultRules
11
+ const ASM_PRODUCTS = new Set(['ASM', 'ASM_DD', 'ASM_DATA'])
10
12
 
11
- let appliedRulesData = new Map()
12
- let appliedRulesetId
13
- let appliedRulesOverride = new Map()
14
- let appliedExclusions = new Map()
15
- let appliedCustomRules = new Map()
13
+ /*
14
+ ASM Actions must be tracked in order to update the defaultBlockingActions in blocking. These actions are used
15
+ by blockRequest method exposed in the user blocking SDK (see packages/dd-trace/src/appsec/sdk/user_blocking.js)
16
+ */
16
17
  let appliedActions = new Map()
17
18
 
18
19
  function loadRules (config) {
19
- defaultRules = config.rules
20
+ const defaultRules = config.rules
20
21
  ? JSON.parse(fs.readFileSync(config.rules))
21
22
  : require('./recommended.json')
22
23
 
@@ -26,137 +27,63 @@ function loadRules (config) {
26
27
  }
27
28
 
28
29
  function updateWafFromRC ({ toUnapply, toApply, toModify }) {
29
- const batch = new Set()
30
-
31
- const newRulesData = new SpyMap(appliedRulesData)
32
- let newRuleset
33
- let newRulesetId
34
- const newRulesOverride = new SpyMap(appliedRulesOverride)
35
- const newExclusions = new SpyMap(appliedExclusions)
36
- const newCustomRules = new SpyMap(appliedCustomRules)
37
30
  const newActions = new SpyMap(appliedActions)
38
31
 
32
+ let wafUpdated = false
33
+ let wafUpdatedFailed = false
34
+
39
35
  for (const item of toUnapply) {
40
- const { product, id } = item
36
+ if (!ASM_PRODUCTS.has(item.product)) continue
37
+
38
+ try {
39
+ waf.removeConfig(item.path)
40
+
41
+ item.apply_state = ACKNOWLEDGED
42
+ wafUpdated = true
41
43
 
42
- if (product === 'ASM_DATA') {
43
- newRulesData.delete(id)
44
- } else if (product === 'ASM_DD') {
45
- if (appliedRulesetId === id) {
46
- newRuleset = defaultRules
44
+ // ASM actions
45
+ if (item.product === 'ASM') {
46
+ newActions.delete(item.id)
47
47
  }
48
- } else if (product === 'ASM') {
49
- newRulesOverride.delete(id)
50
- newExclusions.delete(id)
51
- newCustomRules.delete(id)
52
- newActions.delete(id)
48
+ } catch (e) {
49
+ item.apply_state = ERROR
50
+ item.apply_error = e.toString()
51
+ wafUpdatedFailed = true
53
52
  }
54
53
  }
55
54
 
56
55
  for (const item of [...toApply, ...toModify]) {
57
- const { product, id, file } = item
58
-
59
- if (product === 'ASM_DATA') {
60
- if (file && file.rules_data && file.rules_data.length) {
61
- newRulesData.set(id, file.rules_data)
62
- }
63
-
64
- batch.add(item)
65
- } else if (product === 'ASM_DD') {
66
- if (appliedRulesetId && appliedRulesetId !== id && newRuleset !== defaultRules) {
67
- item.apply_state = ERROR
68
- item.apply_error = 'Multiple ruleset received in ASM_DD'
69
- } else {
70
- if (file?.rules?.length) {
71
- const { version, metadata, rules, processors, scanners } = file
72
-
73
- newRuleset = { version, metadata, rules, processors, scanners }
74
- newRulesetId = id
75
- }
76
-
77
- batch.add(item)
78
- }
79
- } else if (product === 'ASM') {
80
- if (file?.rules_override?.length) {
81
- newRulesOverride.set(id, file.rules_override)
82
- }
56
+ if (!ASM_PRODUCTS.has(item.product)) continue
83
57
 
84
- if (file?.exclusions?.length) {
85
- newExclusions.set(id, file.exclusions)
86
- }
58
+ try {
59
+ waf.updateConfig(item.product, item.id, item.path, item.file)
87
60
 
88
- if (file?.custom_rules?.length) {
89
- newCustomRules.set(id, file.custom_rules)
90
- }
61
+ item.apply_state = ACKNOWLEDGED
62
+ wafUpdated = true
91
63
 
92
- if (file?.actions?.length) {
93
- newActions.set(id, file.actions)
64
+ // ASM actions
65
+ if (item.product === 'ASM' && item.file?.actions?.length) {
66
+ newActions.set(item.id, item.file.actions)
94
67
  }
95
-
96
- batch.add(item)
68
+ } catch (e) {
69
+ item.apply_state = ERROR
70
+ item.apply_error = e instanceof waf.WafUpdateError
71
+ ? JSON.stringify(extractErrors(e.diagnosticErrors))
72
+ : e.toString()
73
+ wafUpdatedFailed = true
97
74
  }
98
75
  }
99
76
 
100
- let newApplyState = ACKNOWLEDGED
101
- let newApplyError
102
-
103
- if (newRulesData.modified ||
104
- newRuleset ||
105
- newRulesOverride.modified ||
106
- newExclusions.modified ||
107
- newCustomRules.modified ||
108
- newActions.modified
109
- ) {
110
- const payload = newRuleset || {}
111
-
112
- if (newRulesData.modified) {
113
- payload.rules_data = mergeRulesData(newRulesData)
114
- }
115
- if (newRulesOverride.modified) {
116
- payload.rules_override = concatArrays(newRulesOverride)
117
- }
118
- if (newExclusions.modified) {
119
- payload.exclusions = concatArrays(newExclusions)
120
- }
121
- if (newCustomRules.modified) {
122
- payload.custom_rules = concatArrays(newCustomRules)
123
- }
124
- if (newActions.modified) {
125
- payload.actions = concatArrays(newActions)
126
- }
127
-
128
- try {
129
- waf.update(payload)
130
-
131
- if (newRulesData.modified) {
132
- appliedRulesData = newRulesData
133
- }
134
- if (newRuleset) {
135
- appliedRulesetId = newRulesetId
136
- }
137
- if (newRulesOverride.modified) {
138
- appliedRulesOverride = newRulesOverride
139
- }
140
- if (newExclusions.modified) {
141
- appliedExclusions = newExclusions
142
- }
143
- if (newCustomRules.modified) {
144
- appliedCustomRules = newCustomRules
145
- }
146
- if (newActions.modified) {
147
- appliedActions = newActions
77
+ waf.checkAsmDdFallback()
148
78
 
149
- blocking.setDefaultBlockingActionParameters(concatArrays(newActions))
150
- }
151
- } catch (err) {
152
- newApplyState = ERROR
153
- newApplyError = err.toString()
154
- }
79
+ if (wafUpdated) {
80
+ Reporter.reportWafUpdate(waf.wafManager.ddwafVersion, waf.wafManager.rulesVersion, !wafUpdatedFailed)
155
81
  }
156
82
 
157
- for (const config of batch) {
158
- config.apply_state = newApplyState
159
- if (newApplyError) config.apply_error = newApplyError
83
+ // Manage blocking actions
84
+ if (newActions.modified) {
85
+ appliedActions = newActions
86
+ blocking.setDefaultBlockingActionParameters(concatArrays(newActions))
160
87
  }
161
88
  }
162
89
 
@@ -188,67 +115,32 @@ function concatArrays (files) {
188
115
  return [...files.values()].flat()
189
116
  }
190
117
 
191
- /*
192
- ASM_DATA Merge strategy:
193
- The merge should be based on the id and type. For any duplicate items, the longer expiration should be taken.
194
- As a result, multiple Rule Data may use the same DATA_ID and DATA_TYPE. In this case, all values are considered part
195
- of a set and are merged. For instance, a denylist customized by environment may use a global Rule Data for all
196
- environments and a Rule Data per environment
197
- */
198
-
199
- function mergeRulesData (files) {
200
- const mergedRulesData = new Map()
201
- for (const [, file] of files) {
202
- for (const ruleData of file) {
203
- const key = `${ruleData.id}+${ruleData.type}`
204
- if (mergedRulesData.has(key)) {
205
- const existingRulesData = mergedRulesData.get(key)
206
- ruleData.data.reduce(rulesReducer, existingRulesData.data)
207
- } else {
208
- mergedRulesData.set(key, copyRulesData(ruleData))
209
- }
118
+ function extractErrors (diagnostics) {
119
+ if (!diagnostics) return
120
+
121
+ if (diagnostics.error) return diagnostics
122
+
123
+ const result = {}
124
+ let isResultPopulated = false
125
+
126
+ for (const diagnosticKey of DIAGNOSTIC_KEYS) {
127
+ if (diagnostics[diagnosticKey]?.error) {
128
+ (result[diagnosticKey] ??= {}).error = diagnostics[diagnosticKey]?.error
129
+ isResultPopulated = true
210
130
  }
211
- }
212
- return [...mergedRulesData.values()]
213
- }
214
131
 
215
- function rulesReducer (existingEntries, rulesDataEntry) {
216
- const existingEntry = existingEntries.find((entry) => entry.value === rulesDataEntry.value)
217
- if (existingEntry && !('expiration' in existingEntry)) return existingEntries
218
- if (existingEntry && 'expiration' in rulesDataEntry && rulesDataEntry.expiration > existingEntry.expiration) {
219
- existingEntry.expiration = rulesDataEntry.expiration
220
- } else if (existingEntry && !('expiration' in rulesDataEntry)) {
221
- delete existingEntry.expiration
222
- } else if (!existingEntry) {
223
- existingEntries.push({ ...rulesDataEntry })
132
+ if (diagnostics[diagnosticKey]?.errors) {
133
+ (result[diagnosticKey] ??= {}).errors = diagnostics[diagnosticKey]?.errors
134
+ isResultPopulated = true
135
+ }
224
136
  }
225
- return existingEntries
226
- }
227
137
 
228
- function copyRulesData (rulesData) {
229
- const copy = { ...rulesData }
230
- if (copy.data) {
231
- const data = []
232
- copy.data.forEach(item => {
233
- data.push({ ...item })
234
- })
235
- copy.data = data
236
- }
237
- return copy
138
+ return isResultPopulated ? result : null
238
139
  }
239
140
 
240
141
  function clearAllRules () {
241
142
  waf.destroy()
242
-
243
- defaultRules = undefined
244
-
245
- appliedRulesData.clear()
246
- appliedRulesetId = undefined
247
- appliedRulesOverride.clear()
248
- appliedExclusions.clear()
249
- appliedCustomRules.clear()
250
143
  appliedActions.clear()
251
-
252
144
  blocking.setDefaultBlockingActionParameters(undefined)
253
145
  }
254
146