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.
- package/LICENSE-3rdparty.csv +2 -0
- package/index.d.ts +20 -8
- package/package.json +11 -5
- package/packages/datadog-instrumentations/src/aerospike.js +1 -1
- package/packages/datadog-instrumentations/src/apollo-server.js +1 -1
- package/packages/datadog-instrumentations/src/aws-sdk.js +4 -4
- package/packages/datadog-instrumentations/src/body-parser.js +4 -4
- package/packages/datadog-instrumentations/src/cassandra-driver.js +2 -2
- package/packages/datadog-instrumentations/src/child_process.js +2 -2
- package/packages/datadog-instrumentations/src/connect.js +4 -4
- package/packages/datadog-instrumentations/src/cookie-parser.js +4 -4
- package/packages/datadog-instrumentations/src/couchbase.js +12 -12
- package/packages/datadog-instrumentations/src/cucumber.js +294 -56
- package/packages/datadog-instrumentations/src/dns.js +10 -10
- package/packages/datadog-instrumentations/src/elasticsearch.js +4 -4
- package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +3 -3
- package/packages/datadog-instrumentations/src/express.js +4 -4
- package/packages/datadog-instrumentations/src/fastify.js +6 -6
- package/packages/datadog-instrumentations/src/fetch.js +1 -1
- package/packages/datadog-instrumentations/src/find-my-way.js +2 -2
- package/packages/datadog-instrumentations/src/fs.js +2 -2
- package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +2 -2
- package/packages/datadog-instrumentations/src/grpc/client.js +4 -6
- package/packages/datadog-instrumentations/src/grpc/server.js +2 -2
- package/packages/datadog-instrumentations/src/hapi.js +10 -13
- package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
- package/packages/datadog-instrumentations/src/http/client.js +3 -3
- package/packages/datadog-instrumentations/src/jest.js +8 -5
- package/packages/datadog-instrumentations/src/kafkajs.js +67 -31
- package/packages/datadog-instrumentations/src/knex.js +2 -2
- package/packages/datadog-instrumentations/src/koa.js +5 -5
- package/packages/datadog-instrumentations/src/ldapjs.js +1 -1
- package/packages/datadog-instrumentations/src/mariadb.js +8 -8
- package/packages/datadog-instrumentations/src/memcached.js +2 -2
- package/packages/datadog-instrumentations/src/microgateway-core.js +7 -5
- package/packages/datadog-instrumentations/src/mocha/common.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +139 -53
- package/packages/datadog-instrumentations/src/mocha/utils.js +37 -18
- package/packages/datadog-instrumentations/src/mocha/worker.js +29 -1
- package/packages/datadog-instrumentations/src/mocha.js +4 -0
- package/packages/datadog-instrumentations/src/moleculer/server.js +2 -2
- package/packages/datadog-instrumentations/src/mongodb-core.js +7 -7
- package/packages/datadog-instrumentations/src/mongoose.js +5 -6
- package/packages/datadog-instrumentations/src/mysql.js +3 -3
- package/packages/datadog-instrumentations/src/mysql2.js +6 -6
- package/packages/datadog-instrumentations/src/net.js +2 -2
- package/packages/datadog-instrumentations/src/next.js +5 -5
- package/packages/datadog-instrumentations/src/openai.js +62 -71
- package/packages/datadog-instrumentations/src/oracledb.js +8 -8
- package/packages/datadog-instrumentations/src/passport-http.js +1 -1
- package/packages/datadog-instrumentations/src/passport-local.js +1 -1
- package/packages/datadog-instrumentations/src/passport-utils.js +1 -1
- package/packages/datadog-instrumentations/src/pg.js +60 -5
- package/packages/datadog-instrumentations/src/pino.js +4 -4
- package/packages/datadog-instrumentations/src/playwright.js +6 -4
- package/packages/datadog-instrumentations/src/redis.js +2 -2
- package/packages/datadog-instrumentations/src/restify.js +4 -4
- package/packages/datadog-instrumentations/src/rhea.js +4 -4
- package/packages/datadog-instrumentations/src/router.js +5 -5
- package/packages/datadog-instrumentations/src/sharedb.js +2 -2
- package/packages/datadog-instrumentations/src/vitest.js +188 -12
- package/packages/datadog-instrumentations/src/winston.js +2 -3
- package/packages/datadog-plugin-amqplib/src/consumer.js +1 -3
- package/packages/datadog-plugin-aws-sdk/src/base.js +33 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +2 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -1
- package/packages/datadog-plugin-cucumber/src/index.js +24 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +39 -10
- package/packages/datadog-plugin-cypress/src/support.js +4 -1
- package/packages/datadog-plugin-hapi/src/index.js +2 -2
- package/packages/datadog-plugin-http/src/client.js +1 -42
- package/packages/datadog-plugin-http2/src/client.js +1 -26
- package/packages/datadog-plugin-jest/src/index.js +18 -1
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +20 -0
- package/packages/datadog-plugin-kafkajs/src/consumer.js +1 -2
- package/packages/datadog-plugin-kafkajs/src/index.js +3 -1
- package/packages/datadog-plugin-mocha/src/index.js +18 -0
- package/packages/datadog-plugin-openai/src/index.js +85 -65
- package/packages/datadog-plugin-playwright/src/index.js +9 -0
- package/packages/datadog-plugin-rhea/src/consumer.js +1 -3
- package/packages/datadog-plugin-vitest/src/index.js +68 -3
- package/packages/datadog-shimmer/src/shimmer.js +144 -10
- package/packages/dd-trace/src/appsec/addresses.js +3 -1
- package/packages/dd-trace/src/appsec/blocking.js +23 -17
- package/packages/dd-trace/src/appsec/channels.js +4 -2
- package/packages/dd-trace/src/appsec/graphql.js +3 -1
- package/packages/dd-trace/src/appsec/iast/iast-log.js +2 -1
- package/packages/dd-trace/src/appsec/rasp/index.js +103 -0
- package/packages/dd-trace/src/appsec/rasp/sql_injection.js +86 -0
- package/packages/dd-trace/src/appsec/rasp/ssrf.js +37 -0
- package/packages/dd-trace/src/appsec/rasp/utils.js +63 -0
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -0
- package/packages/dd-trace/src/appsec/remote_config/index.js +16 -7
- package/packages/dd-trace/src/appsec/remote_config/manager.js +93 -52
- package/packages/dd-trace/src/appsec/rule_manager.js +8 -0
- package/packages/dd-trace/src/appsec/telemetry.js +3 -3
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +33 -14
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +2 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +4 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +15 -1
- package/packages/dd-trace/src/config.js +100 -40
- package/packages/dd-trace/src/constants.js +11 -1
- package/packages/dd-trace/src/data_streams_context.js +3 -0
- package/packages/dd-trace/src/datastreams/fnv.js +23 -0
- package/packages/dd-trace/src/datastreams/pathway.js +12 -5
- package/packages/dd-trace/src/datastreams/processor.js +35 -0
- package/packages/dd-trace/src/datastreams/schemas/schema.js +8 -0
- package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +125 -0
- package/packages/dd-trace/src/datastreams/schemas/schema_sampler.js +29 -0
- package/packages/dd-trace/src/debugger/devtools_client/config.js +24 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +57 -0
- package/packages/dd-trace/src/debugger/devtools_client/inspector_promises_polyfill.js +23 -0
- package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +164 -0
- package/packages/dd-trace/src/debugger/devtools_client/send.js +28 -0
- package/packages/dd-trace/src/debugger/devtools_client/session.js +7 -0
- package/packages/dd-trace/src/debugger/devtools_client/state.js +47 -0
- package/packages/dd-trace/src/debugger/devtools_client/status.js +109 -0
- package/packages/dd-trace/src/debugger/index.js +92 -0
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +29 -2
- package/packages/dd-trace/src/exporters/common/request.js +1 -1
- package/packages/dd-trace/src/lambda/handler.js +1 -0
- package/packages/dd-trace/src/lambda/index.js +12 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -6
- package/packages/dd-trace/src/payload-tagging/config/aws.json +30 -0
- package/packages/dd-trace/src/payload-tagging/config/index.js +30 -0
- package/packages/dd-trace/src/payload-tagging/index.js +93 -0
- package/packages/dd-trace/src/payload-tagging/tagging.js +83 -0
- package/packages/dd-trace/src/plugin_manager.js +11 -10
- package/packages/dd-trace/src/plugins/ci_plugin.js +33 -8
- package/packages/dd-trace/src/plugins/util/env.js +5 -2
- package/packages/dd-trace/src/plugins/util/test.js +24 -4
- package/packages/dd-trace/src/profiler.js +15 -5
- package/packages/dd-trace/src/profiling/config.js +7 -4
- package/packages/dd-trace/src/profiling/exporter_cli.js +13 -1
- package/packages/dd-trace/src/profiling/exporters/agent.js +8 -2
- package/packages/dd-trace/src/profiling/profiler.js +0 -9
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns.js +13 -0
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookup.js +16 -0
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookupservice.js +16 -0
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_resolve.js +24 -0
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_reverse.js +16 -0
- package/packages/dd-trace/src/profiling/profilers/event_plugins/event.js +48 -0
- package/packages/dd-trace/src/profiling/profilers/event_plugins/net.js +24 -0
- package/packages/dd-trace/src/profiling/profilers/events.js +108 -32
- package/packages/dd-trace/src/profiling/profilers/shared.js +5 -0
- package/packages/dd-trace/src/profiling/profilers/wall.js +9 -3
- package/packages/dd-trace/src/profiling/ssi-heuristics.js +59 -60
- package/packages/dd-trace/src/proxy.js +31 -24
- package/packages/dd-trace/src/span_stats.js +4 -2
- package/packages/dd-trace/src/telemetry/index.js +23 -6
- package/packages/dd-trace/src/telemetry/logs/index.js +20 -0
- 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
|
+
}
|
|
@@ -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.
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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.
|
|
102
|
-
rc.
|
|
103
|
-
|
|
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
|
-
|
|
86
|
-
|
|
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
|
-
|
|
98
|
-
|
|
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 =
|
|
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.
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
-
|
|
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('
|
|
93
|
+
appsecMetrics.count('rasp.rule.eval', tags).inc(1)
|
|
94
94
|
|
|
95
95
|
if (metrics.wafTimeout) {
|
|
96
|
-
appsecMetrics.count('
|
|
96
|
+
appsecMetrics.count('rasp.timeout', tags).inc(1)
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
if (metrics.ruleTriggered) {
|
|
100
|
-
appsecMetrics.count('
|
|
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
|
-
|
|
35
|
+
const persistentInputs = {}
|
|
36
|
+
|
|
30
37
|
for (const key of Object.keys(persistent)) {
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
if (Object.keys(persistentInputs).length) {
|
|
47
|
+
payload.persistent = persistentInputs
|
|
48
|
+
payloadHasData = true
|
|
49
|
+
}
|
|
45
50
|
}
|
|
46
51
|
|
|
47
|
-
if (ephemeral &&
|
|
48
|
-
|
|
49
|
-
|
|
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')
|
|
@@ -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
|