dd-trace 4.15.0 → 4.16.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 -0
- package/ext/tags.d.ts +1 -0
- package/ext/tags.js +1 -0
- package/index.d.ts +1 -0
- package/package.json +6 -4
- package/packages/datadog-esbuild/index.js +30 -25
- package/packages/datadog-instrumentations/src/body-parser.js +2 -2
- package/packages/datadog-instrumentations/src/cookie-parser.js +37 -0
- package/packages/datadog-instrumentations/src/express.js +1 -1
- package/packages/datadog-instrumentations/src/graphql.js +5 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
- package/packages/datadog-instrumentations/src/http/server.js +1 -1
- package/packages/datadog-instrumentations/src/next.js +22 -80
- package/packages/datadog-instrumentations/src/pg.js +14 -15
- package/packages/datadog-instrumentations/src/playwright.js +15 -3
- package/packages/datadog-plugin-mysql/src/index.js +2 -2
- package/packages/datadog-plugin-next/src/index.js +14 -5
- package/packages/datadog-plugin-pg/src/index.js +2 -2
- package/packages/dd-trace/src/appsec/channels.js +1 -0
- package/packages/dd-trace/src/appsec/index.js +18 -5
- package/packages/dd-trace/src/appsec/recommended.json +549 -24
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
- package/packages/dd-trace/src/appsec/remote_config/index.js +2 -0
- package/packages/dd-trace/src/appsec/reporter.js +7 -5
- package/packages/dd-trace/src/appsec/telemetry.js +2 -2
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +5 -4
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +1 -14
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +1 -13
- package/packages/dd-trace/src/datastreams/processor.js +6 -2
- package/packages/dd-trace/src/format.js +6 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +2 -2
- package/packages/dd-trace/src/opentracing/tracer.js +0 -2
- package/packages/dd-trace/src/plugin_manager.js +1 -2
- package/packages/dd-trace/src/plugins/database.js +14 -4
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/outbound.js +4 -3
- package/packages/dd-trace/src/telemetry/index.js +10 -1
|
@@ -46,6 +46,7 @@ function enableWafUpdate (appsecConfig) {
|
|
|
46
46
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_REQUEST_BLOCKING, true)
|
|
47
47
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_RULES, true)
|
|
48
48
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_BLOCKING_RESPONSE, true)
|
|
49
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_TRUSTED_IPS, true)
|
|
49
50
|
|
|
50
51
|
rc.on('ASM_DATA', noop)
|
|
51
52
|
rc.on('ASM_DD', noop)
|
|
@@ -66,6 +67,7 @@ function disableWafUpdate () {
|
|
|
66
67
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_REQUEST_BLOCKING, false)
|
|
67
68
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_RULES, false)
|
|
68
69
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_BLOCKING_RESPONSE, false)
|
|
70
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_TRUSTED_IPS, false)
|
|
69
71
|
|
|
70
72
|
rc.off('ASM_DATA', noop)
|
|
71
73
|
rc.off('ASM_DD', noop)
|
|
@@ -69,16 +69,18 @@ function formatHeaderName (name) {
|
|
|
69
69
|
.toLowerCase()
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
function reportWafInit (wafVersion,
|
|
72
|
+
function reportWafInit (wafVersion, rulesVersion, diagnosticsRules = {}) {
|
|
73
73
|
metricsQueue.set('_dd.appsec.waf.version', wafVersion)
|
|
74
74
|
|
|
75
|
-
metricsQueue.set('_dd.appsec.event_rules.loaded',
|
|
76
|
-
metricsQueue.set('_dd.appsec.event_rules.error_count',
|
|
77
|
-
if (
|
|
75
|
+
metricsQueue.set('_dd.appsec.event_rules.loaded', diagnosticsRules.loaded?.length || 0)
|
|
76
|
+
metricsQueue.set('_dd.appsec.event_rules.error_count', diagnosticsRules.failed?.length || 0)
|
|
77
|
+
if (diagnosticsRules.failed?.length) {
|
|
78
|
+
metricsQueue.set('_dd.appsec.event_rules.errors', JSON.stringify(diagnosticsRules.errors))
|
|
79
|
+
}
|
|
78
80
|
|
|
79
81
|
metricsQueue.set('manual.keep', 'true')
|
|
80
82
|
|
|
81
|
-
incrementWafInitMetric(wafVersion,
|
|
83
|
+
incrementWafInitMetric(wafVersion, rulesVersion)
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
function reportMetrics (metrics) {
|
|
@@ -51,7 +51,7 @@ function trackWafDurations (metrics, versionsTags) {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
function getOrCreateMetricTags (
|
|
54
|
+
function getOrCreateMetricTags (req, versionsTags) {
|
|
55
55
|
const store = getStore(req)
|
|
56
56
|
|
|
57
57
|
let metricTags = store[DD_TELEMETRY_WAF_RESULT_TAGS]
|
|
@@ -75,7 +75,7 @@ function updateWafRequestsMetricTags (metrics, req) {
|
|
|
75
75
|
|
|
76
76
|
trackWafDurations(metrics, versionsTags)
|
|
77
77
|
|
|
78
|
-
const metricTags = getOrCreateMetricTags(
|
|
78
|
+
const metricTags = getOrCreateMetricTags(req, versionsTags)
|
|
79
79
|
|
|
80
80
|
const { blockTriggered, ruleTriggered, wafTimeout } = metrics
|
|
81
81
|
|
|
@@ -4,12 +4,12 @@ const log = require('../../log')
|
|
|
4
4
|
const Reporter = require('../reporter')
|
|
5
5
|
|
|
6
6
|
class WAFContextWrapper {
|
|
7
|
-
constructor (ddwafContext, requiredAddresses, wafTimeout,
|
|
7
|
+
constructor (ddwafContext, requiredAddresses, wafTimeout, wafVersion, rulesVersion) {
|
|
8
8
|
this.ddwafContext = ddwafContext
|
|
9
9
|
this.requiredAddresses = requiredAddresses
|
|
10
10
|
this.wafTimeout = wafTimeout
|
|
11
|
-
this.rulesVersion = rulesInfo.version
|
|
12
11
|
this.wafVersion = wafVersion
|
|
12
|
+
this.rulesVersion = rulesVersion
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
run (params) {
|
|
@@ -33,7 +33,7 @@ class WAFContextWrapper {
|
|
|
33
33
|
|
|
34
34
|
const end = process.hrtime.bigint()
|
|
35
35
|
|
|
36
|
-
const ruleTriggered = !!result.
|
|
36
|
+
const ruleTriggered = !!result.events?.length
|
|
37
37
|
const blockTriggered = result.actions?.includes('block')
|
|
38
38
|
|
|
39
39
|
Reporter.reportMetrics({
|
|
@@ -47,7 +47,7 @@ class WAFContextWrapper {
|
|
|
47
47
|
})
|
|
48
48
|
|
|
49
49
|
if (ruleTriggered) {
|
|
50
|
-
Reporter.reportAttack(result.
|
|
50
|
+
Reporter.reportAttack(JSON.stringify(result.events))
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
return result.actions
|
|
@@ -12,8 +12,9 @@ class WAFManager {
|
|
|
12
12
|
this.wafTimeout = config.wafTimeout
|
|
13
13
|
this.ddwaf = this._loadDDWAF(rules)
|
|
14
14
|
this.ddwafVersion = this.ddwaf.constructor.version()
|
|
15
|
+
this.rulesVersion = this.ddwaf.diagnostics.ruleset_version
|
|
15
16
|
|
|
16
|
-
Reporter.reportWafInit(this.ddwafVersion, this.ddwaf.
|
|
17
|
+
Reporter.reportWafInit(this.ddwafVersion, this.rulesVersion, this.ddwaf.diagnostics.rules)
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
_loadDDWAF (rules) {
|
|
@@ -38,8 +39,8 @@ class WAFManager {
|
|
|
38
39
|
this.ddwaf.createContext(),
|
|
39
40
|
this.ddwaf.requiredAddresses,
|
|
40
41
|
this.wafTimeout,
|
|
41
|
-
this.
|
|
42
|
-
this.
|
|
42
|
+
this.ddwafVersion,
|
|
43
|
+
this.rulesVersion
|
|
43
44
|
)
|
|
44
45
|
contexts.set(req, wafContext)
|
|
45
46
|
}
|
|
@@ -50,7 +51,7 @@ class WAFManager {
|
|
|
50
51
|
update (newRules) {
|
|
51
52
|
this.ddwaf.update(newRules)
|
|
52
53
|
|
|
53
|
-
Reporter.reportWafUpdate(this.ddwafVersion, this.
|
|
54
|
+
Reporter.reportWafUpdate(this.ddwafVersion, this.rulesVersion)
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
destroy () {
|
package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js
CHANGED
|
@@ -28,25 +28,12 @@ function getItrConfiguration ({
|
|
|
28
28
|
if (isEvpProxy) {
|
|
29
29
|
options.path = '/evp_proxy/v2/api/v2/libraries/tests/services/setting'
|
|
30
30
|
options.headers['X-Datadog-EVP-Subdomain'] = 'api'
|
|
31
|
-
options.headers['X-Datadog-NeedsAppKey'] = 'true'
|
|
32
31
|
} else {
|
|
33
32
|
const apiKey = process.env.DATADOG_API_KEY || process.env.DD_API_KEY
|
|
34
|
-
const appKey = process.env.DATADOG_APP_KEY ||
|
|
35
|
-
process.env.DD_APP_KEY ||
|
|
36
|
-
process.env.DATADOG_APPLICATION_KEY ||
|
|
37
|
-
process.env.DD_APPLICATION_KEY
|
|
38
|
-
|
|
39
|
-
const messagePrefix = 'Request to settings endpoint was not done because Datadog'
|
|
40
|
-
|
|
41
|
-
if (!appKey) {
|
|
42
|
-
return done(new Error(`${messagePrefix} application key is not defined.`))
|
|
43
|
-
}
|
|
44
33
|
if (!apiKey) {
|
|
45
|
-
return done(new Error(
|
|
34
|
+
return done(new Error('Request to settings endpoint was not done because Datadog API key is not defined.'))
|
|
46
35
|
}
|
|
47
|
-
|
|
48
36
|
options.headers['dd-api-key'] = apiKey
|
|
49
|
-
options.headers['dd-application-key'] = appKey
|
|
50
37
|
}
|
|
51
38
|
|
|
52
39
|
const data = JSON.stringify({
|
|
@@ -28,25 +28,13 @@ function getSkippableSuites ({
|
|
|
28
28
|
if (isEvpProxy) {
|
|
29
29
|
options.path = '/evp_proxy/v2/api/v2/ci/tests/skippable'
|
|
30
30
|
options.headers['X-Datadog-EVP-Subdomain'] = 'api'
|
|
31
|
-
options.headers['X-Datadog-NeedsAppKey'] = 'true'
|
|
32
31
|
} else {
|
|
33
32
|
const apiKey = process.env.DATADOG_API_KEY || process.env.DD_API_KEY
|
|
34
|
-
const appKey = process.env.DATADOG_APP_KEY ||
|
|
35
|
-
process.env.DD_APP_KEY ||
|
|
36
|
-
process.env.DATADOG_APPLICATION_KEY ||
|
|
37
|
-
process.env.DD_APPLICATION_KEY
|
|
38
|
-
|
|
39
|
-
const messagePrefix = 'Skippable suites were not fetched because Datadog'
|
|
40
|
-
|
|
41
|
-
if (!appKey) {
|
|
42
|
-
return done(new Error(`${messagePrefix} application key is not defined.`))
|
|
43
|
-
}
|
|
44
33
|
if (!apiKey) {
|
|
45
|
-
return done(new Error(
|
|
34
|
+
return done(new Error('Skippable suites were not fetched because Datadog API key is not defined.'))
|
|
46
35
|
}
|
|
47
36
|
|
|
48
37
|
options.headers['dd-api-key'] = apiKey
|
|
49
|
-
options.headers['dd-application-key'] = appKey
|
|
50
38
|
}
|
|
51
39
|
|
|
52
40
|
const data = JSON.stringify({
|
|
@@ -66,7 +66,9 @@ class DataStreamsProcessor {
|
|
|
66
66
|
port,
|
|
67
67
|
url,
|
|
68
68
|
env,
|
|
69
|
-
tags
|
|
69
|
+
tags,
|
|
70
|
+
version,
|
|
71
|
+
service
|
|
70
72
|
} = {}) {
|
|
71
73
|
this.writer = new DataStreamsWriter({
|
|
72
74
|
hostname,
|
|
@@ -79,7 +81,8 @@ class DataStreamsProcessor {
|
|
|
79
81
|
this.enabled = dsmEnabled
|
|
80
82
|
this.env = env
|
|
81
83
|
this.tags = tags || {}
|
|
82
|
-
this.service =
|
|
84
|
+
this.service = service || 'unnamed-nodejs-service'
|
|
85
|
+
this.version = version || ''
|
|
83
86
|
this.sequence = 0
|
|
84
87
|
|
|
85
88
|
if (this.enabled) {
|
|
@@ -96,6 +99,7 @@ class DataStreamsProcessor {
|
|
|
96
99
|
Service: this.service,
|
|
97
100
|
Stats: serialized,
|
|
98
101
|
TracerVersion: pkg.version,
|
|
102
|
+
Version: this.version,
|
|
99
103
|
Lang: 'javascript'
|
|
100
104
|
}
|
|
101
105
|
this.writer.flush(payload)
|
|
@@ -13,7 +13,7 @@ const SPAN_SAMPLING_MECHANISM = constants.SPAN_SAMPLING_MECHANISM
|
|
|
13
13
|
const SPAN_SAMPLING_RULE_RATE = constants.SPAN_SAMPLING_RULE_RATE
|
|
14
14
|
const SPAN_SAMPLING_MAX_PER_SECOND = constants.SPAN_SAMPLING_MAX_PER_SECOND
|
|
15
15
|
const SAMPLING_MECHANISM_SPAN = constants.SAMPLING_MECHANISM_SPAN
|
|
16
|
-
const MEASURED = tags
|
|
16
|
+
const { MEASURED, BASE_SERVICE } = tags
|
|
17
17
|
const ORIGIN_KEY = constants.ORIGIN_KEY
|
|
18
18
|
const HOSTNAME_KEY = constants.HOSTNAME_KEY
|
|
19
19
|
const TOP_LEVEL_KEY = constants.TOP_LEVEL_KEY
|
|
@@ -73,6 +73,11 @@ function extractTags (trace, span) {
|
|
|
73
73
|
addTag({}, trace.metrics, MEASURED, 1)
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
const tracerService = span.tracer()._service.toLowerCase()
|
|
77
|
+
if (tags['service.name']?.toLowerCase() !== tracerService) {
|
|
78
|
+
span.setTag(BASE_SERVICE, tracerService)
|
|
79
|
+
}
|
|
80
|
+
|
|
76
81
|
for (const tag in tags) {
|
|
77
82
|
switch (tag) {
|
|
78
83
|
case 'service.name':
|
|
@@ -343,10 +343,10 @@ class TextMapPropagator {
|
|
|
343
343
|
spanContext._trace.origin = value
|
|
344
344
|
break
|
|
345
345
|
case 't.dm': {
|
|
346
|
-
const mechanism =
|
|
346
|
+
const mechanism = Math.abs(parseInt(value, 10))
|
|
347
347
|
if (Number.isInteger(mechanism)) {
|
|
348
348
|
spanContext._sampling.mechanism = mechanism
|
|
349
|
-
spanContext._trace.tags['_dd.p.dm'] =
|
|
349
|
+
spanContext._trace.tags['_dd.p.dm'] = `-${mechanism}`
|
|
350
350
|
}
|
|
351
351
|
break
|
|
352
352
|
}
|
|
@@ -26,8 +26,6 @@ class DatadogTracer {
|
|
|
26
26
|
this._version = config.version
|
|
27
27
|
this._env = config.env
|
|
28
28
|
this._tags = config.tags
|
|
29
|
-
this._computePeerService = config.spanComputePeerService
|
|
30
|
-
this._peerServiceMapping = config.peerServiceMapping
|
|
31
29
|
this._logInjection = config.logInjection
|
|
32
30
|
this._debug = config.debug
|
|
33
31
|
this._prioritySampler = new PrioritySampler(config.env, config.sampler)
|
|
@@ -72,11 +72,10 @@ module.exports = class PluginManager {
|
|
|
72
72
|
const Plugin = pluginClasses[name]
|
|
73
73
|
|
|
74
74
|
if (!Plugin) return
|
|
75
|
+
if (!this._tracerConfig) return // TODO: don't wait for tracer to be initialized
|
|
75
76
|
if (!this._pluginsByName[name]) {
|
|
76
77
|
this._pluginsByName[name] = new Plugin(this._tracer, this._tracerConfig)
|
|
77
78
|
}
|
|
78
|
-
if (!this._tracerConfig) return // TODO: don't wait for tracer to be initialized
|
|
79
|
-
|
|
80
79
|
const pluginConfig = this._configsByName[name] || {
|
|
81
80
|
enabled: this._tracerConfig.plugins !== false
|
|
82
81
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const StoragePlugin = require('./storage')
|
|
4
|
+
const { PEER_SERVICE_KEY } = require('../constants')
|
|
4
5
|
|
|
5
6
|
class DatabasePlugin extends StoragePlugin {
|
|
6
7
|
static get operation () { return 'query' }
|
|
@@ -38,20 +39,29 @@ class DatabasePlugin extends StoragePlugin {
|
|
|
38
39
|
`ddps='${encodedDdps}',ddpv='${encodedDdpv}'`
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
getDbmServiceName (span, tracerService) {
|
|
43
|
+
if (this._tracerConfig.spanComputePeerService) {
|
|
44
|
+
const peerData = this.getPeerService(span.context()._tags)
|
|
45
|
+
return this.getPeerServiceRemap(peerData)[PEER_SERVICE_KEY] || tracerService
|
|
46
|
+
}
|
|
47
|
+
return tracerService
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
injectDbmQuery (span, query, serviceName, isPreparedStatement = false) {
|
|
42
51
|
const mode = this.config.dbmPropagationMode
|
|
52
|
+
const dbmService = this.getDbmServiceName(span, serviceName)
|
|
43
53
|
|
|
44
54
|
if (mode === 'disabled') {
|
|
45
55
|
return query
|
|
46
56
|
}
|
|
47
57
|
|
|
48
|
-
const servicePropagation = this.createDBMPropagationCommentService(
|
|
58
|
+
const servicePropagation = this.createDBMPropagationCommentService(dbmService)
|
|
49
59
|
|
|
50
60
|
if (isPreparedStatement || mode === 'service') {
|
|
51
61
|
return `/*${servicePropagation}*/ ${query}`
|
|
52
62
|
} else if (mode === 'full') {
|
|
53
|
-
|
|
54
|
-
const traceparent =
|
|
63
|
+
span.setTag('_dd.dbm_trace_injected', 'true')
|
|
64
|
+
const traceparent = span._spanContext.toTraceparent()
|
|
55
65
|
return `/*${servicePropagation},traceparent='${traceparent}'*/ ${query}`
|
|
56
66
|
}
|
|
57
67
|
}
|
|
@@ -64,6 +64,7 @@ module.exports = {
|
|
|
64
64
|
get 'pg' () { return require('../../../datadog-plugin-pg/src') },
|
|
65
65
|
get 'pino' () { return require('../../../datadog-plugin-pino/src') },
|
|
66
66
|
get 'pino-pretty' () { return require('../../../datadog-plugin-pino/src') },
|
|
67
|
+
get 'playwright' () { return require('../../../datadog-plugin-playwright/src') },
|
|
67
68
|
get 'redis' () { return require('../../../datadog-plugin-redis/src') },
|
|
68
69
|
get 'restify' () { return require('../../../datadog-plugin-restify/src') },
|
|
69
70
|
get 'rhea' () { return require('../../../datadog-plugin-rhea/src') },
|
|
@@ -64,10 +64,11 @@ class OutboundPlugin extends TracingPlugin {
|
|
|
64
64
|
* peer service and add the value we overrode.
|
|
65
65
|
*/
|
|
66
66
|
const peerService = peerData[PEER_SERVICE_KEY]
|
|
67
|
-
|
|
67
|
+
const mappedService = this._tracerConfig.peerServiceMapping?.[peerService]
|
|
68
|
+
if (peerService && mappedService) {
|
|
68
69
|
return {
|
|
69
70
|
...peerData,
|
|
70
|
-
[PEER_SERVICE_KEY]:
|
|
71
|
+
[PEER_SERVICE_KEY]: mappedService,
|
|
71
72
|
[PEER_SERVICE_REMAP_KEY]: peerService
|
|
72
73
|
}
|
|
73
74
|
}
|
|
@@ -80,7 +81,7 @@ class OutboundPlugin extends TracingPlugin {
|
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
tagPeerService (span) {
|
|
83
|
-
if (this.
|
|
84
|
+
if (this._tracerConfig.spanComputePeerService) {
|
|
84
85
|
const peerData = this.getPeerService(span.context()._tags)
|
|
85
86
|
if (peerData !== undefined) {
|
|
86
87
|
span.addTags(this.getPeerServiceRemap(peerData))
|
|
@@ -57,11 +57,20 @@ function appStarted () {
|
|
|
57
57
|
return {
|
|
58
58
|
integrations: getIntegrations(),
|
|
59
59
|
dependencies: [],
|
|
60
|
-
configuration: flatten(config),
|
|
60
|
+
configuration: flatten(formatConfig(config)),
|
|
61
61
|
additional_payload: []
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
function formatConfig (config) {
|
|
66
|
+
// format peerServiceMapping from an object to a string map in order for
|
|
67
|
+
// telemetry intake to accept the configuration
|
|
68
|
+
config.peerServiceMapping = config.peerServiceMapping
|
|
69
|
+
? Object.entries(config.peerServiceMapping).map(([key, value]) => `${key}:${value}`).join(',')
|
|
70
|
+
: ''
|
|
71
|
+
return config
|
|
72
|
+
}
|
|
73
|
+
|
|
65
74
|
function onBeforeExit () {
|
|
66
75
|
process.removeListener('beforeExit', onBeforeExit)
|
|
67
76
|
sendData(config, application, host, 'app-closing')
|