dd-trace 5.21.0 → 5.23.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 (153) hide show
  1. package/LICENSE-3rdparty.csv +2 -0
  2. package/index.d.ts +20 -8
  3. package/package.json +11 -5
  4. package/packages/datadog-instrumentations/src/aerospike.js +1 -1
  5. package/packages/datadog-instrumentations/src/apollo-server.js +1 -1
  6. package/packages/datadog-instrumentations/src/aws-sdk.js +4 -4
  7. package/packages/datadog-instrumentations/src/body-parser.js +4 -4
  8. package/packages/datadog-instrumentations/src/cassandra-driver.js +2 -2
  9. package/packages/datadog-instrumentations/src/child_process.js +2 -2
  10. package/packages/datadog-instrumentations/src/connect.js +4 -4
  11. package/packages/datadog-instrumentations/src/cookie-parser.js +4 -4
  12. package/packages/datadog-instrumentations/src/couchbase.js +12 -12
  13. package/packages/datadog-instrumentations/src/cucumber.js +294 -56
  14. package/packages/datadog-instrumentations/src/dns.js +10 -10
  15. package/packages/datadog-instrumentations/src/elasticsearch.js +4 -4
  16. package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +3 -3
  17. package/packages/datadog-instrumentations/src/express.js +4 -4
  18. package/packages/datadog-instrumentations/src/fastify.js +6 -6
  19. package/packages/datadog-instrumentations/src/fetch.js +1 -1
  20. package/packages/datadog-instrumentations/src/find-my-way.js +2 -2
  21. package/packages/datadog-instrumentations/src/fs.js +2 -2
  22. package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +2 -2
  23. package/packages/datadog-instrumentations/src/grpc/client.js +4 -6
  24. package/packages/datadog-instrumentations/src/grpc/server.js +2 -2
  25. package/packages/datadog-instrumentations/src/hapi.js +10 -13
  26. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  27. package/packages/datadog-instrumentations/src/http/client.js +3 -3
  28. package/packages/datadog-instrumentations/src/jest.js +8 -5
  29. package/packages/datadog-instrumentations/src/kafkajs.js +67 -31
  30. package/packages/datadog-instrumentations/src/knex.js +2 -2
  31. package/packages/datadog-instrumentations/src/koa.js +5 -5
  32. package/packages/datadog-instrumentations/src/ldapjs.js +1 -1
  33. package/packages/datadog-instrumentations/src/mariadb.js +8 -8
  34. package/packages/datadog-instrumentations/src/memcached.js +2 -2
  35. package/packages/datadog-instrumentations/src/microgateway-core.js +7 -5
  36. package/packages/datadog-instrumentations/src/mocha/common.js +1 -1
  37. package/packages/datadog-instrumentations/src/mocha/main.js +139 -53
  38. package/packages/datadog-instrumentations/src/mocha/utils.js +37 -18
  39. package/packages/datadog-instrumentations/src/mocha/worker.js +29 -1
  40. package/packages/datadog-instrumentations/src/mocha.js +4 -0
  41. package/packages/datadog-instrumentations/src/moleculer/server.js +2 -2
  42. package/packages/datadog-instrumentations/src/mongodb-core.js +7 -7
  43. package/packages/datadog-instrumentations/src/mongoose.js +5 -6
  44. package/packages/datadog-instrumentations/src/mysql.js +3 -3
  45. package/packages/datadog-instrumentations/src/mysql2.js +6 -6
  46. package/packages/datadog-instrumentations/src/net.js +2 -2
  47. package/packages/datadog-instrumentations/src/next.js +5 -5
  48. package/packages/datadog-instrumentations/src/openai.js +62 -71
  49. package/packages/datadog-instrumentations/src/oracledb.js +8 -8
  50. package/packages/datadog-instrumentations/src/passport-http.js +1 -1
  51. package/packages/datadog-instrumentations/src/passport-local.js +1 -1
  52. package/packages/datadog-instrumentations/src/passport-utils.js +1 -1
  53. package/packages/datadog-instrumentations/src/pg.js +60 -5
  54. package/packages/datadog-instrumentations/src/pino.js +4 -4
  55. package/packages/datadog-instrumentations/src/playwright.js +6 -4
  56. package/packages/datadog-instrumentations/src/redis.js +2 -2
  57. package/packages/datadog-instrumentations/src/restify.js +4 -4
  58. package/packages/datadog-instrumentations/src/rhea.js +4 -4
  59. package/packages/datadog-instrumentations/src/router.js +5 -5
  60. package/packages/datadog-instrumentations/src/sharedb.js +2 -2
  61. package/packages/datadog-instrumentations/src/vitest.js +188 -12
  62. package/packages/datadog-instrumentations/src/winston.js +2 -3
  63. package/packages/datadog-plugin-amqplib/src/consumer.js +1 -3
  64. package/packages/datadog-plugin-aws-sdk/src/base.js +33 -0
  65. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
  66. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +2 -0
  67. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -1
  68. package/packages/datadog-plugin-cucumber/src/index.js +24 -1
  69. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +39 -10
  70. package/packages/datadog-plugin-cypress/src/support.js +4 -1
  71. package/packages/datadog-plugin-hapi/src/index.js +2 -2
  72. package/packages/datadog-plugin-http/src/client.js +1 -42
  73. package/packages/datadog-plugin-http2/src/client.js +1 -26
  74. package/packages/datadog-plugin-jest/src/index.js +18 -1
  75. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +20 -0
  76. package/packages/datadog-plugin-kafkajs/src/consumer.js +1 -2
  77. package/packages/datadog-plugin-kafkajs/src/index.js +3 -1
  78. package/packages/datadog-plugin-mocha/src/index.js +18 -0
  79. package/packages/datadog-plugin-openai/src/index.js +85 -65
  80. package/packages/datadog-plugin-playwright/src/index.js +9 -0
  81. package/packages/datadog-plugin-rhea/src/consumer.js +1 -3
  82. package/packages/datadog-plugin-vitest/src/index.js +68 -3
  83. package/packages/datadog-shimmer/src/shimmer.js +144 -10
  84. package/packages/dd-trace/src/appsec/addresses.js +3 -1
  85. package/packages/dd-trace/src/appsec/blocking.js +23 -17
  86. package/packages/dd-trace/src/appsec/channels.js +4 -2
  87. package/packages/dd-trace/src/appsec/graphql.js +3 -1
  88. package/packages/dd-trace/src/appsec/iast/iast-log.js +2 -1
  89. package/packages/dd-trace/src/appsec/rasp/index.js +103 -0
  90. package/packages/dd-trace/src/appsec/rasp/sql_injection.js +86 -0
  91. package/packages/dd-trace/src/appsec/rasp/ssrf.js +37 -0
  92. package/packages/dd-trace/src/appsec/rasp/utils.js +63 -0
  93. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -0
  94. package/packages/dd-trace/src/appsec/remote_config/index.js +16 -7
  95. package/packages/dd-trace/src/appsec/remote_config/manager.js +93 -52
  96. package/packages/dd-trace/src/appsec/rule_manager.js +8 -0
  97. package/packages/dd-trace/src/appsec/telemetry.js +3 -3
  98. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +33 -14
  99. package/packages/dd-trace/src/appsec/waf/waf_manager.js +2 -1
  100. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +4 -0
  101. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +15 -1
  102. package/packages/dd-trace/src/config.js +100 -40
  103. package/packages/dd-trace/src/constants.js +11 -1
  104. package/packages/dd-trace/src/data_streams_context.js +3 -0
  105. package/packages/dd-trace/src/datastreams/fnv.js +23 -0
  106. package/packages/dd-trace/src/datastreams/pathway.js +12 -5
  107. package/packages/dd-trace/src/datastreams/processor.js +35 -0
  108. package/packages/dd-trace/src/datastreams/schemas/schema.js +8 -0
  109. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +125 -0
  110. package/packages/dd-trace/src/datastreams/schemas/schema_sampler.js +29 -0
  111. package/packages/dd-trace/src/debugger/devtools_client/config.js +24 -0
  112. package/packages/dd-trace/src/debugger/devtools_client/index.js +57 -0
  113. package/packages/dd-trace/src/debugger/devtools_client/inspector_promises_polyfill.js +23 -0
  114. package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +164 -0
  115. package/packages/dd-trace/src/debugger/devtools_client/send.js +28 -0
  116. package/packages/dd-trace/src/debugger/devtools_client/session.js +7 -0
  117. package/packages/dd-trace/src/debugger/devtools_client/state.js +47 -0
  118. package/packages/dd-trace/src/debugger/devtools_client/status.js +109 -0
  119. package/packages/dd-trace/src/debugger/index.js +92 -0
  120. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +29 -2
  121. package/packages/dd-trace/src/exporters/common/request.js +1 -1
  122. package/packages/dd-trace/src/lambda/handler.js +1 -0
  123. package/packages/dd-trace/src/lambda/index.js +12 -1
  124. package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -6
  125. package/packages/dd-trace/src/payload-tagging/config/aws.json +30 -0
  126. package/packages/dd-trace/src/payload-tagging/config/index.js +30 -0
  127. package/packages/dd-trace/src/payload-tagging/index.js +93 -0
  128. package/packages/dd-trace/src/payload-tagging/tagging.js +83 -0
  129. package/packages/dd-trace/src/plugin_manager.js +11 -10
  130. package/packages/dd-trace/src/plugins/ci_plugin.js +33 -8
  131. package/packages/dd-trace/src/plugins/util/env.js +5 -2
  132. package/packages/dd-trace/src/plugins/util/test.js +24 -4
  133. package/packages/dd-trace/src/profiler.js +15 -5
  134. package/packages/dd-trace/src/profiling/config.js +7 -4
  135. package/packages/dd-trace/src/profiling/exporter_cli.js +13 -1
  136. package/packages/dd-trace/src/profiling/exporters/agent.js +8 -2
  137. package/packages/dd-trace/src/profiling/profiler.js +0 -9
  138. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns.js +13 -0
  139. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookup.js +16 -0
  140. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookupservice.js +16 -0
  141. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_resolve.js +24 -0
  142. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_reverse.js +16 -0
  143. package/packages/dd-trace/src/profiling/profilers/event_plugins/event.js +48 -0
  144. package/packages/dd-trace/src/profiling/profilers/event_plugins/net.js +24 -0
  145. package/packages/dd-trace/src/profiling/profilers/events.js +108 -32
  146. package/packages/dd-trace/src/profiling/profilers/shared.js +5 -0
  147. package/packages/dd-trace/src/profiling/profilers/wall.js +9 -3
  148. package/packages/dd-trace/src/profiling/ssi-heuristics.js +59 -60
  149. package/packages/dd-trace/src/proxy.js +31 -24
  150. package/packages/dd-trace/src/span_stats.js +4 -2
  151. package/packages/dd-trace/src/telemetry/index.js +23 -6
  152. package/packages/dd-trace/src/telemetry/logs/index.js +20 -0
  153. package/packages/dd-trace/src/appsec/rasp.js +0 -176
@@ -0,0 +1,37 @@
1
+ 'use strict'
2
+
3
+ const { httpClientRequestStart } = require('../channels')
4
+ const { storage } = require('../../../../datadog-core')
5
+ const addresses = require('../addresses')
6
+ const waf = require('../waf')
7
+ const { RULE_TYPES, handleResult } = require('./utils')
8
+
9
+ let config
10
+
11
+ function enable (_config) {
12
+ config = _config
13
+ httpClientRequestStart.subscribe(analyzeSsrf)
14
+ }
15
+
16
+ function disable () {
17
+ if (httpClientRequestStart.hasSubscribers) httpClientRequestStart.unsubscribe(analyzeSsrf)
18
+ }
19
+
20
+ function analyzeSsrf (ctx) {
21
+ const store = storage.getStore()
22
+ const req = store?.req
23
+ const url = ctx.args.uri
24
+
25
+ if (!req || !url) return
26
+
27
+ const persistent = {
28
+ [addresses.HTTP_OUTGOING_URL]: url
29
+ }
30
+
31
+ const result = waf.run({ persistent }, req, RULE_TYPES.SSRF)
32
+
33
+ const res = store?.res
34
+ handleResult(result, req, res, ctx.abortController, config)
35
+ }
36
+
37
+ module.exports = { enable, disable }
@@ -0,0 +1,63 @@
1
+ 'use strict'
2
+
3
+ const web = require('../../plugins/util/web')
4
+ const { reportStackTrace } = require('../stack_trace')
5
+ const { getBlockingAction } = require('../blocking')
6
+ const log = require('../../log')
7
+
8
+ const abortOnUncaughtException = process.execArgv?.includes('--abort-on-uncaught-exception')
9
+
10
+ if (abortOnUncaughtException) {
11
+ log.warn('The --abort-on-uncaught-exception flag is enabled. The RASP module will not block operations.')
12
+ }
13
+
14
+ const RULE_TYPES = {
15
+ SSRF: 'ssrf',
16
+ SQL_INJECTION: 'sql_injection'
17
+ }
18
+
19
+ class DatadogRaspAbortError extends Error {
20
+ constructor (req, res, blockingAction) {
21
+ super('DatadogRaspAbortError')
22
+ this.name = 'DatadogRaspAbortError'
23
+ this.req = req
24
+ this.res = res
25
+ this.blockingAction = blockingAction
26
+ }
27
+ }
28
+
29
+ function handleResult (actions, req, res, abortController, config) {
30
+ const generateStackTraceAction = actions?.generate_stack
31
+ if (generateStackTraceAction && config.appsec.stackTrace.enabled) {
32
+ const rootSpan = web.root(req)
33
+ reportStackTrace(
34
+ rootSpan,
35
+ generateStackTraceAction.stack_id,
36
+ config.appsec.stackTrace.maxDepth,
37
+ config.appsec.stackTrace.maxStackTraces
38
+ )
39
+ }
40
+
41
+ if (!abortController || abortOnUncaughtException) return
42
+
43
+ const blockingAction = getBlockingAction(actions)
44
+ if (blockingAction) {
45
+ const rootSpan = web.root(req)
46
+ // Should block only in express
47
+ if (rootSpan?.context()._name === 'express.request') {
48
+ const abortError = new DatadogRaspAbortError(req, res, blockingAction)
49
+ abortController.abort(abortError)
50
+
51
+ // TODO Delete this when support for node 16 is removed
52
+ if (!abortController.signal.reason) {
53
+ abortController.signal.reason = abortError
54
+ }
55
+ }
56
+ }
57
+ }
58
+
59
+ module.exports = {
60
+ handleResult,
61
+ RULE_TYPES,
62
+ DatadogRaspAbortError
63
+ }
@@ -17,5 +17,7 @@ module.exports = {
17
17
  APM_TRACING_HTTP_HEADER_TAGS: 1n << 14n,
18
18
  APM_TRACING_CUSTOM_TAGS: 1n << 15n,
19
19
  APM_TRACING_ENABLED: 1n << 19n,
20
+ ASM_RASP_SQLI: 1n << 21n,
21
+ ASM_RASP_SSRF: 1n << 23n,
20
22
  APM_TRACING_SAMPLE_RULES: 1n << 29n
21
23
  }
@@ -28,7 +28,7 @@ function enable (config, appsec) {
28
28
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_API_SECURITY_SAMPLE_RATE, true)
29
29
  }
30
30
 
31
- rc.on('ASM_FEATURES', (action, rcConfig) => {
31
+ rc.setProductHandler('ASM_FEATURES', (action, rcConfig) => {
32
32
  if (!rcConfig) return
33
33
 
34
34
  if (activation === Activation.ONECLICK) {
@@ -76,9 +76,15 @@ function enableWafUpdate (appsecConfig) {
76
76
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_BLOCKING_RESPONSE, true)
77
77
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_TRUSTED_IPS, true)
78
78
 
79
- rc.on('ASM_DATA', noop)
80
- rc.on('ASM_DD', noop)
81
- rc.on('ASM', noop)
79
+ if (appsecConfig.rasp?.enabled) {
80
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SQLI, true)
81
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SSRF, true)
82
+ }
83
+
84
+ // TODO: delete noop handlers and kPreUpdate and replace with batched handlers
85
+ rc.setProductHandler('ASM_DATA', noop)
86
+ rc.setProductHandler('ASM_DD', noop)
87
+ rc.setProductHandler('ASM', noop)
82
88
 
83
89
  rc.on(RemoteConfigManager.kPreUpdate, RuleManager.updateWafFromRC)
84
90
  }
@@ -98,9 +104,12 @@ function disableWafUpdate () {
98
104
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_BLOCKING_RESPONSE, false)
99
105
  rc.updateCapabilities(RemoteConfigCapabilities.ASM_TRUSTED_IPS, false)
100
106
 
101
- rc.off('ASM_DATA', noop)
102
- rc.off('ASM_DD', noop)
103
- rc.off('ASM', noop)
107
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SQLI, false)
108
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SSRF, false)
109
+
110
+ rc.removeProductHandler('ASM_DATA')
111
+ rc.removeProductHandler('ASM_DD')
112
+ rc.removeProductHandler('ASM')
104
113
 
105
114
  rc.off(RemoteConfigManager.kPreUpdate, RuleManager.updateWafFromRC)
106
115
  }
@@ -15,6 +15,7 @@ const clientId = uuid()
15
15
  const DEFAULT_CAPABILITY = Buffer.alloc(1).toString('base64') // 0x00
16
16
 
17
17
  const kPreUpdate = Symbol('kPreUpdate')
18
+ const kSupportsAckCallback = Symbol('kSupportsAckCallback')
18
19
 
19
20
  // There MUST NOT exist separate instances of RC clients in a tracer making separate ClientGetConfigsRequest
20
21
  // with their own separated Client.ClientState.
@@ -32,14 +33,26 @@ class RemoteConfigManager extends EventEmitter {
32
33
  port: config.port
33
34
  }))
34
35
 
36
+ this._handlers = new Map()
37
+ const appliedConfigs = this.appliedConfigs = new Map()
38
+
35
39
  this.scheduler = new Scheduler((cb) => this.poll(cb), pollInterval)
36
40
 
37
41
  this.state = {
38
42
  client: {
39
- state: { // updated by `parseConfig()`
43
+ state: { // updated by `parseConfig()` and `poll()`
40
44
  root_version: 1,
41
45
  targets_version: 0,
42
- config_states: [],
46
+ // Use getter so `apply_*` can be updated async and still affect the content of `config_states`
47
+ get config_states () {
48
+ return Array.from(appliedConfigs.values()).map((conf) => ({
49
+ id: conf.id,
50
+ version: conf.version,
51
+ product: conf.product,
52
+ apply_state: conf.apply_state,
53
+ apply_error: conf.apply_error
54
+ }))
55
+ },
43
56
  has_error: false,
44
57
  error: '',
45
58
  backend_client_state: ''
@@ -60,8 +73,6 @@ class RemoteConfigManager extends EventEmitter {
60
73
  },
61
74
  cached_target_files: [] // updated by `parseConfig()`
62
75
  }
63
-
64
- this.appliedConfigs = new Map()
65
76
  }
66
77
 
67
78
  updateCapabilities (mask, value) {
@@ -82,32 +93,24 @@ class RemoteConfigManager extends EventEmitter {
82
93
  this.state.client.capabilities = Buffer.from(str, 'hex').toString('base64')
83
94
  }
84
95
 
85
- on (event, listener) {
86
- super.on(event, listener)
87
-
96
+ setProductHandler (product, handler) {
97
+ this._handlers.set(product, handler)
88
98
  this.updateProducts()
89
-
90
- if (this.state.client.products.length) {
99
+ if (this.state.client.products.length === 1) {
91
100
  this.scheduler.start()
92
101
  }
93
-
94
- return this
95
102
  }
96
103
 
97
- off (event, listener) {
98
- super.off(event, listener)
99
-
104
+ removeProductHandler (product) {
105
+ this._handlers.delete(product)
100
106
  this.updateProducts()
101
-
102
- if (!this.state.client.products.length) {
107
+ if (this.state.client.products.length === 0) {
103
108
  this.scheduler.stop()
104
109
  }
105
-
106
- return this
107
110
  }
108
111
 
109
112
  updateProducts () {
110
- this.state.client.products = this.eventNames().filter(e => typeof e === 'string')
113
+ this.state.client.products = Array.from(this._handlers.keys())
111
114
  }
112
115
 
113
116
  getPayload () {
@@ -120,7 +123,10 @@ class RemoteConfigManager extends EventEmitter {
120
123
  const options = {
121
124
  url: this.url,
122
125
  method: 'POST',
123
- path: '/v0.7/config'
126
+ path: '/v0.7/config',
127
+ headers: {
128
+ 'Content-Type': 'application/json; charset=utf-8'
129
+ }
124
130
  }
125
131
 
126
132
  request(this.getPayload(), options, (err, data, statusCode) => {
@@ -225,24 +231,11 @@ class RemoteConfigManager extends EventEmitter {
225
231
  this.dispatch(toApply, 'apply')
226
232
  this.dispatch(toModify, 'modify')
227
233
 
228
- this.state.client.state.config_states = []
229
- this.state.cached_target_files = []
230
-
231
- for (const conf of this.appliedConfigs.values()) {
232
- this.state.client.state.config_states.push({
233
- id: conf.id,
234
- version: conf.version,
235
- product: conf.product,
236
- apply_state: conf.apply_state,
237
- apply_error: conf.apply_error
238
- })
239
-
240
- this.state.cached_target_files.push({
241
- path: conf.path,
242
- length: conf.length,
243
- hashes: Object.entries(conf.hashes).map((entry) => ({ algorithm: entry[0], hash: entry[1] }))
244
- })
245
- }
234
+ this.state.cached_target_files = Array.from(this.appliedConfigs.values()).map((conf) => ({
235
+ path: conf.path,
236
+ length: conf.length,
237
+ hashes: Object.entries(conf.hashes).map((entry) => ({ algorithm: entry[0], hash: entry[1] }))
238
+ }))
246
239
  }
247
240
  }
248
241
 
@@ -251,20 +244,7 @@ class RemoteConfigManager extends EventEmitter {
251
244
  // TODO: we need a way to tell if unapply configs were handled by kPreUpdate or not, because they're always
252
245
  // emitted unlike the apply and modify configs
253
246
 
254
- // in case the item was already handled by kPreUpdate
255
- if (item.apply_state === UNACKNOWLEDGED || action === 'unapply') {
256
- try {
257
- // TODO: do we want to pass old and new config ?
258
- const hadListeners = this.emit(item.product, action, item.file, item.id)
259
-
260
- if (hadListeners) {
261
- item.apply_state = ACKNOWLEDGED
262
- }
263
- } catch (err) {
264
- item.apply_state = ERROR
265
- item.apply_error = err.toString()
266
- }
267
- }
247
+ this._callHandlerFor(action, item)
268
248
 
269
249
  if (action === 'unapply') {
270
250
  this.appliedConfigs.delete(item.path)
@@ -273,6 +253,49 @@ class RemoteConfigManager extends EventEmitter {
273
253
  }
274
254
  }
275
255
  }
256
+
257
+ _callHandlerFor (action, item) {
258
+ // in case the item was already handled by kPreUpdate
259
+ if (item.apply_state !== UNACKNOWLEDGED && action !== 'unapply') return
260
+
261
+ const handler = this._handlers.get(item.product)
262
+
263
+ if (!handler) return
264
+
265
+ try {
266
+ if (supportsAckCallback(handler)) {
267
+ // If the handler accepts an `ack` callback, expect that to be called and set `apply_state` accordinly
268
+ // TODO: do we want to pass old and new config ?
269
+ handler(action, item.file, item.id, (err) => {
270
+ if (err) {
271
+ item.apply_state = ERROR
272
+ item.apply_error = err.toString()
273
+ } else if (item.apply_state !== ERROR) {
274
+ item.apply_state = ACKNOWLEDGED
275
+ }
276
+ })
277
+ } else {
278
+ // If the handler doesn't accept an `ack` callback, assume `apply_state` is `ACKNOWLEDGED`,
279
+ // unless it returns a promise, in which case we wait for the promise to be resolved or rejected.
280
+ // TODO: do we want to pass old and new config ?
281
+ const result = handler(action, item.file, item.id)
282
+ if (result instanceof Promise) {
283
+ result.then(
284
+ () => { item.apply_state = ACKNOWLEDGED },
285
+ (err) => {
286
+ item.apply_state = ERROR
287
+ item.apply_error = err.toString()
288
+ }
289
+ )
290
+ } else {
291
+ item.apply_state = ACKNOWLEDGED
292
+ }
293
+ }
294
+ } catch (err) {
295
+ item.apply_state = ERROR
296
+ item.apply_error = err.toString()
297
+ }
298
+ }
276
299
  }
277
300
 
278
301
  function fromBase64JSON (str) {
@@ -296,4 +319,22 @@ function parseConfigPath (configPath) {
296
319
  }
297
320
  }
298
321
 
322
+ function supportsAckCallback (handler) {
323
+ if (kSupportsAckCallback in handler) return handler[kSupportsAckCallback]
324
+
325
+ const numOfArgs = handler.length
326
+ let result = false
327
+
328
+ if (numOfArgs >= 4) {
329
+ result = true
330
+ } else if (numOfArgs !== 0) {
331
+ const source = handler.toString()
332
+ result = source.slice(0, source.indexOf(')')).includes('...')
333
+ }
334
+
335
+ handler[kSupportsAckCallback] = result
336
+
337
+ return result
338
+ }
339
+
299
340
  module.exports = RemoteConfigManager
@@ -4,6 +4,8 @@ const fs = require('fs')
4
4
  const waf = require('./waf')
5
5
  const { ACKNOWLEDGED, ERROR } = require('./remote_config/apply_states')
6
6
 
7
+ const blocking = require('./blocking')
8
+
7
9
  let defaultRules
8
10
 
9
11
  let appliedRulesData = new Map()
@@ -19,6 +21,8 @@ function loadRules (config) {
19
21
  : require('./recommended.json')
20
22
 
21
23
  waf.init(defaultRules, config)
24
+
25
+ blocking.setDefaultBlockingActionParameters(defaultRules?.actions)
22
26
  }
23
27
 
24
28
  function updateWafFromRC ({ toUnapply, toApply, toModify }) {
@@ -141,6 +145,8 @@ function updateWafFromRC ({ toUnapply, toApply, toModify }) {
141
145
  }
142
146
  if (newActions.modified) {
143
147
  appliedActions = newActions
148
+
149
+ blocking.setDefaultBlockingActionParameters(concatArrays(newActions))
144
150
  }
145
151
  } catch (err) {
146
152
  newApplyState = ERROR
@@ -242,6 +248,8 @@ function clearAllRules () {
242
248
  appliedExclusions.clear()
243
249
  appliedCustomRules.clear()
244
250
  appliedActions.clear()
251
+
252
+ blocking.setDefaultBlockingActionParameters(undefined)
245
253
  }
246
254
 
247
255
  module.exports = {
@@ -90,14 +90,14 @@ function updateRaspRequestsMetricTags (metrics, req, raspRuleType) {
90
90
  if (!enabled) return
91
91
 
92
92
  const tags = { rule_type: raspRuleType, waf_version: metrics.wafVersion }
93
- appsecMetrics.count('appsec.rasp.rule.eval', tags).inc(1)
93
+ appsecMetrics.count('rasp.rule.eval', tags).inc(1)
94
94
 
95
95
  if (metrics.wafTimeout) {
96
- appsecMetrics.count('appsec.rasp.timeout', tags).inc(1)
96
+ appsecMetrics.count('rasp.timeout', tags).inc(1)
97
97
  }
98
98
 
99
99
  if (metrics.ruleTriggered) {
100
- appsecMetrics.count('appsec.rasp.rule.match', tags).inc(1)
100
+ appsecMetrics.count('rasp.rule.match', tags).inc(1)
101
101
  }
102
102
  }
103
103
 
@@ -4,6 +4,7 @@ const log = require('../../log')
4
4
  const Reporter = require('../reporter')
5
5
  const addresses = require('../addresses')
6
6
  const { getBlockingAction } = require('../blocking')
7
+ const { wafRunFinished } = require('../channels')
7
8
 
8
9
  // TODO: remove once ephemeral addresses are implemented
9
10
  const preventDuplicateAddresses = new Set([
@@ -11,42 +12,56 @@ const preventDuplicateAddresses = new Set([
11
12
  ])
12
13
 
13
14
  class WAFContextWrapper {
14
- constructor (ddwafContext, wafTimeout, wafVersion, rulesVersion) {
15
+ constructor (ddwafContext, wafTimeout, wafVersion, rulesVersion, knownAddresses) {
15
16
  this.ddwafContext = ddwafContext
16
17
  this.wafTimeout = wafTimeout
17
18
  this.wafVersion = wafVersion
18
19
  this.rulesVersion = rulesVersion
19
20
  this.addressesToSkip = new Set()
21
+ this.knownAddresses = knownAddresses
20
22
  }
21
23
 
22
24
  run ({ persistent, ephemeral }, raspRuleType) {
25
+ if (this.ddwafContext.disposed) {
26
+ log.warn('Calling run on a disposed context')
27
+ return
28
+ }
29
+
23
30
  const payload = {}
24
31
  let payloadHasData = false
25
- const inputs = {}
26
32
  const newAddressesToSkip = new Set(this.addressesToSkip)
27
33
 
28
34
  if (persistent !== null && typeof persistent === 'object') {
29
- // TODO: possible optimization: only send params that haven't already been sent with same value to this wafContext
35
+ const persistentInputs = {}
36
+
30
37
  for (const key of Object.keys(persistent)) {
31
- // TODO: requiredAddresses is no longer used due to processor addresses are not included in the list. Check on
32
- // future versions when the actual addresses are included in the 'loaded' section inside diagnostics.
33
- if (!this.addressesToSkip.has(key)) {
34
- inputs[key] = persistent[key]
38
+ if (!this.addressesToSkip.has(key) && this.knownAddresses.has(key)) {
39
+ persistentInputs[key] = persistent[key]
35
40
  if (preventDuplicateAddresses.has(key)) {
36
41
  newAddressesToSkip.add(key)
37
42
  }
38
43
  }
39
44
  }
40
- }
41
45
 
42
- if (Object.keys(inputs).length) {
43
- payload.persistent = inputs
44
- payloadHasData = true
46
+ if (Object.keys(persistentInputs).length) {
47
+ payload.persistent = persistentInputs
48
+ payloadHasData = true
49
+ }
45
50
  }
46
51
 
47
- if (ephemeral && Object.keys(ephemeral).length) {
48
- payload.ephemeral = ephemeral
49
- payloadHasData = true
52
+ if (ephemeral !== null && typeof ephemeral === 'object') {
53
+ const ephemeralInputs = {}
54
+
55
+ for (const key of Object.keys(ephemeral)) {
56
+ if (this.knownAddresses.has(key)) {
57
+ ephemeralInputs[key] = ephemeral[key]
58
+ }
59
+ }
60
+
61
+ if (Object.keys(ephemeralInputs).length) {
62
+ payload.ephemeral = ephemeralInputs
63
+ payloadHasData = true
64
+ }
50
65
  }
51
66
 
52
67
  if (!payloadHasData) return
@@ -80,6 +95,10 @@ class WAFContextWrapper {
80
95
 
81
96
  Reporter.reportSchemas(result.derivatives)
82
97
 
98
+ if (wafRunFinished.hasSubscribers) {
99
+ wafRunFinished.publish({ payload })
100
+ }
101
+
83
102
  return result.actions
84
103
  } catch (err) {
85
104
  log.error('Error while running the AppSec WAF')
@@ -39,7 +39,8 @@ class WAFManager {
39
39
  this.ddwaf.createContext(),
40
40
  this.wafTimeout,
41
41
  this.ddwafVersion,
42
- this.rulesVersion
42
+ this.rulesVersion,
43
+ this.ddwaf.knownAddresses
43
44
  )
44
45
  contexts.set(req, wafContext)
45
46
  }
@@ -72,6 +72,10 @@ class Writer extends BaseWriter {
72
72
  done()
73
73
  })
74
74
  }
75
+
76
+ setMetadataTags (tags) {
77
+ this._encoder.setMetadataTags(tags)
78
+ }
75
79
  }
76
80
 
77
81
  module.exports = Writer
@@ -201,7 +201,8 @@ class CiVisibilityExporter extends AgentInfoExporter {
201
201
  isEarlyFlakeDetectionEnabled: isEarlyFlakeDetectionEnabled && this._config.isEarlyFlakeDetectionEnabled,
202
202
  earlyFlakeDetectionNumRetries,
203
203
  earlyFlakeDetectionFaultyThreshold,
204
- isFlakyTestRetriesEnabled
204
+ isFlakyTestRetriesEnabled: isFlakyTestRetriesEnabled && this._config.isFlakyTestRetriesEnabled,
205
+ flakyTestRetriesCount: this._config.flakyTestRetriesCount
205
206
  }
206
207
  }
207
208
 
@@ -290,6 +291,19 @@ class CiVisibilityExporter extends AgentInfoExporter {
290
291
  _getApiUrl () {
291
292
  return this._url
292
293
  }
294
+
295
+ // By the time setMetadataTags is called, the agent info request might not have finished
296
+ setMetadataTags (tags) {
297
+ if (this._writer?.setMetadataTags) {
298
+ this._writer.setMetadataTags(tags)
299
+ } else {
300
+ this._canUseCiVisProtocolPromise.then(() => {
301
+ if (this._writer?.setMetadataTags) {
302
+ this._writer.setMetadataTags(tags)
303
+ }
304
+ })
305
+ }
306
+ }
293
307
  }
294
308
 
295
309
  module.exports = CiVisibilityExporter