dd-trace 5.17.0 → 5.19.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 +1 -2
- package/ext/exporters.d.ts +1 -1
- package/index.d.ts +105 -37
- package/init.js +40 -1
- package/initialize.mjs +8 -5
- package/package.json +29 -29
- package/packages/datadog-core/src/storage/index.js +1 -10
- package/packages/datadog-esbuild/index.js +5 -1
- package/packages/datadog-instrumentations/src/aws-sdk.js +2 -1
- package/packages/datadog-instrumentations/src/child_process.js +2 -2
- package/packages/datadog-instrumentations/src/cucumber.js +76 -34
- package/packages/datadog-instrumentations/src/fs.js +1 -1
- package/packages/datadog-instrumentations/src/hapi.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/hook.js +8 -3
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +4 -3
- package/packages/datadog-instrumentations/src/helpers/register.js +56 -5
- package/packages/datadog-instrumentations/src/http/client.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +17 -2
- package/packages/datadog-instrumentations/src/kafkajs.js +1 -1
- package/packages/datadog-instrumentations/src/ldapjs.js +2 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +12 -1
- package/packages/datadog-instrumentations/src/mocha/utils.js +58 -14
- package/packages/datadog-instrumentations/src/mocha/worker.js +1 -0
- package/packages/datadog-instrumentations/src/mquery.js +2 -2
- package/packages/datadog-instrumentations/src/next.js +1 -1
- package/packages/datadog-instrumentations/src/pg.js +2 -2
- package/packages/datadog-instrumentations/src/playwright.js +47 -33
- package/packages/datadog-instrumentations/src/restify.js +1 -1
- package/packages/datadog-instrumentations/src/vitest.js +349 -0
- package/packages/datadog-plugin-aws-sdk/src/base.js +8 -1
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +6 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +23 -5
- package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +1 -1
- package/packages/datadog-plugin-child_process/src/index.js +1 -1
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -4
- package/packages/datadog-plugin-cucumber/src/index.js +24 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +79 -42
- package/packages/datadog-plugin-cypress/src/plugin.js +4 -3
- package/packages/datadog-plugin-fs/src/index.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +7 -1
- package/packages/datadog-plugin-kafkajs/src/producer.js +1 -1
- package/packages/datadog-plugin-mocha/src/index.js +25 -4
- package/packages/datadog-plugin-mongodb-core/src/index.js +1 -1
- package/packages/datadog-plugin-openai/src/index.js +57 -35
- package/packages/datadog-plugin-openai/src/token-estimator.js +20 -0
- package/packages/datadog-plugin-playwright/src/index.js +4 -1
- package/packages/datadog-plugin-sharedb/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +167 -0
- package/packages/dd-trace/src/analytics_sampler.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/path-line.js +2 -19
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +2 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +2 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +3 -1
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -0
- package/packages/dd-trace/src/appsec/index.js +4 -4
- package/packages/dd-trace/src/appsec/passport.js +1 -1
- package/packages/dd-trace/src/appsec/rasp.js +32 -5
- package/packages/dd-trace/src/appsec/recommended.json +208 -3
- package/packages/dd-trace/src/appsec/reporter.js +60 -20
- package/packages/dd-trace/src/appsec/sdk/track_event.js +3 -0
- package/packages/dd-trace/src/appsec/stack_trace.js +90 -0
- package/packages/dd-trace/src/appsec/standalone.js +130 -0
- package/packages/dd-trace/src/appsec/telemetry.js +33 -1
- package/packages/dd-trace/src/appsec/waf/index.js +2 -2
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +3 -3
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
- package/packages/dd-trace/src/config.js +136 -63
- package/packages/dd-trace/src/constants.js +3 -1
- package/packages/dd-trace/src/datastreams/processor.js +3 -2
- package/packages/dd-trace/src/exporters/agent/index.js +2 -2
- package/packages/dd-trace/src/format.js +1 -0
- package/packages/dd-trace/src/opentelemetry/span.js +1 -1
- package/packages/dd-trace/src/opentelemetry/tracer.js +6 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +12 -0
- package/packages/dd-trace/src/opentracing/span.js +4 -1
- package/packages/dd-trace/src/opentracing/tracer.js +2 -2
- package/packages/dd-trace/src/plugins/ci_plugin.js +7 -0
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/util/test.js +5 -1
- package/packages/dd-trace/src/priority_sampler.js +2 -5
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/proxy.js +3 -1
- package/packages/dd-trace/src/rate_limiter.js +2 -2
- package/packages/dd-trace/src/span_stats.js +4 -3
- package/packages/dd-trace/src/telemetry/init-telemetry.js +75 -0
- package/packages/dd-trace/src/tracer.js +2 -2
- package/packages/dd-trace/src/util.js +6 -1
- package/packages/datadog-core/src/storage/async_hooks.js +0 -49
|
@@ -7,11 +7,14 @@ const { ipHeaderList } = require('../plugins/util/ip_extractor')
|
|
|
7
7
|
const {
|
|
8
8
|
incrementWafInitMetric,
|
|
9
9
|
updateWafRequestsMetricTags,
|
|
10
|
+
updateRaspRequestsMetricTags,
|
|
10
11
|
incrementWafUpdatesMetric,
|
|
11
12
|
incrementWafRequestsMetric,
|
|
12
13
|
getRequestMetrics
|
|
13
14
|
} = require('./telemetry')
|
|
14
15
|
const zlib = require('zlib')
|
|
16
|
+
const { MANUAL_KEEP } = require('../../../../ext/tags')
|
|
17
|
+
const standalone = require('./standalone')
|
|
15
18
|
|
|
16
19
|
// default limiter, configurable with setRateLimit()
|
|
17
20
|
let limiter = new Limiter(100)
|
|
@@ -26,19 +29,17 @@ const contentHeaderList = [
|
|
|
26
29
|
'content-language'
|
|
27
30
|
]
|
|
28
31
|
|
|
29
|
-
const
|
|
32
|
+
const EVENT_HEADERS_MAP = mapHeaderAndTags([
|
|
30
33
|
...ipHeaderList,
|
|
31
34
|
'forwarded',
|
|
32
35
|
'via',
|
|
33
36
|
...contentHeaderList,
|
|
34
37
|
'host',
|
|
35
|
-
'user-agent',
|
|
36
|
-
'accept',
|
|
37
38
|
'accept-encoding',
|
|
38
39
|
'accept-language'
|
|
39
40
|
], 'http.request.headers.')
|
|
40
41
|
|
|
41
|
-
const
|
|
42
|
+
const identificationHeaders = [
|
|
42
43
|
'x-amzn-trace-id',
|
|
43
44
|
'cloudfront-viewer-ja3-fingerprint',
|
|
44
45
|
'cf-ray',
|
|
@@ -47,6 +48,14 @@ const IDENTIFICATION_HEADERS_MAP = mapHeaderAndTags([
|
|
|
47
48
|
'x-sigsci-requestid',
|
|
48
49
|
'x-sigsci-tags',
|
|
49
50
|
'akamai-user-risk'
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
// these request headers are always collected - it breaks the expected spec orders
|
|
54
|
+
const REQUEST_HEADERS_MAP = mapHeaderAndTags([
|
|
55
|
+
'content-type',
|
|
56
|
+
'user-agent',
|
|
57
|
+
'accept',
|
|
58
|
+
...identificationHeaders
|
|
50
59
|
], 'http.request.headers.')
|
|
51
60
|
|
|
52
61
|
const RESPONSE_HEADERS_MAP = mapHeaderAndTags(contentHeaderList, 'http.response.headers.')
|
|
@@ -87,12 +96,12 @@ function reportWafInit (wafVersion, rulesVersion, diagnosticsRules = {}) {
|
|
|
87
96
|
metricsQueue.set('_dd.appsec.event_rules.errors', JSON.stringify(diagnosticsRules.errors))
|
|
88
97
|
}
|
|
89
98
|
|
|
90
|
-
metricsQueue.set(
|
|
99
|
+
metricsQueue.set(MANUAL_KEEP, 'true')
|
|
91
100
|
|
|
92
101
|
incrementWafInitMetric(wafVersion, rulesVersion)
|
|
93
102
|
}
|
|
94
103
|
|
|
95
|
-
function reportMetrics (metrics) {
|
|
104
|
+
function reportMetrics (metrics, raspRuleType) {
|
|
96
105
|
const store = storage.getStore()
|
|
97
106
|
const rootSpan = store?.req && web.root(store.req)
|
|
98
107
|
if (!rootSpan) return
|
|
@@ -100,8 +109,11 @@ function reportMetrics (metrics) {
|
|
|
100
109
|
if (metrics.rulesVersion) {
|
|
101
110
|
rootSpan.setTag('_dd.appsec.event_rules.version', metrics.rulesVersion)
|
|
102
111
|
}
|
|
103
|
-
|
|
104
|
-
|
|
112
|
+
if (raspRuleType) {
|
|
113
|
+
updateRaspRequestsMetricTags(metrics, store.req, raspRuleType)
|
|
114
|
+
} else {
|
|
115
|
+
updateWafRequestsMetricTags(metrics, store.req)
|
|
116
|
+
}
|
|
105
117
|
}
|
|
106
118
|
|
|
107
119
|
function reportAttack (attackData) {
|
|
@@ -112,12 +124,14 @@ function reportAttack (attackData) {
|
|
|
112
124
|
|
|
113
125
|
const currentTags = rootSpan.context()._tags
|
|
114
126
|
|
|
115
|
-
const newTags =
|
|
116
|
-
|
|
117
|
-
|
|
127
|
+
const newTags = {
|
|
128
|
+
'appsec.event': 'true'
|
|
129
|
+
}
|
|
118
130
|
|
|
119
131
|
if (limiter.isAllowed()) {
|
|
120
|
-
newTags[
|
|
132
|
+
newTags[MANUAL_KEEP] = 'true'
|
|
133
|
+
|
|
134
|
+
standalone.sample(rootSpan)
|
|
121
135
|
}
|
|
122
136
|
|
|
123
137
|
// TODO: maybe add this to format.js later (to take decision as late as possible)
|
|
@@ -134,11 +148,6 @@ function reportAttack (attackData) {
|
|
|
134
148
|
newTags['_dd.appsec.json'] = '{"triggers":' + attackData + '}'
|
|
135
149
|
}
|
|
136
150
|
|
|
137
|
-
const ua = newTags['http.request.headers.user-agent']
|
|
138
|
-
if (ua) {
|
|
139
|
-
newTags['http.useragent'] = ua
|
|
140
|
-
}
|
|
141
|
-
|
|
142
151
|
newTags['network.client.ip'] = req.socket.remoteAddress
|
|
143
152
|
|
|
144
153
|
rootSpan.addTags(newTags)
|
|
@@ -168,6 +177,8 @@ function finishRequest (req, res) {
|
|
|
168
177
|
if (metricsQueue.size) {
|
|
169
178
|
rootSpan.addTags(Object.fromEntries(metricsQueue))
|
|
170
179
|
|
|
180
|
+
standalone.sample(rootSpan)
|
|
181
|
+
|
|
171
182
|
metricsQueue.clear()
|
|
172
183
|
}
|
|
173
184
|
|
|
@@ -180,22 +191,51 @@ function finishRequest (req, res) {
|
|
|
180
191
|
rootSpan.setTag('_dd.appsec.waf.duration_ext', metrics.durationExt)
|
|
181
192
|
}
|
|
182
193
|
|
|
194
|
+
if (metrics?.raspDuration) {
|
|
195
|
+
rootSpan.setTag('_dd.appsec.rasp.duration', metrics.raspDuration)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (metrics?.raspDurationExt) {
|
|
199
|
+
rootSpan.setTag('_dd.appsec.rasp.duration_ext', metrics.raspDurationExt)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (metrics?.raspEvalCount) {
|
|
203
|
+
rootSpan.setTag('_dd.appsec.rasp.rule.eval', metrics.raspEvalCount)
|
|
204
|
+
}
|
|
205
|
+
|
|
183
206
|
incrementWafRequestsMetric(req)
|
|
184
207
|
|
|
185
208
|
// collect some headers even when no attack is detected
|
|
186
|
-
|
|
209
|
+
const mandatoryTags = filterHeaders(req.headers, REQUEST_HEADERS_MAP)
|
|
210
|
+
rootSpan.addTags(mandatoryTags)
|
|
187
211
|
|
|
188
|
-
|
|
212
|
+
const tags = rootSpan.context()._tags
|
|
213
|
+
if (!shouldCollectEventHeaders(tags)) return
|
|
189
214
|
|
|
190
215
|
const newTags = filterHeaders(res.getHeaders(), RESPONSE_HEADERS_MAP)
|
|
216
|
+
Object.assign(newTags, filterHeaders(req.headers, EVENT_HEADERS_MAP))
|
|
191
217
|
|
|
192
|
-
if (
|
|
218
|
+
if (tags['appsec.event'] === 'true' && typeof req.route?.path === 'string') {
|
|
193
219
|
newTags['http.endpoint'] = req.route.path
|
|
194
220
|
}
|
|
195
221
|
|
|
196
222
|
rootSpan.addTags(newTags)
|
|
197
223
|
}
|
|
198
224
|
|
|
225
|
+
function shouldCollectEventHeaders (tags = {}) {
|
|
226
|
+
if (tags['appsec.event'] === 'true') {
|
|
227
|
+
return true
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
for (const tagName of Object.keys(tags)) {
|
|
231
|
+
if (tagName.startsWith('appsec.events.')) {
|
|
232
|
+
return true
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return false
|
|
237
|
+
}
|
|
238
|
+
|
|
199
239
|
function setRateLimit (rateLimit) {
|
|
200
240
|
limiter = new Limiter(rateLimit)
|
|
201
241
|
}
|
|
@@ -4,6 +4,7 @@ const log = require('../../log')
|
|
|
4
4
|
const { getRootSpan } = require('./utils')
|
|
5
5
|
const { MANUAL_KEEP } = require('../../../../../ext/tags')
|
|
6
6
|
const { setUserTags } = require('./set_user')
|
|
7
|
+
const standalone = require('../standalone')
|
|
7
8
|
|
|
8
9
|
function trackUserLoginSuccessEvent (tracer, user, metadata) {
|
|
9
10
|
// TODO: better user check here and in _setUser() ?
|
|
@@ -73,6 +74,8 @@ function trackEvent (eventName, fields, sdkMethodName, rootSpan, mode) {
|
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
rootSpan.addTags(tags)
|
|
77
|
+
|
|
78
|
+
standalone.sample(rootSpan)
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
module.exports = {
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { calculateDDBasePath } = require('../util')
|
|
4
|
+
|
|
5
|
+
const ddBasePath = calculateDDBasePath(__dirname)
|
|
6
|
+
|
|
7
|
+
const LIBRARY_FRAMES_BUFFER = 20
|
|
8
|
+
|
|
9
|
+
function getCallSiteList (maxDepth = 100) {
|
|
10
|
+
const previousPrepareStackTrace = Error.prepareStackTrace
|
|
11
|
+
const previousStackTraceLimit = Error.stackTraceLimit
|
|
12
|
+
let callsiteList
|
|
13
|
+
Error.stackTraceLimit = maxDepth
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
Error.prepareStackTrace = function (_, callsites) {
|
|
17
|
+
callsiteList = callsites
|
|
18
|
+
}
|
|
19
|
+
const e = new Error()
|
|
20
|
+
e.stack
|
|
21
|
+
} finally {
|
|
22
|
+
Error.prepareStackTrace = previousPrepareStackTrace
|
|
23
|
+
Error.stackTraceLimit = previousStackTraceLimit
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return callsiteList
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function filterOutFramesFromLibrary (callSiteList) {
|
|
30
|
+
return callSiteList.filter(callSite => !callSite.getFileName()?.startsWith(ddBasePath))
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function getFramesForMetaStruct (callSiteList, maxDepth = 32) {
|
|
34
|
+
const filteredFrames = filterOutFramesFromLibrary(callSiteList)
|
|
35
|
+
|
|
36
|
+
const half = filteredFrames.length > maxDepth ? Math.round(maxDepth / 2) : Infinity
|
|
37
|
+
|
|
38
|
+
const indexedFrames = []
|
|
39
|
+
for (let i = 0; i < Math.min(filteredFrames.length, maxDepth); i++) {
|
|
40
|
+
const index = i < half ? i : i + filteredFrames.length - maxDepth
|
|
41
|
+
const callSite = filteredFrames[index]
|
|
42
|
+
indexedFrames.push({
|
|
43
|
+
id: index,
|
|
44
|
+
file: callSite.getFileName(),
|
|
45
|
+
line: callSite.getLineNumber(),
|
|
46
|
+
column: callSite.getColumnNumber(),
|
|
47
|
+
function: callSite.getFunctionName(),
|
|
48
|
+
class_name: callSite.getTypeName()
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return indexedFrames
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function reportStackTrace (rootSpan, stackId, maxDepth, maxStackTraces, callSiteListGetter = getCallSiteList) {
|
|
56
|
+
if (!rootSpan) return
|
|
57
|
+
|
|
58
|
+
if (maxStackTraces < 1 || (rootSpan.meta_struct?.['_dd.stack']?.exploit?.length ?? 0) < maxStackTraces) {
|
|
59
|
+
// Since some frames will be discarded because they come from tracer codebase, a buffer is added
|
|
60
|
+
// to the limit in order to get as close as `maxDepth` number of frames.
|
|
61
|
+
if (maxDepth < 1) maxDepth = Infinity
|
|
62
|
+
const callSiteList = callSiteListGetter(maxDepth + LIBRARY_FRAMES_BUFFER)
|
|
63
|
+
if (!Array.isArray(callSiteList)) return
|
|
64
|
+
|
|
65
|
+
if (!rootSpan.meta_struct) {
|
|
66
|
+
rootSpan.meta_struct = {}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!rootSpan.meta_struct['_dd.stack']) {
|
|
70
|
+
rootSpan.meta_struct['_dd.stack'] = {}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!rootSpan.meta_struct['_dd.stack'].exploit) {
|
|
74
|
+
rootSpan.meta_struct['_dd.stack'].exploit = []
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const frames = getFramesForMetaStruct(callSiteList, maxDepth)
|
|
78
|
+
|
|
79
|
+
rootSpan.meta_struct['_dd.stack'].exploit.push({
|
|
80
|
+
id: stackId,
|
|
81
|
+
language: 'nodejs',
|
|
82
|
+
frames
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = {
|
|
88
|
+
getCallSiteList,
|
|
89
|
+
reportStackTrace
|
|
90
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { channel } = require('dc-polyfill')
|
|
4
|
+
const { USER_KEEP, AUTO_KEEP, AUTO_REJECT } = require('../../../../ext/priority')
|
|
5
|
+
const { MANUAL_KEEP } = require('../../../../ext/tags')
|
|
6
|
+
const PrioritySampler = require('../priority_sampler')
|
|
7
|
+
const RateLimiter = require('../rate_limiter')
|
|
8
|
+
const TraceState = require('../opentracing/propagation/tracestate')
|
|
9
|
+
const { hasOwn } = require('../util')
|
|
10
|
+
const { APM_TRACING_ENABLED_KEY, APPSEC_PROPAGATION_KEY, SAMPLING_MECHANISM_DEFAULT } = require('../constants')
|
|
11
|
+
|
|
12
|
+
const startCh = channel('dd-trace:span:start')
|
|
13
|
+
const injectCh = channel('dd-trace:span:inject')
|
|
14
|
+
const extractCh = channel('dd-trace:span:extract')
|
|
15
|
+
|
|
16
|
+
let enabled
|
|
17
|
+
|
|
18
|
+
class StandAloneAsmPrioritySampler extends PrioritySampler {
|
|
19
|
+
constructor (env) {
|
|
20
|
+
super(env, { sampleRate: 0, rateLimit: 0, rules: [] })
|
|
21
|
+
|
|
22
|
+
// let some regular APM traces go through, 1 per minute to keep alive the service
|
|
23
|
+
this._limiter = new RateLimiter(1, 'minute')
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
configure (env, config) {
|
|
27
|
+
// rules not supported
|
|
28
|
+
this._env = env
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
_getPriorityFromTags (tags, context) {
|
|
32
|
+
if (hasOwn(tags, MANUAL_KEEP) &&
|
|
33
|
+
tags[MANUAL_KEEP] !== false &&
|
|
34
|
+
hasOwn(context._trace.tags, APPSEC_PROPAGATION_KEY)
|
|
35
|
+
) {
|
|
36
|
+
return USER_KEEP
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
_getPriorityFromAuto (span) {
|
|
41
|
+
const context = this._getContext(span)
|
|
42
|
+
|
|
43
|
+
context._sampling.mechanism = SAMPLING_MECHANISM_DEFAULT
|
|
44
|
+
|
|
45
|
+
if (hasOwn(context._trace.tags, APPSEC_PROPAGATION_KEY)) {
|
|
46
|
+
return USER_KEEP
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return this._isSampledByRateLimit(context) ? AUTO_KEEP : AUTO_REJECT
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function onSpanStart ({ span, fields }) {
|
|
54
|
+
const tags = span.context?.()?._tags
|
|
55
|
+
if (!tags) return
|
|
56
|
+
|
|
57
|
+
const { parent } = fields
|
|
58
|
+
if (!parent || parent._isRemote) {
|
|
59
|
+
tags[APM_TRACING_ENABLED_KEY] = 0
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function onSpanInject ({ spanContext, carrier }) {
|
|
64
|
+
if (!spanContext?._trace?.tags || !carrier) return
|
|
65
|
+
|
|
66
|
+
// do not inject trace and sampling if there is no appsec event
|
|
67
|
+
if (!hasOwn(spanContext._trace.tags, APPSEC_PROPAGATION_KEY)) {
|
|
68
|
+
for (const key in carrier) {
|
|
69
|
+
const lKey = key.toLowerCase()
|
|
70
|
+
if (lKey.startsWith('x-datadog')) {
|
|
71
|
+
delete carrier[key]
|
|
72
|
+
} else if (lKey === 'tracestate') {
|
|
73
|
+
const tracestate = TraceState.fromString(carrier[key])
|
|
74
|
+
tracestate.forVendor('dd', state => state.clear())
|
|
75
|
+
carrier[key] = tracestate.toString()
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function onSpanExtract ({ spanContext = {} }) {
|
|
82
|
+
if (!spanContext._trace?.tags || !spanContext._sampling) return
|
|
83
|
+
|
|
84
|
+
// reset upstream priority if _dd.p.appsec is not found
|
|
85
|
+
if (!hasOwn(spanContext._trace.tags, APPSEC_PROPAGATION_KEY)) {
|
|
86
|
+
spanContext._sampling.priority = undefined
|
|
87
|
+
} else if (spanContext._sampling.priority !== USER_KEEP) {
|
|
88
|
+
spanContext._sampling.priority = USER_KEEP
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function sample (span) {
|
|
93
|
+
const spanContext = span.context?.()
|
|
94
|
+
if (enabled && spanContext?._trace?.tags) {
|
|
95
|
+
spanContext._trace.tags[APPSEC_PROPAGATION_KEY] = '1'
|
|
96
|
+
|
|
97
|
+
// TODO: ask. can we reset here sampling like this?
|
|
98
|
+
if (spanContext._sampling?.priority < AUTO_KEEP) {
|
|
99
|
+
spanContext._sampling.priority = undefined
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function configure (config) {
|
|
105
|
+
const configChanged = enabled !== config.appsec?.standalone?.enabled
|
|
106
|
+
if (!configChanged) return
|
|
107
|
+
|
|
108
|
+
enabled = config.appsec?.standalone?.enabled
|
|
109
|
+
|
|
110
|
+
let prioritySampler
|
|
111
|
+
if (enabled) {
|
|
112
|
+
startCh.subscribe(onSpanStart)
|
|
113
|
+
injectCh.subscribe(onSpanInject)
|
|
114
|
+
extractCh.subscribe(onSpanExtract)
|
|
115
|
+
|
|
116
|
+
prioritySampler = new StandAloneAsmPrioritySampler(config.env)
|
|
117
|
+
} else {
|
|
118
|
+
if (startCh.hasSubscribers) startCh.unsubscribe(onSpanStart)
|
|
119
|
+
if (injectCh.hasSubscribers) injectCh.unsubscribe(onSpanInject)
|
|
120
|
+
if (extractCh.hasSubscribers) extractCh.unsubscribe(onSpanExtract)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return prioritySampler
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
module.exports = {
|
|
127
|
+
configure,
|
|
128
|
+
sample,
|
|
129
|
+
StandAloneAsmPrioritySampler
|
|
130
|
+
}
|
|
@@ -31,7 +31,10 @@ function newStore () {
|
|
|
31
31
|
return {
|
|
32
32
|
[DD_TELEMETRY_REQUEST_METRICS]: {
|
|
33
33
|
duration: 0,
|
|
34
|
-
durationExt: 0
|
|
34
|
+
durationExt: 0,
|
|
35
|
+
raspDuration: 0,
|
|
36
|
+
raspDurationExt: 0,
|
|
37
|
+
raspEvalCount: 0
|
|
35
38
|
}
|
|
36
39
|
}
|
|
37
40
|
}
|
|
@@ -76,6 +79,28 @@ function getOrCreateMetricTags (store, versionsTags) {
|
|
|
76
79
|
return metricTags
|
|
77
80
|
}
|
|
78
81
|
|
|
82
|
+
function updateRaspRequestsMetricTags (metrics, req, raspRuleType) {
|
|
83
|
+
if (!req) return
|
|
84
|
+
|
|
85
|
+
const store = getStore(req)
|
|
86
|
+
|
|
87
|
+
// it does not depend on whether telemetry is enabled or not
|
|
88
|
+
addRaspRequestMetrics(store, metrics)
|
|
89
|
+
|
|
90
|
+
if (!enabled) return
|
|
91
|
+
|
|
92
|
+
const tags = { rule_type: raspRuleType, waf_version: metrics.wafVersion }
|
|
93
|
+
appsecMetrics.count('appsec.rasp.rule.eval', tags).inc(1)
|
|
94
|
+
|
|
95
|
+
if (metrics.wafTimeout) {
|
|
96
|
+
appsecMetrics.count('appsec.rasp.timeout', tags).inc(1)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (metrics.ruleTriggered) {
|
|
100
|
+
appsecMetrics.count('appsec.rasp.rule.match', tags).inc(1)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
79
104
|
function updateWafRequestsMetricTags (metrics, req) {
|
|
80
105
|
if (!req) return
|
|
81
106
|
|
|
@@ -141,6 +166,12 @@ function addRequestMetrics (store, { duration, durationExt }) {
|
|
|
141
166
|
store[DD_TELEMETRY_REQUEST_METRICS].durationExt += durationExt || 0
|
|
142
167
|
}
|
|
143
168
|
|
|
169
|
+
function addRaspRequestMetrics (store, { duration, durationExt }) {
|
|
170
|
+
store[DD_TELEMETRY_REQUEST_METRICS].raspDuration += duration || 0
|
|
171
|
+
store[DD_TELEMETRY_REQUEST_METRICS].raspDurationExt += durationExt || 0
|
|
172
|
+
store[DD_TELEMETRY_REQUEST_METRICS].raspEvalCount++
|
|
173
|
+
}
|
|
174
|
+
|
|
144
175
|
function getRequestMetrics (req) {
|
|
145
176
|
if (req) {
|
|
146
177
|
const store = getStore(req)
|
|
@@ -153,6 +184,7 @@ module.exports = {
|
|
|
153
184
|
disable,
|
|
154
185
|
|
|
155
186
|
updateWafRequestsMetricTags,
|
|
187
|
+
updateRaspRequestsMetricTags,
|
|
156
188
|
incrementWafInitMetric,
|
|
157
189
|
incrementWafUpdatesMetric,
|
|
158
190
|
incrementWafRequestsMetric,
|
|
@@ -46,7 +46,7 @@ function update (newRules) {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
function run (data, req) {
|
|
49
|
+
function run (data, req, raspRuleType) {
|
|
50
50
|
if (!req) {
|
|
51
51
|
const store = storage.getStore()
|
|
52
52
|
if (!store || !store.req) {
|
|
@@ -59,7 +59,7 @@ function run (data, req) {
|
|
|
59
59
|
|
|
60
60
|
const wafContext = waf.wafManager.getWAFContext(req)
|
|
61
61
|
|
|
62
|
-
return wafContext.run(data)
|
|
62
|
+
return wafContext.run(data, raspRuleType)
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
function disposeContext (req) {
|
|
@@ -19,13 +19,13 @@ class WAFContextWrapper {
|
|
|
19
19
|
this.addressesToSkip = new Set()
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
run ({ persistent, ephemeral }) {
|
|
22
|
+
run ({ persistent, ephemeral }, raspRuleType) {
|
|
23
23
|
const payload = {}
|
|
24
24
|
let payloadHasData = false
|
|
25
25
|
const inputs = {}
|
|
26
26
|
const newAddressesToSkip = new Set(this.addressesToSkip)
|
|
27
27
|
|
|
28
|
-
if (persistent && typeof persistent === 'object') {
|
|
28
|
+
if (persistent !== null && typeof persistent === 'object') {
|
|
29
29
|
// TODO: possible optimization: only send params that haven't already been sent with same value to this wafContext
|
|
30
30
|
for (const key of Object.keys(persistent)) {
|
|
31
31
|
// TODO: requiredAddresses is no longer used due to processor addresses are not included in the list. Check on
|
|
@@ -72,7 +72,7 @@ class WAFContextWrapper {
|
|
|
72
72
|
blockTriggered,
|
|
73
73
|
wafVersion: this.wafVersion,
|
|
74
74
|
wafTimeout: result.timeout
|
|
75
|
-
})
|
|
75
|
+
}, raspRuleType)
|
|
76
76
|
|
|
77
77
|
if (ruleTriggered) {
|
|
78
78
|
Reporter.reportAttack(JSON.stringify(result.events))
|
|
@@ -190,7 +190,8 @@ class CiVisibilityExporter extends AgentInfoExporter {
|
|
|
190
190
|
requireGit,
|
|
191
191
|
isEarlyFlakeDetectionEnabled,
|
|
192
192
|
earlyFlakeDetectionNumRetries,
|
|
193
|
-
earlyFlakeDetectionFaultyThreshold
|
|
193
|
+
earlyFlakeDetectionFaultyThreshold,
|
|
194
|
+
isFlakyTestRetriesEnabled
|
|
194
195
|
} = remoteConfiguration
|
|
195
196
|
return {
|
|
196
197
|
isCodeCoverageEnabled,
|
|
@@ -199,7 +200,8 @@ class CiVisibilityExporter extends AgentInfoExporter {
|
|
|
199
200
|
requireGit,
|
|
200
201
|
isEarlyFlakeDetectionEnabled: isEarlyFlakeDetectionEnabled && this._config.isEarlyFlakeDetectionEnabled,
|
|
201
202
|
earlyFlakeDetectionNumRetries,
|
|
202
|
-
earlyFlakeDetectionFaultyThreshold
|
|
203
|
+
earlyFlakeDetectionFaultyThreshold,
|
|
204
|
+
isFlakyTestRetriesEnabled
|
|
203
205
|
}
|
|
204
206
|
}
|
|
205
207
|
|
|
@@ -93,7 +93,8 @@ function getLibraryConfiguration ({
|
|
|
93
93
|
tests_skipping: isSuitesSkippingEnabled,
|
|
94
94
|
itr_enabled: isItrEnabled,
|
|
95
95
|
require_git: requireGit,
|
|
96
|
-
early_flake_detection: earlyFlakeDetectionConfig
|
|
96
|
+
early_flake_detection: earlyFlakeDetectionConfig,
|
|
97
|
+
flaky_test_retries_enabled: isFlakyTestRetriesEnabled
|
|
97
98
|
}
|
|
98
99
|
}
|
|
99
100
|
} = JSON.parse(res)
|
|
@@ -107,7 +108,8 @@ function getLibraryConfiguration ({
|
|
|
107
108
|
earlyFlakeDetectionNumRetries:
|
|
108
109
|
earlyFlakeDetectionConfig?.slow_test_retries?.['5s'] || DEFAULT_EARLY_FLAKE_DETECTION_NUM_RETRIES,
|
|
109
110
|
earlyFlakeDetectionFaultyThreshold:
|
|
110
|
-
earlyFlakeDetectionConfig?.faulty_session_threshold ?? DEFAULT_EARLY_FLAKE_DETECTION_ERROR_THRESHOLD
|
|
111
|
+
earlyFlakeDetectionConfig?.faulty_session_threshold ?? DEFAULT_EARLY_FLAKE_DETECTION_ERROR_THRESHOLD,
|
|
112
|
+
isFlakyTestRetriesEnabled
|
|
111
113
|
}
|
|
112
114
|
|
|
113
115
|
log.debug(() => `Remote settings: ${JSON.stringify(settings)}`)
|