dd-trace 5.28.0 → 5.29.1
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 +8 -2
- package/ci/init.js +16 -0
- package/index.d.ts +31 -13
- package/init.js +4 -68
- package/loader-hook.mjs +4 -0
- package/package.json +16 -11
- package/packages/datadog-core/src/storage.js +39 -2
- package/packages/datadog-instrumentations/src/aerospike.js +1 -1
- package/packages/datadog-instrumentations/src/cucumber.js +29 -3
- package/packages/datadog-instrumentations/src/express.js +38 -4
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +3 -3
- package/packages/datadog-instrumentations/src/helpers/hooks.js +0 -1
- package/packages/datadog-instrumentations/src/helpers/register.js +3 -4
- package/packages/datadog-instrumentations/src/http/client.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +27 -8
- package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
- package/packages/datadog-instrumentations/src/mysql2.js +13 -8
- package/packages/datadog-instrumentations/src/next.js +7 -4
- package/packages/datadog-instrumentations/src/passport-http.js +2 -14
- package/packages/datadog-instrumentations/src/passport-local.js +2 -14
- package/packages/datadog-instrumentations/src/passport-utils.js +43 -19
- package/packages/datadog-instrumentations/src/pg.js +6 -6
- package/packages/datadog-instrumentations/src/playwright.js +17 -4
- package/packages/datadog-instrumentations/src/router.js +97 -1
- package/packages/datadog-instrumentations/src/sequelize.js +9 -4
- package/packages/datadog-instrumentations/src/url.js +4 -0
- package/packages/datadog-instrumentations/src/vitest.js +27 -2
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +8 -3
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +154 -0
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/util.js +92 -0
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -1
- package/packages/datadog-plugin-cucumber/src/index.js +39 -4
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +3 -3
- package/packages/datadog-plugin-grpc/src/client.js +2 -2
- package/packages/datadog-plugin-grpc/src/util.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +39 -4
- package/packages/datadog-plugin-mocha/src/index.js +36 -2
- package/packages/datadog-plugin-oracledb/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +34 -2
- package/packages/datadog-shimmer/src/shimmer.js +8 -4
- package/packages/dd-trace/src/appsec/addresses.js +3 -0
- package/packages/dd-trace/src/appsec/blocked_templates.js +1 -1
- package/packages/dd-trace/src/appsec/channels.js +1 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/code-injection-analyzer.js +4 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-rules.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-rules.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/injection-analyzer.js +10 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +4 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/template-injection-analyzer.js +4 -0
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +6 -19
- package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +3 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +64 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +2 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +2 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +32 -37
- package/packages/dd-trace/src/appsec/index.js +16 -10
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +1 -0
- package/packages/dd-trace/src/appsec/remote_config/index.js +25 -1
- package/packages/dd-trace/src/appsec/reporter.js +3 -1
- package/packages/dd-trace/src/appsec/sdk/track_event.js +32 -19
- package/packages/dd-trace/src/appsec/telemetry.js +10 -0
- package/packages/dd-trace/src/appsec/user_tracking.js +168 -0
- package/packages/dd-trace/src/azure_metadata.js +4 -4
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +5 -4
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +39 -3
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +29 -9
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
- package/packages/dd-trace/src/config.js +24 -32
- package/packages/dd-trace/src/constants.js +1 -0
- package/packages/dd-trace/src/crashtracking/crashtracker.js +3 -2
- package/packages/dd-trace/src/datastreams/processor.js +4 -6
- package/packages/dd-trace/src/datastreams/writer.js +6 -5
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +80 -0
- package/packages/dd-trace/src/debugger/devtools_client/config.js +3 -1
- package/packages/dd-trace/src/debugger/devtools_client/defaults.js +6 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +63 -8
- package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +10 -67
- package/packages/dd-trace/src/debugger/devtools_client/send.js +2 -1
- package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/status.js +4 -4
- package/packages/dd-trace/src/debugger/index.js +14 -10
- package/packages/dd-trace/src/dogstatsd.js +2 -2
- package/packages/dd-trace/src/encode/0.4.js +23 -78
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +0 -32
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +1 -2
- package/packages/dd-trace/src/encode/span-stats.js +0 -30
- package/packages/dd-trace/src/exporters/agent/writer.js +3 -3
- package/packages/dd-trace/src/exporters/common/request.js +1 -1
- package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -1
- package/packages/dd-trace/src/flare/index.js +1 -1
- package/packages/dd-trace/src/guardrails/index.js +64 -0
- package/packages/dd-trace/src/guardrails/log.js +32 -0
- package/packages/dd-trace/src/guardrails/telemetry.js +78 -0
- package/packages/dd-trace/src/guardrails/util.js +10 -0
- package/packages/dd-trace/src/lambda/runtime/ritm.js +2 -2
- package/packages/dd-trace/src/llmobs/storage.js +2 -3
- package/packages/dd-trace/src/llmobs/writers/base.js +2 -2
- package/packages/dd-trace/src/log/channels.js +9 -2
- package/packages/dd-trace/src/log/index.js +11 -1
- package/packages/dd-trace/src/log/writer.js +14 -3
- package/packages/dd-trace/src/{encode → msgpack}/chunk.js +8 -5
- package/packages/dd-trace/src/msgpack/encoder.js +309 -0
- package/packages/dd-trace/src/msgpack/index.js +6 -0
- package/packages/dd-trace/src/opentelemetry/context_manager.js +2 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +12 -9
- package/packages/dd-trace/src/opentracing/span.js +1 -1
- package/packages/dd-trace/src/opentracing/tracer.js +2 -2
- package/packages/dd-trace/src/plugin_manager.js +4 -2
- package/packages/dd-trace/src/plugins/ci_plugin.js +47 -4
- package/packages/dd-trace/src/plugins/plugin.js +1 -1
- package/packages/dd-trace/src/plugins/tracing.js +1 -1
- package/packages/dd-trace/src/plugins/util/git.js +7 -7
- package/packages/dd-trace/src/plugins/util/test.js +36 -3
- package/packages/dd-trace/src/plugins/util/web.js +2 -2
- package/packages/dd-trace/src/priority_sampler.js +11 -1
- package/packages/dd-trace/src/profiling/config.js +3 -0
- package/packages/dd-trace/src/profiling/exporters/agent.js +9 -68
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +76 -0
- package/packages/dd-trace/src/profiling/exporters/file.js +8 -4
- package/packages/dd-trace/src/profiling/profiler.js +62 -10
- package/packages/dd-trace/src/profiling/profilers/event_plugins/event.js +22 -12
- package/packages/dd-trace/src/profiling/profilers/events.js +47 -8
- package/packages/dd-trace/src/profiling/profilers/wall.js +2 -17
- package/packages/dd-trace/src/profiling/webspan-utils.js +23 -0
- package/packages/dd-trace/src/proxy.js +7 -2
- package/packages/dd-trace/src/runtime_metrics.js +107 -4
- package/packages/dd-trace/src/serverless.js +1 -1
- package/packages/dd-trace/src/span_processor.js +10 -10
- package/packages/dd-trace/src/tagger.js +1 -1
- package/packages/dd-trace/src/telemetry/index.js +1 -0
- package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
- package/packages/dd-trace/src/telemetry/logs/log-collector.js +10 -2
- package/packages/dd-trace/src/telemetry/send-data.js +2 -2
- package/packages/dd-trace/src/util.js +5 -16
- package/packages/datadog-instrumentations/src/qs.js +0 -24
- package/packages/dd-trace/src/appsec/passport.js +0 -110
- package/packages/dd-trace/src/telemetry/init-telemetry.js +0 -75
|
@@ -17,7 +17,12 @@ const {
|
|
|
17
17
|
TEST_SOURCE_START,
|
|
18
18
|
TEST_IS_NEW,
|
|
19
19
|
TEST_EARLY_FLAKE_ENABLED,
|
|
20
|
-
TEST_EARLY_FLAKE_ABORT_REASON
|
|
20
|
+
TEST_EARLY_FLAKE_ABORT_REASON,
|
|
21
|
+
TEST_NAME,
|
|
22
|
+
DI_ERROR_DEBUG_INFO_CAPTURED,
|
|
23
|
+
DI_DEBUG_ERROR_SNAPSHOT_ID,
|
|
24
|
+
DI_DEBUG_ERROR_FILE,
|
|
25
|
+
DI_DEBUG_ERROR_LINE
|
|
21
26
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
22
27
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
23
28
|
const {
|
|
@@ -31,6 +36,8 @@ const {
|
|
|
31
36
|
// This is because there's some loss of resolution.
|
|
32
37
|
const MILLISECONDS_TO_SUBTRACT_FROM_FAILED_TEST_DURATION = 5
|
|
33
38
|
|
|
39
|
+
const debuggerParameterPerTest = new Map()
|
|
40
|
+
|
|
34
41
|
class VitestPlugin extends CiPlugin {
|
|
35
42
|
static get id () {
|
|
36
43
|
return 'vitest'
|
|
@@ -81,6 +88,26 @@ class VitestPlugin extends CiPlugin {
|
|
|
81
88
|
extraTags
|
|
82
89
|
)
|
|
83
90
|
|
|
91
|
+
const debuggerParameters = debuggerParameterPerTest.get(testName)
|
|
92
|
+
|
|
93
|
+
if (debuggerParameters) {
|
|
94
|
+
const spanContext = span.context()
|
|
95
|
+
|
|
96
|
+
// TODO: handle race conditions with this.retriedTestIds
|
|
97
|
+
this.retriedTestIds = {
|
|
98
|
+
spanId: spanContext.toSpanId(),
|
|
99
|
+
traceId: spanContext.toTraceId()
|
|
100
|
+
}
|
|
101
|
+
const { snapshotId, file, line } = debuggerParameters
|
|
102
|
+
|
|
103
|
+
// TODO: should these be added on test:end if and only if the probe is hit?
|
|
104
|
+
// Sync issues: `hitProbePromise` might be resolved after the test ends
|
|
105
|
+
span.setTag(DI_ERROR_DEBUG_INFO_CAPTURED, 'true')
|
|
106
|
+
span.setTag(DI_DEBUG_ERROR_SNAPSHOT_ID, snapshotId)
|
|
107
|
+
span.setTag(DI_DEBUG_ERROR_FILE, file)
|
|
108
|
+
span.setTag(DI_DEBUG_ERROR_LINE, line)
|
|
109
|
+
}
|
|
110
|
+
|
|
84
111
|
this.enter(span, store)
|
|
85
112
|
})
|
|
86
113
|
|
|
@@ -110,11 +137,16 @@ class VitestPlugin extends CiPlugin {
|
|
|
110
137
|
}
|
|
111
138
|
})
|
|
112
139
|
|
|
113
|
-
this.addSub('ci:vitest:test:error', ({ duration, error }) => {
|
|
140
|
+
this.addSub('ci:vitest:test:error', ({ duration, error, willBeRetried, probe, isDiEnabled }) => {
|
|
114
141
|
const store = storage.getStore()
|
|
115
142
|
const span = store?.span
|
|
116
143
|
|
|
117
144
|
if (span) {
|
|
145
|
+
if (willBeRetried && this.di && isDiEnabled) {
|
|
146
|
+
const testName = span.context()._tags[TEST_NAME]
|
|
147
|
+
const debuggerParameters = this.addDiProbe(error, probe)
|
|
148
|
+
debuggerParameterPerTest.set(testName, debuggerParameters)
|
|
149
|
+
}
|
|
118
150
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test', {
|
|
119
151
|
hasCodeowners: !!span.context()._tags[TEST_CODE_OWNERS]
|
|
120
152
|
})
|
|
@@ -6,8 +6,12 @@ const log = require('../../dd-trace/src/log')
|
|
|
6
6
|
const unwrappers = new WeakMap()
|
|
7
7
|
|
|
8
8
|
function copyProperties (original, wrapped) {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
// TODO getPrototypeOf is not fast. Should we instead do this in specific
|
|
10
|
+
// instrumentations where needed?
|
|
11
|
+
const proto = Object.getPrototypeOf(original)
|
|
12
|
+
if (proto !== Function.prototype) {
|
|
13
|
+
Object.setPrototypeOf(wrapped, proto)
|
|
14
|
+
}
|
|
11
15
|
const props = Object.getOwnPropertyDescriptors(original)
|
|
12
16
|
const keys = Reflect.ownKeys(props)
|
|
13
17
|
|
|
@@ -136,7 +140,7 @@ function wrapMethod (target, name, wrapper, noAssert) {
|
|
|
136
140
|
if (callState.completed) {
|
|
137
141
|
// error was thrown after original function returned/resolved, so
|
|
138
142
|
// it was us. log it.
|
|
139
|
-
log.error(e)
|
|
143
|
+
log.error('Shimmer error was thrown after original function returned/resolved', e)
|
|
140
144
|
// original ran and returned something. return it.
|
|
141
145
|
return callState.retVal
|
|
142
146
|
}
|
|
@@ -144,7 +148,7 @@ function wrapMethod (target, name, wrapper, noAssert) {
|
|
|
144
148
|
if (!callState.called) {
|
|
145
149
|
// error was thrown before original function was called, so
|
|
146
150
|
// it was us. log it.
|
|
147
|
-
log.error(e)
|
|
151
|
+
log.error('Shimmer error was thrown before original function was called', e)
|
|
148
152
|
// original never ran. call it unwrapped.
|
|
149
153
|
return original.apply(this, args)
|
|
150
154
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
// TODO: reorder all this, it's a mess
|
|
3
4
|
module.exports = {
|
|
4
5
|
HTTP_INCOMING_BODY: 'server.request.body',
|
|
5
6
|
HTTP_INCOMING_QUERY: 'server.request.query',
|
|
@@ -20,6 +21,8 @@ module.exports = {
|
|
|
20
21
|
HTTP_CLIENT_IP: 'http.client_ip',
|
|
21
22
|
|
|
22
23
|
USER_ID: 'usr.id',
|
|
24
|
+
USER_LOGIN: 'usr.login',
|
|
25
|
+
|
|
23
26
|
WAF_CONTEXT_PROCESSOR: 'waf.context.processor',
|
|
24
27
|
|
|
25
28
|
HTTP_OUTGOING_URL: 'server.io.net.url',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* eslint-disable max-len */
|
|
1
|
+
/* eslint-disable @stylistic/js/max-len */
|
|
2
2
|
'use strict'
|
|
3
3
|
|
|
4
4
|
const html = `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>You've been blocked</title><style>a,body,div,html,span{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}body{background:-webkit-radial-gradient(26% 19%,circle,#fff,#f4f7f9);background:radial-gradient(circle at 26% 19%,#fff,#f4f7f9);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:center;align-content:center;width:100%;min-height:100vh;line-height:1;flex-direction:column}p{display:block}main{text-align:center;flex:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:center;align-content:center;flex-direction:column}p{font-size:18px;line-height:normal;color:#646464;font-family:sans-serif;font-weight:400}a{color:#4842b7}footer{width:100%;text-align:center}footer p{font-size:16px}</style></head><body><main><p>Sorry, you cannot access this page. Please contact the customer service team.</p></main><footer><p>Security provided by <a href="https://www.datadoghq.com/product/security-platform/application-security-monitoring/" target="_blank">Datadog</a></p></footer></body></html>`
|
|
@@ -19,6 +19,7 @@ module.exports = {
|
|
|
19
19
|
nextBodyParsed: dc.channel('apm:next:body-parsed'),
|
|
20
20
|
nextQueryParsed: dc.channel('apm:next:query-parsed'),
|
|
21
21
|
expressProcessParams: dc.channel('datadog:express:process_params:start'),
|
|
22
|
+
routerParam: dc.channel('datadog:router:param:start'),
|
|
22
23
|
responseBody: dc.channel('datadog:express:response:json:start'),
|
|
23
24
|
responseWriteHead: dc.channel('apm:http:server:response:writeHead:start'),
|
|
24
25
|
httpClientRequestStart: dc.channel('apm:http:client:request:start'),
|
|
@@ -11,6 +11,10 @@ class CodeInjectionAnalyzer extends InjectionAnalyzer {
|
|
|
11
11
|
onConfigure () {
|
|
12
12
|
this.addSub('datadog:eval:call', ({ script }) => this.analyze(script))
|
|
13
13
|
}
|
|
14
|
+
|
|
15
|
+
_areRangesVulnerable () {
|
|
16
|
+
return true
|
|
17
|
+
}
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
module.exports = new CodeInjectionAnalyzer()
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
const Analyzer = require('./vulnerability-analyzer')
|
|
3
|
-
const {
|
|
3
|
+
const { getRanges } = require('../taint-tracking/operations')
|
|
4
|
+
const { SQL_ROW_VALUE } = require('../taint-tracking/source-types')
|
|
4
5
|
|
|
5
6
|
class InjectionAnalyzer extends Analyzer {
|
|
6
7
|
_isVulnerable (value, iastContext) {
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
const ranges = value && getRanges(iastContext, value)
|
|
9
|
+
if (ranges?.length > 0) {
|
|
10
|
+
return this._areRangesVulnerable(ranges)
|
|
9
11
|
}
|
|
12
|
+
|
|
10
13
|
return false
|
|
11
14
|
}
|
|
12
15
|
|
|
@@ -14,6 +17,10 @@ class InjectionAnalyzer extends Analyzer {
|
|
|
14
17
|
const ranges = getRanges(iastContext, value)
|
|
15
18
|
return { value, ranges }
|
|
16
19
|
}
|
|
20
|
+
|
|
21
|
+
_areRangesVulnerable (ranges) {
|
|
22
|
+
return ranges?.some(range => range.iinfo.type !== SQL_ROW_VALUE)
|
|
23
|
+
}
|
|
17
24
|
}
|
|
18
25
|
|
|
19
26
|
module.exports = InjectionAnalyzer
|
|
@@ -13,6 +13,10 @@ class TemplateInjectionAnalyzer extends InjectionAnalyzer {
|
|
|
13
13
|
this.addSub('datadog:handlebars:register-partial:start', ({ partial }) => this.analyze(partial))
|
|
14
14
|
this.addSub('datadog:pug:compile:start', ({ source }) => this.analyze(source))
|
|
15
15
|
}
|
|
16
|
+
|
|
17
|
+
_areRangesVulnerable () {
|
|
18
|
+
return true
|
|
19
|
+
}
|
|
16
20
|
}
|
|
17
21
|
|
|
18
22
|
module.exports = new TemplateInjectionAnalyzer()
|
|
@@ -60,24 +60,10 @@ class IastPlugin extends Plugin {
|
|
|
60
60
|
this.pluginSubs = []
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
_wrapHandler (handler) {
|
|
64
|
-
return (message, name) => {
|
|
65
|
-
try {
|
|
66
|
-
handler(message, name)
|
|
67
|
-
} catch (e) {
|
|
68
|
-
log.error('[ASM] Error executing IAST plugin handler', e)
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
63
|
_getTelemetryHandler (iastSub) {
|
|
74
64
|
return () => {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
iastSub.increaseExecuted(iastContext)
|
|
78
|
-
} catch (e) {
|
|
79
|
-
log.error('[ASM] Error increasing handler executed metrics', e)
|
|
80
|
-
}
|
|
65
|
+
const iastContext = getIastContext(storage.getStore())
|
|
66
|
+
iastSub.increaseExecuted(iastContext)
|
|
81
67
|
}
|
|
82
68
|
}
|
|
83
69
|
|
|
@@ -99,11 +85,11 @@ class IastPlugin extends Plugin {
|
|
|
99
85
|
|
|
100
86
|
addSub (iastSub, handler) {
|
|
101
87
|
if (typeof iastSub === 'string') {
|
|
102
|
-
super.addSub(iastSub,
|
|
88
|
+
super.addSub(iastSub, handler)
|
|
103
89
|
} else {
|
|
104
90
|
iastSub = this._getAndRegisterSubscription(iastSub)
|
|
105
91
|
if (iastSub) {
|
|
106
|
-
super.addSub(iastSub.channelName,
|
|
92
|
+
super.addSub(iastSub.channelName, handler)
|
|
107
93
|
|
|
108
94
|
if (iastTelemetry.isEnabled()) {
|
|
109
95
|
super.addSub(iastSub.channelName, this._getTelemetryHandler(iastSub))
|
|
@@ -112,7 +98,8 @@ class IastPlugin extends Plugin {
|
|
|
112
98
|
}
|
|
113
99
|
}
|
|
114
100
|
|
|
115
|
-
enable () {
|
|
101
|
+
enable (iastConfig) {
|
|
102
|
+
this.iastConfig = iastConfig
|
|
116
103
|
this.configure(true)
|
|
117
104
|
}
|
|
118
105
|
|
|
@@ -18,10 +18,10 @@ module.exports = {
|
|
|
18
18
|
enableTaintTracking (config, telemetryVerbosity) {
|
|
19
19
|
enableRewriter(telemetryVerbosity)
|
|
20
20
|
enableTaintOperations(telemetryVerbosity)
|
|
21
|
-
taintTrackingPlugin.enable()
|
|
21
|
+
taintTrackingPlugin.enable(config)
|
|
22
22
|
|
|
23
|
-
kafkaContextPlugin.enable()
|
|
24
|
-
kafkaConsumerPlugin.enable()
|
|
23
|
+
kafkaContextPlugin.enable(config)
|
|
24
|
+
kafkaConsumerPlugin.enable(config)
|
|
25
25
|
|
|
26
26
|
setMaxTransactions(config.maxConcurrentRequests)
|
|
27
27
|
},
|
|
@@ -12,7 +12,8 @@ const {
|
|
|
12
12
|
HTTP_REQUEST_HEADER_NAME,
|
|
13
13
|
HTTP_REQUEST_PARAMETER,
|
|
14
14
|
HTTP_REQUEST_PATH_PARAM,
|
|
15
|
-
HTTP_REQUEST_URI
|
|
15
|
+
HTTP_REQUEST_URI,
|
|
16
|
+
SQL_ROW_VALUE
|
|
16
17
|
} = require('./source-types')
|
|
17
18
|
const { EXECUTED_SOURCE } = require('../telemetry/iast-metric')
|
|
18
19
|
|
|
@@ -26,6 +27,16 @@ class TaintTrackingPlugin extends SourceIastPlugin {
|
|
|
26
27
|
this._taintedURLs = new WeakMap()
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
configure (config) {
|
|
31
|
+
super.configure(config)
|
|
32
|
+
|
|
33
|
+
let rowsToTaint = this.iastConfig?.dbRowsToTaint
|
|
34
|
+
if (typeof rowsToTaint !== 'number') {
|
|
35
|
+
rowsToTaint = 1
|
|
36
|
+
}
|
|
37
|
+
this._rowsToTaint = rowsToTaint
|
|
38
|
+
}
|
|
39
|
+
|
|
29
40
|
onConfigure () {
|
|
30
41
|
const onRequestBody = ({ req }) => {
|
|
31
42
|
const iastContext = getIastContext(storage.getStore())
|
|
@@ -46,8 +57,13 @@ class TaintTrackingPlugin extends SourceIastPlugin {
|
|
|
46
57
|
)
|
|
47
58
|
|
|
48
59
|
this.addSub(
|
|
49
|
-
{ channelName: 'datadog:
|
|
50
|
-
({
|
|
60
|
+
{ channelName: 'datadog:query:read:finish', tag: HTTP_REQUEST_PARAMETER },
|
|
61
|
+
({ query }) => this._taintTrackingHandler(HTTP_REQUEST_PARAMETER, query)
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
this.addSub(
|
|
65
|
+
{ channelName: 'datadog:express:query:finish', tag: HTTP_REQUEST_PARAMETER },
|
|
66
|
+
({ query }) => this._taintTrackingHandler(HTTP_REQUEST_PARAMETER, query)
|
|
51
67
|
)
|
|
52
68
|
|
|
53
69
|
this.addSub(
|
|
@@ -68,6 +84,16 @@ class TaintTrackingPlugin extends SourceIastPlugin {
|
|
|
68
84
|
({ cookies }) => this._cookiesTaintTrackingHandler(cookies)
|
|
69
85
|
)
|
|
70
86
|
|
|
87
|
+
this.addSub(
|
|
88
|
+
{ channelName: 'datadog:sequelize:query:finish', tag: SQL_ROW_VALUE },
|
|
89
|
+
({ result }) => this._taintDatabaseResult(result, 'sequelize')
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
this.addSub(
|
|
93
|
+
{ channelName: 'apm:pg:query:finish', tag: SQL_ROW_VALUE },
|
|
94
|
+
({ result }) => this._taintDatabaseResult(result, 'pg')
|
|
95
|
+
)
|
|
96
|
+
|
|
71
97
|
this.addSub(
|
|
72
98
|
{ channelName: 'datadog:express:process_params:start', tag: HTTP_REQUEST_PATH_PARAM },
|
|
73
99
|
({ req }) => {
|
|
@@ -77,6 +103,15 @@ class TaintTrackingPlugin extends SourceIastPlugin {
|
|
|
77
103
|
}
|
|
78
104
|
)
|
|
79
105
|
|
|
106
|
+
this.addSub(
|
|
107
|
+
{ channelName: 'datadog:router:param:start', tag: HTTP_REQUEST_PATH_PARAM },
|
|
108
|
+
({ req }) => {
|
|
109
|
+
if (req && req.params !== null && typeof req.params === 'object') {
|
|
110
|
+
this._taintTrackingHandler(HTTP_REQUEST_PATH_PARAM, req, 'params')
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
|
|
80
115
|
this.addSub(
|
|
81
116
|
{ channelName: 'apm:graphql:resolve:start', tag: HTTP_REQUEST_BODY },
|
|
82
117
|
(data) => {
|
|
@@ -170,6 +205,32 @@ class TaintTrackingPlugin extends SourceIastPlugin {
|
|
|
170
205
|
this.taintHeaders(req.headers, iastContext)
|
|
171
206
|
this.taintUrl(req, iastContext)
|
|
172
207
|
}
|
|
208
|
+
|
|
209
|
+
_taintDatabaseResult (result, dbOrigin, iastContext = getIastContext(storage.getStore()), name) {
|
|
210
|
+
if (!iastContext) return result
|
|
211
|
+
|
|
212
|
+
if (this._rowsToTaint === 0) return result
|
|
213
|
+
|
|
214
|
+
if (Array.isArray(result)) {
|
|
215
|
+
for (let i = 0; i < result.length && i < this._rowsToTaint; i++) {
|
|
216
|
+
const nextName = name ? `${name}.${i}` : '' + i
|
|
217
|
+
result[i] = this._taintDatabaseResult(result[i], dbOrigin, iastContext, nextName)
|
|
218
|
+
}
|
|
219
|
+
} else if (result && typeof result === 'object') {
|
|
220
|
+
if (dbOrigin === 'sequelize' && result.dataValues) {
|
|
221
|
+
result.dataValues = this._taintDatabaseResult(result.dataValues, dbOrigin, iastContext, name)
|
|
222
|
+
} else {
|
|
223
|
+
for (const key in result) {
|
|
224
|
+
const nextName = name ? `${name}.${key}` : key
|
|
225
|
+
result[key] = this._taintDatabaseResult(result[key], dbOrigin, iastContext, nextName)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
} else if (typeof result === 'string') {
|
|
229
|
+
result = newTaintedString(iastContext, result, name, SQL_ROW_VALUE)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return result
|
|
233
|
+
}
|
|
173
234
|
}
|
|
174
235
|
|
|
175
236
|
module.exports = new TaintTrackingPlugin()
|
|
@@ -11,5 +11,6 @@ module.exports = {
|
|
|
11
11
|
HTTP_REQUEST_PATH_PARAM: 'http.request.path.parameter',
|
|
12
12
|
HTTP_REQUEST_URI: 'http.request.uri',
|
|
13
13
|
KAFKA_MESSAGE_KEY: 'kafka.message.key',
|
|
14
|
-
KAFKA_MESSAGE_VALUE: 'kafka.message.value'
|
|
14
|
+
KAFKA_MESSAGE_VALUE: 'kafka.message.value',
|
|
15
|
+
SQL_ROW_VALUE: 'sql.row.value'
|
|
15
16
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// eslint-disable-next-line max-len
|
|
1
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
2
2
|
const DEFAULT_IAST_REDACTION_NAME_PATTERN = '(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?|access_?|secret_?)key(?:_?id)?|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?|(?:sur|last)name|user(?:name)?|address|e?mail)'
|
|
3
|
-
// eslint-disable-next-line max-len
|
|
3
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
4
4
|
const DEFAULT_IAST_REDACTION_VALUE_PATTERN = '(?:bearer\\s+[a-z0-9\\._\\-]+|glpat-[\\w\\-]{20}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L][\\w=\\-]+\\.ey[I-L][\\w=\\-]+(?:\\.[\\w.+/=\\-]+)?|(?:[\\-]{5}BEGIN[a-z\\s]+PRIVATE\\sKEY[\\-]{5}[^\\-]+[\\-]{5}END[a-z\\s]+PRIVATE\\sKEY[\\-]{5}|ssh-rsa\\s*[a-z0-9/\\.+]{100,})|[\\w\\.-]+@[a-zA-Z\\d\\.-]+\\.[a-zA-Z]{2,})'
|
|
5
5
|
|
|
6
6
|
module.exports = {
|
|
@@ -7,7 +7,7 @@ const STRINGIFY_RANGE_KEY = 'DD_' + crypto.randomBytes(20).toString('hex')
|
|
|
7
7
|
const STRINGIFY_SENSITIVE_KEY = STRINGIFY_RANGE_KEY + 'SENSITIVE'
|
|
8
8
|
const STRINGIFY_SENSITIVE_NOT_STRING_KEY = STRINGIFY_SENSITIVE_KEY + 'NOTSTRING'
|
|
9
9
|
|
|
10
|
-
// eslint-disable-next-line max-len
|
|
10
|
+
// eslint-disable-next-line @stylistic/js/max-len
|
|
11
11
|
const KEYS_REGEX_WITH_SENSITIVE_RANGES = new RegExp(`(?:"(${STRINGIFY_RANGE_KEY}_\\d+_))|(?:"(${STRINGIFY_SENSITIVE_KEY}_\\d+_(\\d+)_))|("${STRINGIFY_SENSITIVE_NOT_STRING_KEY}_\\d+_([\\s0-9.a-zA-Z]*)")`, 'gm')
|
|
12
12
|
const KEYS_REGEX_WITHOUT_SENSITIVE_RANGES = new RegExp(`"(${STRINGIFY_RANGE_KEY}_\\d+_)`, 'gm')
|
|
13
13
|
|
|
@@ -17,13 +17,36 @@ let resetVulnerabilityCacheTimer
|
|
|
17
17
|
let deduplicationEnabled = true
|
|
18
18
|
|
|
19
19
|
function addVulnerability (iastContext, vulnerability) {
|
|
20
|
-
if (vulnerability && vulnerability
|
|
21
|
-
vulnerability
|
|
22
|
-
|
|
20
|
+
if (vulnerability?.evidence && vulnerability?.type && vulnerability?.location) {
|
|
21
|
+
if (deduplicationEnabled && isDuplicatedVulnerability(vulnerability)) return
|
|
22
|
+
|
|
23
|
+
VULNERABILITY_HASHES.set(`${vulnerability.type}${vulnerability.hash}`, true)
|
|
24
|
+
|
|
25
|
+
let span = iastContext?.rootSpan
|
|
26
|
+
|
|
27
|
+
if (!span && tracer) {
|
|
28
|
+
span = tracer.startSpan('vulnerability', {
|
|
29
|
+
type: 'vulnerability'
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
vulnerability.location.spanId = span.context().toSpanId()
|
|
33
|
+
|
|
34
|
+
span.addTags({
|
|
35
|
+
[IAST_ENABLED_TAG_KEY]: 1
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!span) return
|
|
40
|
+
|
|
41
|
+
keepTrace(span, SAMPLING_MECHANISM_APPSEC)
|
|
42
|
+
standalone.sample(span)
|
|
43
|
+
|
|
44
|
+
if (iastContext?.rootSpan) {
|
|
23
45
|
iastContext[VULNERABILITIES_KEY] = iastContext[VULNERABILITIES_KEY] || []
|
|
24
46
|
iastContext[VULNERABILITIES_KEY].push(vulnerability)
|
|
25
47
|
} else {
|
|
26
|
-
sendVulnerabilities([vulnerability])
|
|
48
|
+
sendVulnerabilities([vulnerability], span)
|
|
49
|
+
span.finish()
|
|
27
50
|
}
|
|
28
51
|
}
|
|
29
52
|
}
|
|
@@ -34,36 +57,17 @@ function isValidVulnerability (vulnerability) {
|
|
|
34
57
|
vulnerability.location && vulnerability.location.spanId
|
|
35
58
|
}
|
|
36
59
|
|
|
37
|
-
function sendVulnerabilities (vulnerabilities,
|
|
60
|
+
function sendVulnerabilities (vulnerabilities, span) {
|
|
38
61
|
if (vulnerabilities && vulnerabilities.length) {
|
|
39
|
-
let span = rootSpan
|
|
40
|
-
if (!span && tracer) {
|
|
41
|
-
span = tracer.startSpan('vulnerability', {
|
|
42
|
-
type: 'vulnerability'
|
|
43
|
-
})
|
|
44
|
-
vulnerabilities.forEach((vulnerability) => {
|
|
45
|
-
vulnerability.location.spanId = span.context().toSpanId()
|
|
46
|
-
})
|
|
47
|
-
span.addTags({
|
|
48
|
-
[IAST_ENABLED_TAG_KEY]: 1
|
|
49
|
-
})
|
|
50
|
-
}
|
|
51
|
-
|
|
52
62
|
if (span && span.addTags) {
|
|
53
|
-
const
|
|
54
|
-
const jsonToSend = vulnerabilitiesFormatter.toJson(
|
|
63
|
+
const validatedVulnerabilities = vulnerabilities.filter(isValidVulnerability)
|
|
64
|
+
const jsonToSend = vulnerabilitiesFormatter.toJson(validatedVulnerabilities)
|
|
55
65
|
|
|
56
66
|
if (jsonToSend.vulnerabilities.length > 0) {
|
|
57
67
|
const tags = {}
|
|
58
68
|
// TODO: Store this outside of the span and set the tag in the exporter.
|
|
59
69
|
tags[IAST_JSON_TAG_KEY] = JSON.stringify(jsonToSend)
|
|
60
70
|
span.addTags(tags)
|
|
61
|
-
|
|
62
|
-
keepTrace(span, SAMPLING_MECHANISM_APPSEC)
|
|
63
|
-
|
|
64
|
-
standalone.sample(span)
|
|
65
|
-
|
|
66
|
-
if (!rootSpan) span.finish()
|
|
67
71
|
}
|
|
68
72
|
}
|
|
69
73
|
}
|
|
@@ -86,17 +90,8 @@ function stopClearCacheTimer () {
|
|
|
86
90
|
}
|
|
87
91
|
}
|
|
88
92
|
|
|
89
|
-
function
|
|
90
|
-
|
|
91
|
-
const deduplicated = vulnerabilities.filter((vulnerability) => {
|
|
92
|
-
const key = `${vulnerability.type}${vulnerability.hash}`
|
|
93
|
-
if (!VULNERABILITY_HASHES.get(key)) {
|
|
94
|
-
VULNERABILITY_HASHES.set(key, true)
|
|
95
|
-
return true
|
|
96
|
-
}
|
|
97
|
-
return false
|
|
98
|
-
})
|
|
99
|
-
return deduplicated
|
|
93
|
+
function isDuplicatedVulnerability (vulnerability) {
|
|
94
|
+
return VULNERABILITY_HASHES.get(`${vulnerability.type}${vulnerability.hash}`)
|
|
100
95
|
}
|
|
101
96
|
|
|
102
97
|
function start (config, _tracer) {
|
|
@@ -16,7 +16,8 @@ const {
|
|
|
16
16
|
expressProcessParams,
|
|
17
17
|
responseBody,
|
|
18
18
|
responseWriteHead,
|
|
19
|
-
responseSetHeader
|
|
19
|
+
responseSetHeader,
|
|
20
|
+
routerParam
|
|
20
21
|
} = require('./channels')
|
|
21
22
|
const waf = require('./waf')
|
|
22
23
|
const addresses = require('./addresses')
|
|
@@ -27,7 +28,7 @@ const web = require('../plugins/util/web')
|
|
|
27
28
|
const { extractIp } = require('../plugins/util/ip_extractor')
|
|
28
29
|
const { HTTP_CLIENT_IP } = require('../../../../ext/tags')
|
|
29
30
|
const { isBlocked, block, setTemplates, getBlockingAction } = require('./blocking')
|
|
30
|
-
const
|
|
31
|
+
const UserTracking = require('./user_tracking')
|
|
31
32
|
const { storage } = require('../../../datadog-core')
|
|
32
33
|
const graphql = require('./graphql')
|
|
33
34
|
const rasp = require('./rasp')
|
|
@@ -58,23 +59,23 @@ function enable (_config) {
|
|
|
58
59
|
|
|
59
60
|
apiSecuritySampler.configure(_config.appsec)
|
|
60
61
|
|
|
62
|
+
UserTracking.setCollectionMode(_config.appsec.eventTracking.mode, false)
|
|
63
|
+
|
|
61
64
|
bodyParser.subscribe(onRequestBodyParsed)
|
|
62
65
|
multerParser.subscribe(onRequestBodyParsed)
|
|
63
66
|
cookieParser.subscribe(onRequestCookieParser)
|
|
64
67
|
incomingHttpRequestStart.subscribe(incomingHttpStartTranslator)
|
|
65
68
|
incomingHttpRequestEnd.subscribe(incomingHttpEndTranslator)
|
|
69
|
+
passportVerify.subscribe(onPassportVerify) // possible optimization: only subscribe if collection mode is enabled
|
|
66
70
|
queryParser.subscribe(onRequestQueryParsed)
|
|
67
71
|
nextBodyParsed.subscribe(onRequestBodyParsed)
|
|
68
72
|
nextQueryParsed.subscribe(onRequestQueryParsed)
|
|
69
73
|
expressProcessParams.subscribe(onRequestProcessParams)
|
|
74
|
+
routerParam.subscribe(onRequestProcessParams)
|
|
70
75
|
responseBody.subscribe(onResponseBody)
|
|
71
76
|
responseWriteHead.subscribe(onResponseWriteHead)
|
|
72
77
|
responseSetHeader.subscribe(onResponseSetHeader)
|
|
73
78
|
|
|
74
|
-
if (_config.appsec.eventTracking.enabled) {
|
|
75
|
-
passportVerify.subscribe(onPassportVerify)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
79
|
isEnabled = true
|
|
79
80
|
config = _config
|
|
80
81
|
} catch (err) {
|
|
@@ -163,8 +164,10 @@ function incomingHttpEndTranslator ({ req, res }) {
|
|
|
163
164
|
persistent[addresses.HTTP_INCOMING_COOKIES] = req.cookies
|
|
164
165
|
}
|
|
165
166
|
|
|
166
|
-
|
|
167
|
-
|
|
167
|
+
// we need to keep this to support nextjs
|
|
168
|
+
const query = req.query
|
|
169
|
+
if (query !== null && typeof query === 'object') {
|
|
170
|
+
persistent[addresses.HTTP_INCOMING_QUERY] = query
|
|
168
171
|
}
|
|
169
172
|
|
|
170
173
|
if (apiSecuritySampler.sampleRequest(req, res, true)) {
|
|
@@ -180,7 +183,7 @@ function incomingHttpEndTranslator ({ req, res }) {
|
|
|
180
183
|
Reporter.finishRequest(req, res)
|
|
181
184
|
}
|
|
182
185
|
|
|
183
|
-
function onPassportVerify ({
|
|
186
|
+
function onPassportVerify ({ framework, login, user, success, abortController }) {
|
|
184
187
|
const store = storage.getStore()
|
|
185
188
|
const rootSpan = store?.req && web.root(store.req)
|
|
186
189
|
|
|
@@ -189,7 +192,9 @@ function onPassportVerify ({ credentials, user }) {
|
|
|
189
192
|
return
|
|
190
193
|
}
|
|
191
194
|
|
|
192
|
-
|
|
195
|
+
const results = UserTracking.trackLogin(framework, login, user, success, rootSpan)
|
|
196
|
+
|
|
197
|
+
handleResults(results, store.req, store.req.res, rootSpan, abortController)
|
|
193
198
|
}
|
|
194
199
|
|
|
195
200
|
function onRequestQueryParsed ({ req, res, query, abortController }) {
|
|
@@ -309,6 +314,7 @@ function disable () {
|
|
|
309
314
|
if (nextBodyParsed.hasSubscribers) nextBodyParsed.unsubscribe(onRequestBodyParsed)
|
|
310
315
|
if (nextQueryParsed.hasSubscribers) nextQueryParsed.unsubscribe(onRequestQueryParsed)
|
|
311
316
|
if (expressProcessParams.hasSubscribers) expressProcessParams.unsubscribe(onRequestProcessParams)
|
|
317
|
+
if (routerParam.hasSubscribers) routerParam.unsubscribe(onRequestProcessParams)
|
|
312
318
|
if (responseBody.hasSubscribers) responseBody.unsubscribe(onResponseBody)
|
|
313
319
|
if (responseWriteHead.hasSubscribers) responseWriteHead.unsubscribe(onResponseWriteHead)
|
|
314
320
|
if (responseSetHeader.hasSubscribers) responseSetHeader.unsubscribe(onResponseSetHeader)
|
|
@@ -22,6 +22,7 @@ module.exports = {
|
|
|
22
22
|
ASM_RASP_SSRF: 1n << 23n,
|
|
23
23
|
ASM_RASP_SHI: 1n << 24n,
|
|
24
24
|
APM_TRACING_SAMPLE_RULES: 1n << 29n,
|
|
25
|
+
ASM_AUTO_USER_INSTRUM_MODE: 1n << 31n,
|
|
25
26
|
ASM_ENDPOINT_FINGERPRINT: 1n << 32n,
|
|
26
27
|
ASM_NETWORK_FINGERPRINT: 1n << 34n,
|
|
27
28
|
ASM_HEADER_FINGERPRINT: 1n << 35n
|
|
@@ -4,6 +4,8 @@ const Activation = require('../activation')
|
|
|
4
4
|
|
|
5
5
|
const RemoteConfigManager = require('./manager')
|
|
6
6
|
const RemoteConfigCapabilities = require('./capabilities')
|
|
7
|
+
const { setCollectionMode } = require('../user_tracking')
|
|
8
|
+
const log = require('../../log')
|
|
7
9
|
|
|
8
10
|
let rc
|
|
9
11
|
|
|
@@ -23,9 +25,31 @@ function enable (config, appsec) {
|
|
|
23
25
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_ACTIVATION, true)
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
rc.
|
|
28
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_AUTO_USER_INSTRUM_MODE, true)
|
|
29
|
+
|
|
30
|
+
let autoUserInstrumModeId
|
|
31
|
+
|
|
32
|
+
rc.setProductHandler('ASM_FEATURES', (action, rcConfig, configId) => {
|
|
27
33
|
if (!rcConfig) return
|
|
28
34
|
|
|
35
|
+
// this is put before other handlers because it can reject the config
|
|
36
|
+
if (typeof rcConfig.auto_user_instrum?.mode === 'string') {
|
|
37
|
+
if (action === 'apply' || action === 'modify') {
|
|
38
|
+
// check if there is already a config applied with this field
|
|
39
|
+
if (autoUserInstrumModeId && configId !== autoUserInstrumModeId) {
|
|
40
|
+
log.error('[RC] Multiple auto_user_instrum received in ASM_FEATURES. Discarding config')
|
|
41
|
+
// eslint-disable-next-line no-throw-literal
|
|
42
|
+
throw 'Multiple auto_user_instrum.mode received in ASM_FEATURES'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
setCollectionMode(rcConfig.auto_user_instrum.mode)
|
|
46
|
+
autoUserInstrumModeId = configId
|
|
47
|
+
} else if (configId === autoUserInstrumModeId) {
|
|
48
|
+
setCollectionMode(config.appsec.eventTracking.mode)
|
|
49
|
+
autoUserInstrumModeId = null
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
29
53
|
if (activation === Activation.ONECLICK) {
|
|
30
54
|
enableOrDisableAppsec(action, rcConfig, config, appsec)
|
|
31
55
|
}
|