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.
- package/LICENSE-3rdparty.csv +2 -1
- package/init.js +1 -4
- package/package.json +6 -4
- package/packages/datadog-instrumentations/src/azure-functions.js +1 -1
- package/packages/datadog-instrumentations/src/child_process.js +1 -1
- package/packages/datadog-instrumentations/src/graphql.js +9 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +1 -1
- package/packages/datadog-instrumentations/src/mysql2.js +6 -6
- package/packages/datadog-instrumentations/src/next.js +3 -1
- package/packages/datadog-instrumentations/src/oracledb.js +24 -2
- package/packages/datadog-instrumentations/src/tedious.js +12 -17
- package/packages/datadog-plugin-aws-sdk/src/base.js +51 -1
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -1
- package/packages/datadog-plugin-cypress/src/support.js +19 -25
- package/packages/datadog-plugin-graphql/src/tools/index.js +0 -2
- package/packages/datadog-plugin-graphql/src/tools/signature.js +0 -2
- package/packages/datadog-plugin-graphql/src/tools/transforms.js +0 -2
- package/packages/datadog-plugin-http/src/client.js +3 -4
- package/packages/datadog-plugin-http2/src/client.js +9 -8
- package/packages/datadog-plugin-langchain/src/handlers/chain.js +1 -1
- package/packages/datadog-plugin-langchain/src/handlers/language_models/chat_model.js +1 -1
- package/packages/datadog-plugin-langchain/src/handlers/language_models/llm.js +1 -1
- package/packages/datadog-plugin-oracledb/src/connection-parser.js +35 -0
- package/packages/datadog-plugin-oracledb/src/index.js +15 -17
- package/packages/datadog-plugin-tedious/src/index.js +10 -9
- package/packages/dd-trace/src/appsec/iast/analyzers/injection-analyzer.js +6 -4
- package/packages/dd-trace/src/appsec/iast/analyzers/ssrf-analyzer.js +9 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +5 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +1 -0
- package/packages/dd-trace/src/appsec/reporter.js +61 -7
- package/packages/dd-trace/src/appsec/rule_manager.js +63 -171
- package/packages/dd-trace/src/appsec/sdk/track_event.js +3 -5
- package/packages/dd-trace/src/appsec/telemetry/common.js +1 -1
- package/packages/dd-trace/src/appsec/telemetry/index.js +8 -0
- package/packages/dd-trace/src/appsec/telemetry/waf.js +5 -3
- package/packages/dd-trace/src/appsec/waf/diagnostics.js +15 -0
- package/packages/dd-trace/src/appsec/waf/index.js +47 -6
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +22 -12
- package/packages/dd-trace/src/config.js +11 -4
- package/packages/dd-trace/src/constants.js +1 -2
- package/packages/dd-trace/src/exporters/common/request.js +1 -1
- package/packages/dd-trace/src/heap_snapshots.js +58 -0
- package/packages/dd-trace/src/llmobs/noop.js +1 -1
- package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
- package/packages/dd-trace/src/log/log.js +1 -1
- package/packages/dd-trace/src/opentracing/span.js +1 -1
- package/packages/dd-trace/src/payload-tagging/index.js +1 -1
- package/packages/dd-trace/src/payload-tagging/tagging.js +2 -2
- package/packages/dd-trace/src/plugins/outbound.js +7 -0
- package/packages/dd-trace/src/profiling/profilers/wall.js +2 -2
- package/packages/dd-trace/src/proxy.js +4 -0
- package/packages/dd-trace/src/service-naming/schemas/definition.js +2 -9
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +2 -1
- package/packages/dd-trace/src/supported-configurations.json +3 -0
- 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
|
-
|
|
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
|
-
|
|
15
|
-
|
|
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':
|
|
25
|
-
'db.hostname':
|
|
26
|
-
[CLIENT_PORT_KEY]:
|
|
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
|
-
|
|
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:
|
|
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':
|
|
22
|
-
[CLIENT_PORT_KEY]:
|
|
23
|
-
'db.user':
|
|
24
|
-
'db.name':
|
|
25
|
-
'db.instance':
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
11
|
+
const ASM_PRODUCTS = new Set(['ASM', 'ASM_DD', 'ASM_DATA'])
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (appliedRulesetId === id) {
|
|
46
|
-
newRuleset = defaultRules
|
|
44
|
+
// ASM actions
|
|
45
|
+
if (item.product === 'ASM') {
|
|
46
|
+
newActions.delete(item.id)
|
|
47
47
|
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
58
|
+
try {
|
|
59
|
+
waf.updateConfig(item.product, item.id, item.path, item.file)
|
|
87
60
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
61
|
+
item.apply_state = ACKNOWLEDGED
|
|
62
|
+
wafUpdated = true
|
|
91
63
|
|
|
92
|
-
|
|
93
|
-
|
|
64
|
+
// ASM actions
|
|
65
|
+
if (item.product === 'ASM' && item.file?.actions?.length) {
|
|
66
|
+
newActions.set(item.id, item.file.actions)
|
|
94
67
|
}
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
-
|
|
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
|
-
|
|
172
|
-
result[key] = value
|
|
173
|
-
}
|
|
170
|
+
} else if (value !== undefined) {
|
|
171
|
+
result[key] = value
|
|
174
172
|
}
|
|
175
173
|
}
|
|
176
174
|
|
|
@@ -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
|
-
|
|
108
|
-
|
|
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
|
}
|