dd-trace 5.57.1 → 5.58.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 (56) hide show
  1. package/LICENSE-3rdparty.csv +2 -1
  2. package/init.js +1 -4
  3. package/package.json +6 -4
  4. package/packages/datadog-instrumentations/src/azure-functions.js +1 -1
  5. package/packages/datadog-instrumentations/src/child_process.js +1 -1
  6. package/packages/datadog-instrumentations/src/graphql.js +9 -0
  7. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  8. package/packages/datadog-instrumentations/src/jest.js +1 -1
  9. package/packages/datadog-instrumentations/src/mysql2.js +6 -6
  10. package/packages/datadog-instrumentations/src/next.js +3 -1
  11. package/packages/datadog-instrumentations/src/oracledb.js +24 -2
  12. package/packages/datadog-instrumentations/src/tedious.js +12 -17
  13. package/packages/datadog-plugin-aws-sdk/src/base.js +51 -1
  14. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -1
  15. package/packages/datadog-plugin-cypress/src/support.js +19 -25
  16. package/packages/datadog-plugin-graphql/src/tools/index.js +0 -2
  17. package/packages/datadog-plugin-graphql/src/tools/signature.js +0 -2
  18. package/packages/datadog-plugin-graphql/src/tools/transforms.js +0 -2
  19. package/packages/datadog-plugin-http/src/client.js +3 -4
  20. package/packages/datadog-plugin-http2/src/client.js +9 -8
  21. package/packages/datadog-plugin-langchain/src/handlers/chain.js +1 -1
  22. package/packages/datadog-plugin-langchain/src/handlers/language_models/chat_model.js +1 -1
  23. package/packages/datadog-plugin-langchain/src/handlers/language_models/llm.js +1 -1
  24. package/packages/datadog-plugin-oracledb/src/connection-parser.js +35 -0
  25. package/packages/datadog-plugin-oracledb/src/index.js +15 -17
  26. package/packages/datadog-plugin-tedious/src/index.js +10 -9
  27. package/packages/dd-trace/src/appsec/iast/analyzers/injection-analyzer.js +6 -4
  28. package/packages/dd-trace/src/appsec/iast/analyzers/ssrf-analyzer.js +9 -0
  29. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +5 -2
  30. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +1 -0
  31. package/packages/dd-trace/src/appsec/reporter.js +61 -7
  32. package/packages/dd-trace/src/appsec/rule_manager.js +63 -171
  33. package/packages/dd-trace/src/appsec/sdk/track_event.js +3 -5
  34. package/packages/dd-trace/src/appsec/telemetry/common.js +1 -1
  35. package/packages/dd-trace/src/appsec/telemetry/index.js +8 -0
  36. package/packages/dd-trace/src/appsec/telemetry/waf.js +5 -3
  37. package/packages/dd-trace/src/appsec/waf/diagnostics.js +15 -0
  38. package/packages/dd-trace/src/appsec/waf/index.js +47 -6
  39. package/packages/dd-trace/src/appsec/waf/waf_manager.js +22 -12
  40. package/packages/dd-trace/src/config.js +11 -4
  41. package/packages/dd-trace/src/constants.js +1 -2
  42. package/packages/dd-trace/src/exporters/common/request.js +1 -1
  43. package/packages/dd-trace/src/heap_snapshots.js +58 -0
  44. package/packages/dd-trace/src/llmobs/noop.js +1 -1
  45. package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
  46. package/packages/dd-trace/src/log/log.js +1 -1
  47. package/packages/dd-trace/src/opentracing/span.js +1 -1
  48. package/packages/dd-trace/src/payload-tagging/index.js +1 -1
  49. package/packages/dd-trace/src/payload-tagging/tagging.js +2 -2
  50. package/packages/dd-trace/src/plugins/outbound.js +7 -0
  51. package/packages/dd-trace/src/profiling/profilers/wall.js +2 -2
  52. package/packages/dd-trace/src/proxy.js +4 -0
  53. package/packages/dd-trace/src/service-naming/schemas/definition.js +2 -9
  54. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +2 -1
  55. package/packages/dd-trace/src/supported-configurations.json +3 -0
  56. package/packages/dd-trace/src/payload-tagging/jsonpath-plus.js +0 -2094
@@ -2,17 +2,25 @@
2
2
 
3
3
  const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
4
4
  const DatabasePlugin = require('../../dd-trace/src/plugins/database')
5
- const log = require('../../dd-trace/src/log')
5
+
6
+ let parser
6
7
 
7
8
  class OracledbPlugin extends DatabasePlugin {
8
9
  static get id () { return 'oracledb' }
9
10
  static get system () { return 'oracle' }
10
11
  static get peerServicePrecursors () { return ['db.instance', 'db.hostname'] }
11
12
 
12
- start ({ query, connAttrs }) {
13
+ start ({ query, connAttrs, port, hostname, dbInstance }) {
13
14
  const service = this.serviceName({ pluginConfig: this.config, params: connAttrs })
14
- // Users can pass either connectString or connectionString
15
- const url = getUrl(connAttrs.connectString || connAttrs.connectionString)
15
+
16
+ if (hostname === undefined) {
17
+ // Lazy load for performance. This is not needed in v6 and up
18
+ parser ??= require('./connection-parser')
19
+ const dbInfo = parser(connAttrs)
20
+ hostname = dbInfo.hostname
21
+ port ??= dbInfo.port
22
+ dbInstance ??= dbInfo.dbInstance
23
+ }
16
24
 
17
25
  this.startSpan(this.operationName(), {
18
26
  service,
@@ -21,22 +29,12 @@ class OracledbPlugin extends DatabasePlugin {
21
29
  kind: 'client',
22
30
  meta: {
23
31
  'db.user': this.config.user,
24
- 'db.instance': url.pathname && url.pathname.slice(1),
25
- 'db.hostname': url.hostname,
26
- [CLIENT_PORT_KEY]: url.port
32
+ 'db.instance': dbInstance,
33
+ 'db.hostname': hostname,
34
+ [CLIENT_PORT_KEY]: port,
27
35
  }
28
36
  })
29
37
  }
30
38
  }
31
39
 
32
- // TODO: Avoid creating an error since it's a heavy operation.
33
- function getUrl (connectString) {
34
- try {
35
- return new URL(`http://${connectString}`)
36
- } catch (e) {
37
- log.error('Invalid oracle connection string', e)
38
- return {}
39
- }
40
- }
41
-
42
40
  module.exports = OracledbPlugin
@@ -8,27 +8,28 @@ class TediousPlugin extends DatabasePlugin {
8
8
  static get operation () { return 'request' } // TODO: change to match other database plugins
9
9
  static get system () { return 'mssql' }
10
10
 
11
- start (payload) {
11
+ bindStart (ctx) {
12
12
  const service = this.serviceName({ pluginConfig: this.config, system: this.system })
13
13
  const span = this.startSpan(this.operationName(), {
14
14
  service,
15
- resource: payload.queryOrProcedure,
15
+ resource: ctx.queryOrProcedure,
16
16
  type: 'sql',
17
17
  kind: 'client',
18
18
  meta: {
19
19
  'db.type': 'mssql',
20
20
  component: 'tedious',
21
- 'out.host': payload.connectionConfig.server,
22
- [CLIENT_PORT_KEY]: payload.connectionConfig.options.port,
23
- 'db.user': payload.connectionConfig.userName || payload.connectionConfig.authentication.options.userName,
24
- 'db.name': payload.connectionConfig.options.database,
25
- 'db.instance': payload.connectionConfig.options.instanceName
21
+ 'out.host': ctx.connectionConfig.server,
22
+ [CLIENT_PORT_KEY]: ctx.connectionConfig.options.port,
23
+ 'db.user': ctx.connectionConfig.userName || ctx.connectionConfig.authentication.options.userName,
24
+ 'db.name': ctx.connectionConfig.options.database,
25
+ 'db.instance': ctx.connectionConfig.options.instanceName
26
26
  }
27
- })
27
+ }, ctx)
28
28
 
29
29
  // SQL Server includes comments when caching queries
30
30
  // For that reason we allow service mode but not full mode
31
- payload.sql = this.injectDbmQuery(span, payload.queryOrProcedure, service, true)
31
+ ctx.sql = this.injectDbmQuery(span, ctx.queryOrProcedure, service, true)
32
+ return ctx.currentStore
32
33
  }
33
34
  }
34
35
 
@@ -7,7 +7,7 @@ class InjectionAnalyzer extends Analyzer {
7
7
  _isVulnerable (value, iastContext) {
8
8
  let ranges = value && getRanges(iastContext, value)
9
9
  if (ranges?.length > 0) {
10
- ranges = this._filterSecureRanges(ranges)
10
+ ranges = this._filterSecureRanges(ranges, value)
11
11
  if (!ranges?.length) {
12
12
  this._incrementSuppressedMetric(iastContext)
13
13
  }
@@ -27,11 +27,13 @@ class InjectionAnalyzer extends Analyzer {
27
27
  return ranges?.some(range => range.iinfo.type !== SQL_ROW_VALUE)
28
28
  }
29
29
 
30
- _filterSecureRanges (ranges) {
31
- return ranges?.filter(range => !this._isRangeSecure(range))
30
+ _filterSecureRanges (ranges, value) {
31
+ return ranges?.filter(range => !this._isRangeSecure(range, value))
32
32
  }
33
33
 
34
- _isRangeSecure (range) {
34
+ _isRangeSecure (range, _value) {
35
+ // _value is not necessary in this method, but could be used in overridden methods
36
+ // added here for visibility
35
37
  const { secureMarks } = range
36
38
  return (secureMarks & this._secureMark) === this._secureMark
37
39
  }
@@ -23,6 +23,15 @@ class SSRFAnalyzer extends InjectionAnalyzer {
23
23
  }
24
24
  })
25
25
  }
26
+
27
+ _isRangeSecure (range, value) {
28
+ const fragmentIndex = value.indexOf('#')
29
+ if (fragmentIndex !== -1 && range.start >= fragmentIndex) {
30
+ return true
31
+ }
32
+
33
+ return super._isRangeSecure(range, value)
34
+ }
26
35
  }
27
36
 
28
37
  module.exports = new SSRFAnalyzer()
@@ -28,7 +28,10 @@ function taintObject (iastContext, object, type) {
28
28
  } else {
29
29
  result = tainted
30
30
  }
31
- } else if (typeof value === 'object' && !visited.has(value)) {
31
+ } else if (
32
+ // eslint-disable-next-line eslint-rules/eslint-safe-typeof-object
33
+ typeof value === 'object' && !visited.has(value)
34
+ ) {
32
35
  visited.add(value)
33
36
 
34
37
  for (const key of Object.keys(value)) {
@@ -69,7 +72,7 @@ function traverseAndTaint (node, path, cache, transactionId) {
69
72
  return tainted
70
73
  }
71
74
 
72
- if (typeof node === 'object') {
75
+ if (typeof node === 'object') { // eslint-disable-line eslint-rules/eslint-safe-typeof-object
73
76
  const keys = Array.isArray(node) ? node.keys() : Object.keys(node)
74
77
 
75
78
  for (const key of keys) {
@@ -45,6 +45,7 @@ class VulnerabilityFormatter {
45
45
 
46
46
  if (evidence.value == null) return { valueParts }
47
47
 
48
+ // eslint-disable-next-line eslint-rules/eslint-safe-typeof-object
48
49
  if (typeof evidence.value === 'object' && evidence.rangesToApply) {
49
50
  const { value, ranges } = stringifyWithRanges(evidence.value, evidence.rangesToApply)
50
51
  evidence.value = value
@@ -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)
@@ -485,6 +538,7 @@ module.exports = {
485
538
  filterExtendedHeaders,
486
539
  formatHeaderName,
487
540
  reportWafInit,
541
+ reportWafConfigUpdate,
488
542
  reportMetrics,
489
543
  reportAttack,
490
544
  reportWafUpdate: incrementWafUpdatesMetric,
@@ -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
 
@@ -130,7 +130,7 @@ function trackUserLoginFailureV2 (tracer, login, exists, metadata) {
130
130
 
131
131
  const wafData = { login }
132
132
 
133
- if (typeof exists === 'object' && metadata === undefined) {
133
+ if (exists !== null && typeof exists === 'object' && metadata === undefined) {
134
134
  metadata = exists
135
135
  exists = false
136
136
  }
@@ -167,10 +167,8 @@ function flattenFields (fields, depth = 0) {
167
167
  result[`${key}.${flatKey}`] = flatValue[flatKey]
168
168
  }
169
169
  }
170
- } else {
171
- if (value !== undefined) {
172
- result[key] = value
173
- }
170
+ } else if (value !== undefined) {
171
+ result[key] = value
174
172
  }
175
173
  }
176
174
 
@@ -17,7 +17,7 @@ const tags = {
17
17
  function getVersionsTags (wafVersion, rulesVersion) {
18
18
  return {
19
19
  [tags.WAF_VERSION]: wafVersion,
20
- [tags.EVENT_RULES_VERSION]: rulesVersion
20
+ [tags.EVENT_RULES_VERSION]: rulesVersion || 'unknown'
21
21
  }
22
22
  }
23
23
 
@@ -13,6 +13,7 @@ const {
13
13
  trackWafMetrics,
14
14
  incrementWafInit,
15
15
  incrementWafUpdates,
16
+ incrementWafConfigErrors,
16
17
  incrementWafRequests
17
18
  } = require('./waf')
18
19
  const telemetryMetrics = require('../../telemetry/metrics')
@@ -151,6 +152,12 @@ function incrementWafUpdatesMetric (wafVersion, rulesVersion, success) {
151
152
  incrementWafUpdates(wafVersion, rulesVersion, success)
152
153
  }
153
154
 
155
+ function incrementWafConfigErrorsMetric (wafVersion, rulesVersion) {
156
+ if (!enabled) return
157
+
158
+ incrementWafConfigErrors(wafVersion, rulesVersion)
159
+ }
160
+
154
161
  function incrementWafRequestsMetric (req) {
155
162
  if (!req || !enabled) return
156
163
 
@@ -197,6 +204,7 @@ module.exports = {
197
204
  updateRaspRuleSkippedMetricTags,
198
205
  incrementWafInitMetric,
199
206
  incrementWafUpdatesMetric,
207
+ incrementWafConfigErrorsMetric,
200
208
  incrementWafRequestsMetric,
201
209
  incrementMissingUserLoginMetric,
202
210
  incrementMissingUserIdMetric,
@@ -103,10 +103,11 @@ function incrementWafInit (wafVersion, rulesVersion, success) {
103
103
  function incrementWafUpdates (wafVersion, rulesVersion, success) {
104
104
  const versionsTags = getVersionsTags(wafVersion, rulesVersion)
105
105
  appsecMetrics.count('waf.updates', { ...versionsTags, success }).inc()
106
+ }
106
107
 
107
- if (!success) {
108
- appsecMetrics.count('waf.config_errors', versionsTags).inc()
109
- }
108
+ function incrementWafConfigErrors (wafVersion, rulesVersion) {
109
+ const versionsTags = getVersionsTags(wafVersion, rulesVersion)
110
+ appsecMetrics.count('waf.config_errors', versionsTags).inc()
110
111
  }
111
112
 
112
113
  function incrementWafRequests (store) {
@@ -137,5 +138,6 @@ module.exports = {
137
138
  trackWafMetrics,
138
139
  incrementWafInit,
139
140
  incrementWafUpdates,
141
+ incrementWafConfigErrors,
140
142
  incrementWafRequests
141
143
  }
@@ -0,0 +1,15 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ DIAGNOSTIC_KEYS: [
5
+ 'rules',
6
+ 'custom_rules',
7
+ 'exclusions',
8
+ 'actions',
9
+ 'processors',
10
+ 'scanners',
11
+ 'rules_override',
12
+ 'rules_data',
13
+ 'exclusion_data'
14
+ ]
15
+ }