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.
Files changed (93) hide show
  1. package/LICENSE-3rdparty.csv +1 -2
  2. package/ext/exporters.d.ts +1 -1
  3. package/index.d.ts +105 -37
  4. package/init.js +40 -1
  5. package/initialize.mjs +8 -5
  6. package/package.json +29 -29
  7. package/packages/datadog-core/src/storage/index.js +1 -10
  8. package/packages/datadog-esbuild/index.js +5 -1
  9. package/packages/datadog-instrumentations/src/aws-sdk.js +2 -1
  10. package/packages/datadog-instrumentations/src/child_process.js +2 -2
  11. package/packages/datadog-instrumentations/src/cucumber.js +76 -34
  12. package/packages/datadog-instrumentations/src/fs.js +1 -1
  13. package/packages/datadog-instrumentations/src/hapi.js +1 -1
  14. package/packages/datadog-instrumentations/src/helpers/hook.js +8 -3
  15. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  16. package/packages/datadog-instrumentations/src/helpers/instrument.js +4 -3
  17. package/packages/datadog-instrumentations/src/helpers/register.js +56 -5
  18. package/packages/datadog-instrumentations/src/http/client.js +1 -1
  19. package/packages/datadog-instrumentations/src/jest.js +17 -2
  20. package/packages/datadog-instrumentations/src/kafkajs.js +1 -1
  21. package/packages/datadog-instrumentations/src/ldapjs.js +2 -2
  22. package/packages/datadog-instrumentations/src/mocha/main.js +12 -1
  23. package/packages/datadog-instrumentations/src/mocha/utils.js +58 -14
  24. package/packages/datadog-instrumentations/src/mocha/worker.js +1 -0
  25. package/packages/datadog-instrumentations/src/mquery.js +2 -2
  26. package/packages/datadog-instrumentations/src/next.js +1 -1
  27. package/packages/datadog-instrumentations/src/pg.js +2 -2
  28. package/packages/datadog-instrumentations/src/playwright.js +47 -33
  29. package/packages/datadog-instrumentations/src/restify.js +1 -1
  30. package/packages/datadog-instrumentations/src/vitest.js +349 -0
  31. package/packages/datadog-plugin-aws-sdk/src/base.js +8 -1
  32. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -1
  33. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
  34. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +6 -1
  35. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +23 -5
  36. package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +1 -1
  37. package/packages/datadog-plugin-child_process/src/index.js +1 -1
  38. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -4
  39. package/packages/datadog-plugin-cucumber/src/index.js +24 -1
  40. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +79 -42
  41. package/packages/datadog-plugin-cypress/src/plugin.js +4 -3
  42. package/packages/datadog-plugin-fs/src/index.js +1 -1
  43. package/packages/datadog-plugin-jest/src/index.js +7 -1
  44. package/packages/datadog-plugin-kafkajs/src/producer.js +1 -1
  45. package/packages/datadog-plugin-mocha/src/index.js +25 -4
  46. package/packages/datadog-plugin-mongodb-core/src/index.js +1 -1
  47. package/packages/datadog-plugin-openai/src/index.js +57 -35
  48. package/packages/datadog-plugin-openai/src/token-estimator.js +20 -0
  49. package/packages/datadog-plugin-playwright/src/index.js +4 -1
  50. package/packages/datadog-plugin-sharedb/src/index.js +1 -1
  51. package/packages/datadog-plugin-vitest/src/index.js +167 -0
  52. package/packages/dd-trace/src/analytics_sampler.js +1 -1
  53. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +1 -1
  54. package/packages/dd-trace/src/appsec/iast/path-line.js +2 -19
  55. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +2 -2
  56. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +2 -2
  57. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +3 -1
  58. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -0
  59. package/packages/dd-trace/src/appsec/index.js +4 -4
  60. package/packages/dd-trace/src/appsec/passport.js +1 -1
  61. package/packages/dd-trace/src/appsec/rasp.js +32 -5
  62. package/packages/dd-trace/src/appsec/recommended.json +208 -3
  63. package/packages/dd-trace/src/appsec/reporter.js +60 -20
  64. package/packages/dd-trace/src/appsec/sdk/track_event.js +3 -0
  65. package/packages/dd-trace/src/appsec/stack_trace.js +90 -0
  66. package/packages/dd-trace/src/appsec/standalone.js +130 -0
  67. package/packages/dd-trace/src/appsec/telemetry.js +33 -1
  68. package/packages/dd-trace/src/appsec/waf/index.js +2 -2
  69. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +3 -3
  70. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
  71. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
  72. package/packages/dd-trace/src/config.js +136 -63
  73. package/packages/dd-trace/src/constants.js +3 -1
  74. package/packages/dd-trace/src/datastreams/processor.js +3 -2
  75. package/packages/dd-trace/src/exporters/agent/index.js +2 -2
  76. package/packages/dd-trace/src/format.js +1 -0
  77. package/packages/dd-trace/src/opentelemetry/span.js +1 -1
  78. package/packages/dd-trace/src/opentelemetry/tracer.js +6 -0
  79. package/packages/dd-trace/src/opentracing/propagation/text_map.js +12 -0
  80. package/packages/dd-trace/src/opentracing/span.js +4 -1
  81. package/packages/dd-trace/src/opentracing/tracer.js +2 -2
  82. package/packages/dd-trace/src/plugins/ci_plugin.js +7 -0
  83. package/packages/dd-trace/src/plugins/index.js +2 -0
  84. package/packages/dd-trace/src/plugins/util/test.js +5 -1
  85. package/packages/dd-trace/src/priority_sampler.js +2 -5
  86. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  87. package/packages/dd-trace/src/proxy.js +3 -1
  88. package/packages/dd-trace/src/rate_limiter.js +2 -2
  89. package/packages/dd-trace/src/span_stats.js +4 -3
  90. package/packages/dd-trace/src/telemetry/init-telemetry.js +75 -0
  91. package/packages/dd-trace/src/tracer.js +2 -2
  92. package/packages/dd-trace/src/util.js +6 -1
  93. 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 REQUEST_HEADERS_MAP = mapHeaderAndTags([
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 IDENTIFICATION_HEADERS_MAP = mapHeaderAndTags([
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('manual.keep', 'true')
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
- updateWafRequestsMetricTags(metrics, store.req)
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 = filterHeaders(req.headers, REQUEST_HEADERS_MAP)
116
-
117
- newTags['appsec.event'] = 'true'
127
+ const newTags = {
128
+ 'appsec.event': 'true'
129
+ }
118
130
 
119
131
  if (limiter.isAllowed()) {
120
- newTags['manual.keep'] = 'true' // TODO: figure out how to keep appsec traces with sampling revamp
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
- rootSpan.addTags(filterHeaders(req.headers, IDENTIFICATION_HEADERS_MAP))
209
+ const mandatoryTags = filterHeaders(req.headers, REQUEST_HEADERS_MAP)
210
+ rootSpan.addTags(mandatoryTags)
187
211
 
188
- if (!rootSpan.context()._tags['appsec.event']) return
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 (req.route && typeof req.route.path === 'string') {
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)}`)