dd-trace 6.11.0 → 6.13.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 +5 -0
- package/ci/test-optimization-validation/framework-adapters/cypress.js +9 -11
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +5 -4
- package/index.d.ts +143 -25
- package/package.json +26 -25
- package/packages/datadog-instrumentations/src/bunyan.js +4 -2
- package/packages/datadog-instrumentations/src/cucumber.js +4 -4
- package/packages/datadog-instrumentations/src/helpers/bunyan.js +12 -3
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +28 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/jest.js +140 -18
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +1597 -0
- package/packages/datadog-instrumentations/src/mariadb.js +7 -0
- package/packages/datadog-instrumentations/src/mocha/utils.js +5 -2
- package/packages/datadog-instrumentations/src/pino.js +15 -4
- package/packages/datadog-instrumentations/src/playwright-reporter.js +149 -1
- package/packages/datadog-instrumentations/src/playwright.js +67 -53
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +13 -12
- package/packages/datadog-instrumentations/src/vitest-worker.js +11 -0
- package/packages/datadog-instrumentations/src/webdriverio.js +26 -0
- package/packages/datadog-instrumentations/src/winston.js +6 -3
- package/packages/datadog-plugin-cypress/src/support.js +1 -0
- package/packages/datadog-plugin-http/src/client.js +2 -15
- package/packages/datadog-plugin-http2/src/client.js +2 -15
- package/packages/datadog-plugin-openai-agents/src/integration.js +15 -11
- package/packages/datadog-plugin-playwright/src/index.js +13 -1
- package/packages/datadog-plugin-undici/src/index.js +2 -15
- package/packages/dd-trace/src/aiguard/evaluation.js +94 -20
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -3
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +21 -9
- package/packages/dd-trace/src/aiguard/noop.js +9 -1
- package/packages/dd-trace/src/aiguard/redaction.js +183 -0
- package/packages/dd-trace/src/aiguard/sdk.js +7 -2
- package/packages/dd-trace/src/aiguard/tags.js +2 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +10 -9
- package/packages/dd-trace/src/appsec/reporter.js +2 -0
- package/packages/dd-trace/src/appsec/rule_manager.js +2 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +13 -0
- package/packages/dd-trace/src/appsec/telemetry/rasp.js +16 -0
- package/packages/dd-trace/src/appsec/telemetry/waf.js +24 -0
- package/packages/dd-trace/src/carrier.js +107 -48
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +5 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +2 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +5 -3
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +51 -26
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +4 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +5 -11
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +6 -4
- package/packages/dd-trace/src/ci-visibility/final-flush.js +20 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +5 -3
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +196 -29
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +4 -2
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +4 -2
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +5 -5
- package/packages/dd-trace/src/config/generated-config-types.d.ts +6 -0
- package/packages/dd-trace/src/config/index.js +2 -1
- package/packages/dd-trace/src/config/supported-configurations.json +27 -0
- package/packages/dd-trace/src/debugger/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/index.js +3 -10
- package/packages/dd-trace/src/debugger/devtools_client/send.js +4 -3
- package/packages/dd-trace/src/debugger/index.js +4 -2
- package/packages/dd-trace/src/debugger/inspect-segment.js +154 -0
- package/packages/dd-trace/src/dogstatsd.js +45 -18
- package/packages/dd-trace/src/encode/agentless-json.js +5 -4
- package/packages/dd-trace/src/evp_proxy/constants.js +8 -0
- package/packages/dd-trace/src/evp_proxy/direct.js +64 -0
- package/packages/dd-trace/src/evp_proxy/discovery.js +133 -0
- package/packages/dd-trace/src/evp_proxy/path.js +22 -0
- package/packages/dd-trace/src/exporters/agent/index.js +22 -2
- package/packages/dd-trace/src/exporters/agent/writer.js +4 -4
- package/packages/dd-trace/src/exporters/common/request.js +5 -2
- package/packages/dd-trace/src/exporters/common/writer.js +24 -2
- package/packages/dd-trace/src/exporters/span-stats/index.js +23 -4
- package/packages/dd-trace/src/external-logger/src/index.js +4 -3
- package/packages/dd-trace/src/flush.js +102 -0
- package/packages/dd-trace/src/llmobs/experiments/client.js +10 -2
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +301 -14
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +440 -112
- package/packages/dd-trace/src/llmobs/experiments/index.js +68 -44
- package/packages/dd-trace/src/llmobs/experiments/noop.js +103 -12
- package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
- package/packages/dd-trace/src/llmobs/experiments/util.js +81 -4
- package/packages/dd-trace/src/llmobs/index.js +80 -17
- package/packages/dd-trace/src/llmobs/noop.js +2 -1
- package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +8 -4
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +6 -3
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +13 -6
- package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +5 -7
- package/packages/dd-trace/src/llmobs/writers/base.js +60 -24
- package/packages/dd-trace/src/noop/span.js +5 -0
- package/packages/dd-trace/src/openfeature/constants/constants.js +0 -18
- package/packages/dd-trace/src/openfeature/index.js +7 -4
- package/packages/dd-trace/src/openfeature/writers/base.js +153 -11
- package/packages/dd-trace/src/openfeature/writers/exposures.js +70 -15
- package/packages/dd-trace/src/openfeature/writers/util.js +97 -15
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +45 -3
- package/packages/dd-trace/src/opentelemetry/logs/index.js +4 -1
- package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +7 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +3 -1
- package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +8 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +4 -2
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +4 -2
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +25 -5
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +61 -25
- package/packages/dd-trace/src/opentelemetry/tracer.js +26 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +429 -352
- package/packages/dd-trace/src/opentracing/span.js +28 -0
- package/packages/dd-trace/src/otel-thread-ctx.js +23 -5
- package/packages/dd-trace/src/plugin_manager.js +2 -0
- package/packages/dd-trace/src/plugins/util/status-validator.js +98 -0
- package/packages/dd-trace/src/plugins/util/test.js +74 -21
- package/packages/dd-trace/src/plugins/util/web.js +2 -65
- package/packages/dd-trace/src/profiling/oom.js +4 -3
- package/packages/dd-trace/src/profiling/profiler.js +7 -0
- package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +14 -14
- package/packages/dd-trace/src/proxy.js +36 -8
- package/packages/dd-trace/src/runtime_metrics/index.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +17 -6
- package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +55 -0
- package/packages/dd-trace/src/serverless/vercel.js +122 -0
- package/packages/dd-trace/src/serverless.js +37 -20
- package/packages/dd-trace/src/span_stats.js +40 -3
- package/packages/dd-trace/src/telemetry/metrics.js +62 -8
- package/packages/dd-trace/src/telemetry/send-data.js +1 -0
- package/packages/dd-trace/src/tracer.js +22 -0
- package/packages/dd-trace/src/web-tags-cache.js +67 -13
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/https-proxy-agent/LICENSE +22 -0
- package/vendor/dist/https-proxy-agent/index.js +4 -0
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/proxy-from-env/LICENSE +20 -0
- package/vendor/dist/proxy-from-env/index.js +1 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('../../../../index').aiguard.RedactionReplacement} RedactionReplacement */
|
|
4
|
+
|
|
5
|
+
const SEGMENT_PATTERN = /^([A-Za-z0-9_]+)(?:\[([0-9]+)\])?$/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Converts the raw backend replacements into the public, typed contract.
|
|
9
|
+
*
|
|
10
|
+
* @param {unknown} replacements
|
|
11
|
+
* @returns {RedactionReplacement[]}
|
|
12
|
+
*/
|
|
13
|
+
function normalizeRedactionReplacements (replacements) {
|
|
14
|
+
if (!Array.isArray(replacements)) return []
|
|
15
|
+
|
|
16
|
+
const result = []
|
|
17
|
+
for (const entry of replacements) {
|
|
18
|
+
if (entry === null || typeof entry !== 'object' || Array.isArray(entry)) continue
|
|
19
|
+
|
|
20
|
+
const { path, replacement } = entry
|
|
21
|
+
if (typeof path !== 'string' || path.length === 0 || typeof replacement !== 'string') continue
|
|
22
|
+
|
|
23
|
+
result.push({ path, replacement })
|
|
24
|
+
}
|
|
25
|
+
return result
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Parses an AI Guard location path into named segments and optional indexes.
|
|
30
|
+
*
|
|
31
|
+
* @param {string} path
|
|
32
|
+
* @returns {Array<{ name: string, index: number|undefined }>|undefined}
|
|
33
|
+
*/
|
|
34
|
+
function parsePath (path) {
|
|
35
|
+
const segments = []
|
|
36
|
+
for (const rawSegment of path.split('.')) {
|
|
37
|
+
const match = SEGMENT_PATTERN.exec(rawSegment)
|
|
38
|
+
if (!match) return
|
|
39
|
+
|
|
40
|
+
segments.push({
|
|
41
|
+
name: match[1],
|
|
42
|
+
index: match[2] === undefined ? undefined : Number(match[2]),
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
return segments
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Reports whether a parsed path targets an allowed message string.
|
|
50
|
+
*
|
|
51
|
+
* @param {Array<{ name: string, index: number|undefined }>} segments
|
|
52
|
+
* @returns {boolean}
|
|
53
|
+
*/
|
|
54
|
+
function isRedactablePath (segments) {
|
|
55
|
+
const first = segments[0]
|
|
56
|
+
if (first?.name !== 'messages' || first.index === undefined) return false
|
|
57
|
+
|
|
58
|
+
if (segments.length === 2) {
|
|
59
|
+
const content = segments[1]
|
|
60
|
+
return content.name === 'content' && content.index === undefined
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (segments.length === 3) {
|
|
64
|
+
const content = segments[1]
|
|
65
|
+
const text = segments[2]
|
|
66
|
+
return content.name === 'content' && content.index !== undefined &&
|
|
67
|
+
text.name === 'text' && text.index === undefined
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (segments.length === 4) {
|
|
71
|
+
const toolCalls = segments[1]
|
|
72
|
+
const functionSegment = segments[2]
|
|
73
|
+
const argumentsSegment = segments[3]
|
|
74
|
+
return toolCalls.name === 'tool_calls' && toolCalls.index !== undefined &&
|
|
75
|
+
functionSegment.name === 'function' && functionSegment.index === undefined &&
|
|
76
|
+
argumentsSegment.name === 'arguments' && argumentsSegment.index === undefined
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return false
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Resolves a location path to a writable string container and key.
|
|
84
|
+
*
|
|
85
|
+
* @param {{ messages: Array<object> }} root
|
|
86
|
+
* @param {string} path
|
|
87
|
+
* @returns {{ container: object|Array<unknown>, key: string|number }|undefined}
|
|
88
|
+
*/
|
|
89
|
+
function resolveWritableString (root, path) {
|
|
90
|
+
const segments = parsePath(path)
|
|
91
|
+
if (!segments || !isRedactablePath(segments)) return
|
|
92
|
+
|
|
93
|
+
const terminal = segments.at(-1)
|
|
94
|
+
let node = root
|
|
95
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
96
|
+
const { name, index } = segments[i]
|
|
97
|
+
if (!node || typeof node !== 'object' || !Object.hasOwn(node, name)) return
|
|
98
|
+
|
|
99
|
+
node = node[name]
|
|
100
|
+
if (index !== undefined) {
|
|
101
|
+
if (!Array.isArray(node) || index >= node.length) return
|
|
102
|
+
node = node[index]
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!node || typeof node !== 'object') return
|
|
107
|
+
if (!Object.hasOwn(node, terminal.name) || typeof node[terminal.name] !== 'string') return
|
|
108
|
+
return { container: node, key: terminal.name }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Applies redaction replacements to an AI Guard message list.
|
|
113
|
+
* The caller transfers ownership of a private snapshot. Successful replacements mutate that snapshot in place.
|
|
114
|
+
*
|
|
115
|
+
* @param {Array<object>} messages
|
|
116
|
+
* @param {unknown} replacements
|
|
117
|
+
* @returns {{ messages: Array<object>, redacted: boolean, failures: number }}
|
|
118
|
+
*/
|
|
119
|
+
function redactMessages (messages, replacements) {
|
|
120
|
+
if (!Array.isArray(messages) || replacements === undefined || replacements === null) {
|
|
121
|
+
return { messages, redacted: false, failures: 0 }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
if (!Array.isArray(replacements)) {
|
|
126
|
+
return { messages, redacted: false, failures: 1 }
|
|
127
|
+
}
|
|
128
|
+
if (replacements.length === 0) return { messages, redacted: false, failures: 0 }
|
|
129
|
+
|
|
130
|
+
const replacementsByPath = new Map()
|
|
131
|
+
const conflictingPaths = new Set()
|
|
132
|
+
let failures = 0
|
|
133
|
+
for (const entry of replacements) {
|
|
134
|
+
if (!entry || typeof entry !== 'object') {
|
|
135
|
+
failures++
|
|
136
|
+
continue
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const { path, replacement } = entry
|
|
140
|
+
if (typeof path !== 'string' || path.length === 0 || typeof replacement !== 'string') {
|
|
141
|
+
failures++
|
|
142
|
+
continue
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (conflictingPaths.has(path)) continue
|
|
146
|
+
if (replacementsByPath.has(path) && replacementsByPath.get(path) !== replacement) {
|
|
147
|
+
replacementsByPath.delete(path)
|
|
148
|
+
conflictingPaths.add(path)
|
|
149
|
+
failures++
|
|
150
|
+
continue
|
|
151
|
+
}
|
|
152
|
+
replacementsByPath.set(path, replacement)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (replacementsByPath.size === 0) return { messages, redacted: false, failures }
|
|
156
|
+
|
|
157
|
+
const root = { messages }
|
|
158
|
+
const targets = []
|
|
159
|
+
|
|
160
|
+
for (const [path, replacement] of replacementsByPath) {
|
|
161
|
+
const resolved = resolveWritableString(root, path)
|
|
162
|
+
if (!resolved) {
|
|
163
|
+
failures++
|
|
164
|
+
continue
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
targets.push({ ...resolved, replacement })
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
for (const { container, key, replacement } of targets) {
|
|
171
|
+
container[key] = replacement
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return { messages, redacted: targets.length > 0, failures }
|
|
175
|
+
} catch {
|
|
176
|
+
return { messages, redacted: false, failures: 1 }
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
module.exports = {
|
|
181
|
+
normalizeRedactionReplacements,
|
|
182
|
+
redactMessages,
|
|
183
|
+
}
|
|
@@ -12,6 +12,7 @@ class AIGuard extends NoopAIGuard {
|
|
|
12
12
|
#tracer
|
|
13
13
|
#client
|
|
14
14
|
#reporter
|
|
15
|
+
#redactionEnabled
|
|
15
16
|
#meta
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -29,6 +30,7 @@ class AIGuard extends NoopAIGuard {
|
|
|
29
30
|
this.#tracer = tracer
|
|
30
31
|
this.#client = new AIGuardClient(config)
|
|
31
32
|
this.#reporter = new EvaluationReporter(config)
|
|
33
|
+
this.#redactionEnabled = config.experimental.aiguard.redactionEnabled
|
|
32
34
|
this.#meta = { service: config.service, env: config.env }
|
|
33
35
|
this.#initialized = true
|
|
34
36
|
}
|
|
@@ -45,13 +47,16 @@ class AIGuard extends NoopAIGuard {
|
|
|
45
47
|
const report = this.#reporter.start(span, messages, { source, integration })
|
|
46
48
|
let evaluation
|
|
47
49
|
try {
|
|
48
|
-
evaluation = await this.#client.evaluate(messages, this.#meta)
|
|
50
|
+
evaluation = await this.#client.evaluate(report.messages, this.#meta)
|
|
49
51
|
} catch (error) {
|
|
50
52
|
this.#reporter.fail(report, error.telemetryType ?? TAGS.ERROR_TYPE_CLIENT)
|
|
51
53
|
throw error
|
|
52
54
|
}
|
|
53
55
|
|
|
54
|
-
const outcome = createEvaluationOutcome(evaluation,
|
|
56
|
+
const outcome = createEvaluationOutcome(report.messages, evaluation, {
|
|
57
|
+
block,
|
|
58
|
+
redactionEnabled: this.#redactionEnabled,
|
|
59
|
+
})
|
|
55
60
|
this.#reporter.finish(report, outcome)
|
|
56
61
|
|
|
57
62
|
if (outcome.shouldBlock) {
|
|
@@ -7,6 +7,7 @@ module.exports = {
|
|
|
7
7
|
ACTION_TAG_KEY: 'ai_guard.action',
|
|
8
8
|
REASON_TAG_KEY: 'ai_guard.reason',
|
|
9
9
|
BLOCKED_TAG_KEY: 'ai_guard.blocked',
|
|
10
|
+
REDACTED_TAG_KEY: 'ai_guard.redacted',
|
|
10
11
|
EVENT_TAG_KEY: 'ai_guard.event',
|
|
11
12
|
META_STRUCT_KEY: 'ai_guard',
|
|
12
13
|
|
|
@@ -27,4 +28,5 @@ module.exports = {
|
|
|
27
28
|
ERROR_TYPE_CLIENT: 'client_error',
|
|
28
29
|
ERROR_TYPE_STATUS: 'bad_status',
|
|
29
30
|
ERROR_TYPE_RESPONSE: 'bad_response',
|
|
31
|
+
ERROR_TYPE_REDACTION: 'redaction_error',
|
|
30
32
|
}
|
|
@@ -108,7 +108,7 @@ function stringifyWithRanges (obj, objRanges, loadSensitiveRanges = false) {
|
|
|
108
108
|
value = JSON.stringify(cloneObj, null, 2)
|
|
109
109
|
|
|
110
110
|
if (counter > 0) {
|
|
111
|
-
|
|
111
|
+
let formattedValue = ''
|
|
112
112
|
let outputLength = 0
|
|
113
113
|
let pos = 0
|
|
114
114
|
let rangeKeyIndex = value.indexOf(STRINGIFY_RANGE_KEY)
|
|
@@ -133,13 +133,14 @@ function stringifyWithRanges (obj, objRanges, loadSensitiveRanges = false) {
|
|
|
133
133
|
start,
|
|
134
134
|
end: start + originalValue.length,
|
|
135
135
|
})
|
|
136
|
-
|
|
136
|
+
formattedValue += value.slice(pos, rangeKeyIndex)
|
|
137
|
+
formattedValue += originalValue
|
|
137
138
|
outputLength += cleanLength + originalValue.length
|
|
138
139
|
pos = rangeKeyIndex + matchValue.length
|
|
139
140
|
} else {
|
|
140
141
|
// can't happen, the only way to this to happen is
|
|
141
142
|
// if the JSON has a value starting with the value of STRINGIFY_SENSITIVE_NOT_STRING_KEY
|
|
142
|
-
|
|
143
|
+
formattedValue += value.slice(pos, rangeKeyIndex + STRINGIFY_SENSITIVE_NOT_STRING_KEY.length + 1)
|
|
143
144
|
outputLength += cleanLength + STRINGIFY_SENSITIVE_NOT_STRING_KEY.length + 1
|
|
144
145
|
pos = rangeKeyIndex + STRINGIFY_SENSITIVE_NOT_STRING_KEY.length + 1
|
|
145
146
|
}
|
|
@@ -152,13 +153,13 @@ function stringifyWithRanges (obj, objRanges, loadSensitiveRanges = false) {
|
|
|
152
153
|
start,
|
|
153
154
|
end: start + Number.parseInt(regexRes[1], 10),
|
|
154
155
|
})
|
|
155
|
-
|
|
156
|
+
formattedValue += value.slice(pos, rangeKeyIndex)
|
|
156
157
|
outputLength += cleanLength
|
|
157
158
|
pos = rangeKeyIndex + regexRes[0].length
|
|
158
159
|
} else {
|
|
159
160
|
// can't happen, the only way to this to happen is
|
|
160
161
|
// if the JSON has a value starting with the value of STRINGIFY_SENSITIVE_KEY
|
|
161
|
-
|
|
162
|
+
formattedValue += value.slice(pos, rangeKeyIndex + STRINGIFY_SENSITIVE_KEY.length)
|
|
162
163
|
outputLength += cleanLength + STRINGIFY_SENSITIVE_KEY.length
|
|
163
164
|
pos = rangeKeyIndex + STRINGIFY_SENSITIVE_KEY.length
|
|
164
165
|
}
|
|
@@ -175,13 +176,13 @@ function stringifyWithRanges (obj, objRanges, loadSensitiveRanges = false) {
|
|
|
175
176
|
}))
|
|
176
177
|
ranges.push(...updatedRanges)
|
|
177
178
|
|
|
178
|
-
|
|
179
|
+
formattedValue += value.slice(pos, rangeKeyIndex)
|
|
179
180
|
outputLength += cleanLength
|
|
180
181
|
pos = rangeKeyIndex + regexRes[0].length
|
|
181
182
|
} else {
|
|
182
183
|
// can't happen, the only way to this to happen is
|
|
183
184
|
// if the JSON has a value starting with the value of STRINGIFY_RANGE_KEY
|
|
184
|
-
|
|
185
|
+
formattedValue += value.slice(pos, rangeKeyIndex + STRINGIFY_RANGE_KEY.length)
|
|
185
186
|
outputLength += cleanLength + STRINGIFY_RANGE_KEY.length
|
|
186
187
|
pos = rangeKeyIndex + STRINGIFY_RANGE_KEY.length
|
|
187
188
|
}
|
|
@@ -190,8 +191,8 @@ function stringifyWithRanges (obj, objRanges, loadSensitiveRanges = false) {
|
|
|
190
191
|
rangeKeyIndex = value.indexOf(STRINGIFY_RANGE_KEY, pos)
|
|
191
192
|
}
|
|
192
193
|
|
|
193
|
-
|
|
194
|
-
value =
|
|
194
|
+
formattedValue += value.slice(pos)
|
|
195
|
+
value = formattedValue
|
|
195
196
|
}
|
|
196
197
|
} else {
|
|
197
198
|
value = JSON.stringify(obj, null, 2)
|
|
@@ -15,6 +15,7 @@ const {
|
|
|
15
15
|
incrementWafUpdatesMetric,
|
|
16
16
|
incrementWafConfigErrorsMetric,
|
|
17
17
|
incrementWafRequestsMetric,
|
|
18
|
+
incrementRequestDurationMetrics,
|
|
18
19
|
updateWafRequestsMetricTags,
|
|
19
20
|
updateRaspRequestsMetricTags,
|
|
20
21
|
updateRaspRuleSkippedMetricTags,
|
|
@@ -608,6 +609,7 @@ function finishRequest (req, res, storedResponseHeaders, requestBody, rootSpan)
|
|
|
608
609
|
|
|
609
610
|
if (!req) return
|
|
610
611
|
|
|
612
|
+
incrementRequestDurationMetrics(req)
|
|
611
613
|
incrementWafRequestsMetric(req)
|
|
612
614
|
|
|
613
615
|
const tags = rootSpan.context().getTags()
|
|
@@ -12,6 +12,7 @@ const {
|
|
|
12
12
|
trackRaspMetrics,
|
|
13
13
|
trackRaspRuleMatch,
|
|
14
14
|
trackRaspRuleSkipped,
|
|
15
|
+
incrementRaspDurationMetrics,
|
|
15
16
|
} = require('./rasp')
|
|
16
17
|
const {
|
|
17
18
|
addWafRequestMetrics,
|
|
@@ -20,6 +21,7 @@ const {
|
|
|
20
21
|
incrementWafUpdates,
|
|
21
22
|
incrementWafConfigErrors,
|
|
22
23
|
incrementWafRequests,
|
|
24
|
+
incrementWafDurationMetrics,
|
|
23
25
|
} = require('./waf')
|
|
24
26
|
|
|
25
27
|
const metricsStoreMap = new WeakMap()
|
|
@@ -142,6 +144,16 @@ function incrementWafRequestsMetric (req) {
|
|
|
142
144
|
metricsStoreMap.delete(req)
|
|
143
145
|
}
|
|
144
146
|
|
|
147
|
+
function incrementRequestDurationMetrics (req) {
|
|
148
|
+
if (!req || !enabled) return
|
|
149
|
+
|
|
150
|
+
const store = getStore(req)
|
|
151
|
+
const requestMetrics = store[DD_TELEMETRY_REQUEST_METRICS]
|
|
152
|
+
|
|
153
|
+
incrementWafDurationMetrics(requestMetrics)
|
|
154
|
+
incrementRaspDurationMetrics(requestMetrics)
|
|
155
|
+
}
|
|
156
|
+
|
|
145
157
|
function incrementMissingUserLoginMetric (framework, eventType) {
|
|
146
158
|
if (!enabled) return
|
|
147
159
|
|
|
@@ -199,6 +211,7 @@ module.exports = {
|
|
|
199
211
|
incrementWafUpdatesMetric,
|
|
200
212
|
incrementWafConfigErrorsMetric,
|
|
201
213
|
incrementWafRequestsMetric,
|
|
214
|
+
incrementRequestDurationMetrics,
|
|
202
215
|
incrementMissingUserLoginMetric,
|
|
203
216
|
incrementMissingUserIdMetric,
|
|
204
217
|
incrementSdkEventMetric,
|
|
@@ -77,6 +77,21 @@ function trackRaspRuleMatch (store, raspRule, blockTriggered, blocked) {
|
|
|
77
77
|
appsecMetrics.count('rasp.rule.match', tags).inc(1)
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
function incrementRaspDurationMetrics (requestMetrics) {
|
|
81
|
+
const { raspDuration, raspDurationExt, wafVersion, rulesVersion } = requestMetrics
|
|
82
|
+
if (!raspDuration && !raspDurationExt) return
|
|
83
|
+
|
|
84
|
+
const versionsTags = getVersionsTags(wafVersion, rulesVersion)
|
|
85
|
+
|
|
86
|
+
if (raspDuration) {
|
|
87
|
+
appsecMetrics.distribution('rasp.duration', versionsTags).track(raspDuration)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (raspDurationExt) {
|
|
91
|
+
appsecMetrics.distribution('rasp.duration_ext', versionsTags).track(raspDurationExt)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
80
95
|
function trackRaspRuleSkipped (raspRule, reason) {
|
|
81
96
|
const tags = { reason, rule_type: raspRule.type }
|
|
82
97
|
|
|
@@ -100,4 +115,5 @@ module.exports = {
|
|
|
100
115
|
trackRaspMetrics,
|
|
101
116
|
trackRaspRuleMatch,
|
|
102
117
|
trackRaspRuleSkipped,
|
|
118
|
+
incrementRaspDurationMetrics,
|
|
103
119
|
}
|
|
@@ -34,6 +34,14 @@ function addWafRequestMetrics (store, { duration, durationExt, wafTimeout, error
|
|
|
34
34
|
function trackWafMetrics (store, metrics) {
|
|
35
35
|
const versionsTags = getVersionsTags(metrics.wafVersion, metrics.rulesVersion)
|
|
36
36
|
|
|
37
|
+
const requestMetrics = store[DD_TELEMETRY_REQUEST_METRICS]
|
|
38
|
+
if (metrics.wafVersion) {
|
|
39
|
+
requestMetrics.wafVersion = metrics.wafVersion
|
|
40
|
+
}
|
|
41
|
+
if (metrics.rulesVersion) {
|
|
42
|
+
requestMetrics.rulesVersion = metrics.rulesVersion
|
|
43
|
+
}
|
|
44
|
+
|
|
37
45
|
const metricTags = getOrCreateMetricTags(store, versionsTags)
|
|
38
46
|
|
|
39
47
|
if (metrics.blockFailed) {
|
|
@@ -118,6 +126,21 @@ function incrementWafRequests (store) {
|
|
|
118
126
|
}
|
|
119
127
|
}
|
|
120
128
|
|
|
129
|
+
function incrementWafDurationMetrics (requestMetrics) {
|
|
130
|
+
const { duration, durationExt, wafVersion, rulesVersion } = requestMetrics
|
|
131
|
+
if (!duration && !durationExt) return
|
|
132
|
+
|
|
133
|
+
const versionsTags = getVersionsTags(wafVersion, rulesVersion)
|
|
134
|
+
|
|
135
|
+
if (duration) {
|
|
136
|
+
appsecMetrics.distribution('waf.duration', versionsTags).track(duration)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (durationExt) {
|
|
140
|
+
appsecMetrics.distribution('waf.duration_ext', versionsTags).track(durationExt)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
121
144
|
function incrementTruncatedMetrics (metrics, truncationReason) {
|
|
122
145
|
const truncationTags = { truncation_reason: truncationReason }
|
|
123
146
|
appsecMetrics.count('waf.input_truncated', truncationTags).inc(1)
|
|
@@ -140,4 +163,5 @@ module.exports = {
|
|
|
140
163
|
incrementWafUpdates,
|
|
141
164
|
incrementWafConfigErrors,
|
|
142
165
|
incrementWafRequests,
|
|
166
|
+
incrementWafDurationMetrics,
|
|
143
167
|
}
|
|
@@ -9,6 +9,23 @@ const FIELD_HAS = 4
|
|
|
9
9
|
const FIELD_DELETE = 8
|
|
10
10
|
const FIELD_NAME_LENGTH = 16
|
|
11
11
|
|
|
12
|
+
const b3FlagsHeader = 'x-b3-flags'
|
|
13
|
+
const b3Header = 'b3'
|
|
14
|
+
const b3SampledHeader = 'x-b3-sampled'
|
|
15
|
+
const b3SpanIdHeader = 'x-b3-spanid'
|
|
16
|
+
const b3TraceIdHeader = 'x-b3-traceid'
|
|
17
|
+
const baggageHeader = 'baggage'
|
|
18
|
+
const datadogOriginHeader = 'x-datadog-origin'
|
|
19
|
+
const datadogParentIdHeader = 'x-datadog-parent-id'
|
|
20
|
+
const datadogSamplingPriorityHeader = 'x-datadog-sampling-priority'
|
|
21
|
+
const datadogTagsHeader = 'x-datadog-tags'
|
|
22
|
+
const datadogTraceIdHeader = 'x-datadog-trace-id'
|
|
23
|
+
const dsmBase64Header = 'dd-pathway-ctx-base64'
|
|
24
|
+
const dsmBinaryHeader = 'dd-pathway-ctx'
|
|
25
|
+
const sqsdHeader = 'x-aws-sqsd-attr-_datadog'
|
|
26
|
+
const traceparentHeader = 'traceparent'
|
|
27
|
+
const tracestateHeader = 'tracestate'
|
|
28
|
+
|
|
12
29
|
/** @type {Record<string, unknown>} */
|
|
13
30
|
const carrierOperations = {}
|
|
14
31
|
|
|
@@ -24,7 +41,7 @@ const carrierOperations = {}
|
|
|
24
41
|
/**
|
|
25
42
|
* @template Value
|
|
26
43
|
* @typedef {object} CarrierField
|
|
27
|
-
* @property {(carrier: Carrier) => Value | undefined} read
|
|
44
|
+
* @property {((carrier: Carrier) => Value | undefined) | undefined} read
|
|
28
45
|
* @property {(carrier: Carrier, value: Value) => void} write
|
|
29
46
|
* @property {(carrier: Carrier) => boolean} has
|
|
30
47
|
* @property {(carrier: Carrier) => void} delete
|
|
@@ -53,19 +70,13 @@ function defineCapability (name, operations, capability) {
|
|
|
53
70
|
* @template Value
|
|
54
71
|
* @param {string} fieldName
|
|
55
72
|
* @param {string} headerName
|
|
56
|
-
* @param {(
|
|
73
|
+
* @param {((carrier: Carrier) => Value | undefined) | undefined} read
|
|
57
74
|
* @param {number} operations
|
|
58
75
|
* @returns {CarrierField<Value>}
|
|
59
76
|
*/
|
|
60
|
-
function defineField (fieldName, headerName,
|
|
77
|
+
function defineField (fieldName, headerName, read, operations) {
|
|
61
78
|
return defineCapability(fieldName, operations, {
|
|
62
|
-
|
|
63
|
-
* @param {Carrier} carrier
|
|
64
|
-
* @returns {Value | undefined}
|
|
65
|
-
*/
|
|
66
|
-
read (carrier) {
|
|
67
|
-
return resolve(carrier[headerName])
|
|
68
|
-
},
|
|
79
|
+
read,
|
|
69
80
|
|
|
70
81
|
/**
|
|
71
82
|
* @param {Carrier} carrier
|
|
@@ -183,48 +194,96 @@ function readLastPathway (value) {
|
|
|
183
194
|
return result
|
|
184
195
|
}
|
|
185
196
|
|
|
197
|
+
/** @param {Carrier} carrier */
|
|
198
|
+
function readB3Flags (carrier) { return readLastText(carrier[b3FlagsHeader]) }
|
|
199
|
+
|
|
200
|
+
/** @param {Carrier} carrier */
|
|
201
|
+
function readB3 (carrier) { return readLastText(carrier[b3Header]) }
|
|
202
|
+
|
|
203
|
+
/** @param {Carrier} carrier */
|
|
204
|
+
function readB3Sampled (carrier) { return readLastText(carrier[b3SampledHeader]) }
|
|
205
|
+
|
|
206
|
+
/** @param {Carrier} carrier */
|
|
207
|
+
function readB3SpanId (carrier) { return readLastText(carrier[b3SpanIdHeader]) }
|
|
208
|
+
|
|
209
|
+
/** @param {Carrier} carrier */
|
|
210
|
+
function readB3TraceId (carrier) { return readLastText(carrier[b3TraceIdHeader]) }
|
|
211
|
+
|
|
212
|
+
/** @param {Carrier} carrier */
|
|
213
|
+
function readBaggage (carrier) { return readListText(carrier[baggageHeader]) }
|
|
214
|
+
|
|
215
|
+
/** @param {Carrier} carrier */
|
|
216
|
+
function readDatadogOrigin (carrier) { return readLastText(carrier[datadogOriginHeader]) }
|
|
217
|
+
|
|
218
|
+
/** @param {Carrier} carrier */
|
|
219
|
+
function readDatadogParentId (carrier) { return readLastText(carrier[datadogParentIdHeader]) }
|
|
220
|
+
|
|
221
|
+
/** @param {Carrier} carrier */
|
|
222
|
+
function readDatadogSamplingPriority (carrier) { return readLastText(carrier[datadogSamplingPriorityHeader]) }
|
|
223
|
+
|
|
224
|
+
/** @param {Carrier} carrier */
|
|
225
|
+
function readDatadogTags (carrier) { return readListText(carrier[datadogTagsHeader]) }
|
|
226
|
+
|
|
227
|
+
/** @param {Carrier} carrier */
|
|
228
|
+
function readDatadogTraceId (carrier) { return readLastText(carrier[datadogTraceIdHeader]) }
|
|
229
|
+
|
|
230
|
+
/** @param {Carrier} carrier */
|
|
231
|
+
function readDsmBase64 (carrier) { return readLastPathway(carrier[dsmBase64Header]) }
|
|
232
|
+
|
|
233
|
+
/** @param {Carrier} carrier */
|
|
234
|
+
function readDsmBinary (carrier) { return readLastPathway(carrier[dsmBinaryHeader]) }
|
|
235
|
+
|
|
236
|
+
/** @param {Carrier} carrier */
|
|
237
|
+
function readSqsd (carrier) { return readLastText(carrier[sqsdHeader]) }
|
|
238
|
+
|
|
239
|
+
/** @param {Carrier} carrier */
|
|
240
|
+
function readTraceparent (carrier) { return readUniqueText(carrier[traceparentHeader]) }
|
|
241
|
+
|
|
242
|
+
/** @param {Carrier} carrier */
|
|
243
|
+
function readTracestate (carrier) { return readListText(carrier[tracestateHeader]) }
|
|
244
|
+
|
|
186
245
|
const datadogTraceId = defineField(
|
|
187
246
|
'datadogTraceId',
|
|
188
|
-
|
|
189
|
-
|
|
247
|
+
datadogTraceIdHeader,
|
|
248
|
+
readDatadogTraceId,
|
|
190
249
|
FIELD_READ | FIELD_WRITE
|
|
191
250
|
)
|
|
192
251
|
const datadogParentId = defineField(
|
|
193
252
|
'datadogParentId',
|
|
194
|
-
|
|
195
|
-
|
|
253
|
+
datadogParentIdHeader,
|
|
254
|
+
readDatadogParentId,
|
|
196
255
|
FIELD_READ | FIELD_WRITE | FIELD_DELETE
|
|
197
256
|
)
|
|
198
257
|
const datadogOrigin = defineField(
|
|
199
258
|
'datadogOrigin',
|
|
200
|
-
|
|
201
|
-
|
|
259
|
+
datadogOriginHeader,
|
|
260
|
+
readDatadogOrigin,
|
|
202
261
|
FIELD_READ | FIELD_WRITE
|
|
203
262
|
)
|
|
204
263
|
const datadogSamplingPriority = defineField(
|
|
205
264
|
'datadogSamplingPriority',
|
|
206
|
-
|
|
207
|
-
|
|
265
|
+
datadogSamplingPriorityHeader,
|
|
266
|
+
readDatadogSamplingPriority,
|
|
208
267
|
FIELD_READ | FIELD_WRITE
|
|
209
268
|
)
|
|
210
|
-
defineField('datadogTags',
|
|
211
|
-
defineField('baggage',
|
|
212
|
-
const b3TraceId = defineField('b3TraceId',
|
|
213
|
-
const b3SpanId = defineField('b3SpanId',
|
|
214
|
-
const b3ParentId = defineField('b3ParentId', 'x-b3-parentspanid',
|
|
215
|
-
const b3Sampled = defineField('b3Sampled',
|
|
216
|
-
const b3Flags = defineField('b3Flags',
|
|
217
|
-
const b3 = defineField('b3',
|
|
218
|
-
defineField('sqsd',
|
|
219
|
-
const traceparent = defineField('traceparent',
|
|
220
|
-
const tracestate = defineField('tracestate',
|
|
269
|
+
defineField('datadogTags', datadogTagsHeader, readDatadogTags, FIELD_READ | FIELD_WRITE)
|
|
270
|
+
defineField('baggage', baggageHeader, readBaggage, FIELD_READ | FIELD_WRITE)
|
|
271
|
+
const b3TraceId = defineField('b3TraceId', b3TraceIdHeader, readB3TraceId, FIELD_READ | FIELD_WRITE)
|
|
272
|
+
const b3SpanId = defineField('b3SpanId', b3SpanIdHeader, readB3SpanId, FIELD_READ | FIELD_WRITE)
|
|
273
|
+
const b3ParentId = defineField('b3ParentId', 'x-b3-parentspanid', undefined, FIELD_WRITE)
|
|
274
|
+
const b3Sampled = defineField('b3Sampled', b3SampledHeader, readB3Sampled, FIELD_READ | FIELD_WRITE)
|
|
275
|
+
const b3Flags = defineField('b3Flags', b3FlagsHeader, readB3Flags, FIELD_READ | FIELD_WRITE)
|
|
276
|
+
const b3 = defineField('b3', b3Header, readB3, FIELD_READ | FIELD_WRITE)
|
|
277
|
+
defineField('sqsd', sqsdHeader, readSqsd, FIELD_READ)
|
|
278
|
+
const traceparent = defineField('traceparent', traceparentHeader, readTraceparent, FIELD_READ | FIELD_WRITE)
|
|
279
|
+
const tracestate = defineField('tracestate', tracestateHeader, readTracestate, FIELD_READ | FIELD_WRITE)
|
|
221
280
|
const dsmBase64 = defineField(
|
|
222
281
|
'dsmBase64',
|
|
223
|
-
|
|
224
|
-
|
|
282
|
+
dsmBase64Header,
|
|
283
|
+
readDsmBase64,
|
|
225
284
|
FIELD_READ | FIELD_WRITE | FIELD_HAS | FIELD_NAME_LENGTH
|
|
226
285
|
)
|
|
227
|
-
const dsmBinary = defineField('dsmBinary',
|
|
286
|
+
const dsmBinary = defineField('dsmBinary', dsmBinaryHeader, readDsmBinary, FIELD_READ | FIELD_HAS)
|
|
228
287
|
|
|
229
288
|
const legacyBaggagePrefix = 'ot-baggage-'
|
|
230
289
|
// RFC 7230 `token`, compatible with Node.js header-name validation.
|
|
@@ -314,23 +373,23 @@ function pickDsm (carrier) {
|
|
|
314
373
|
* @property {typeof dsmBinary.has} hasDsmBinary
|
|
315
374
|
* @property {typeof pickDsm} pickDsm
|
|
316
375
|
* @property {typeof pickTextMap} pickTextMap
|
|
317
|
-
* @property {
|
|
318
|
-
* @property {typeof
|
|
319
|
-
* @property {typeof
|
|
320
|
-
* @property {typeof
|
|
321
|
-
* @property {typeof
|
|
322
|
-
* @property {typeof
|
|
323
|
-
* @property {typeof
|
|
324
|
-
* @property {typeof
|
|
325
|
-
* @property {typeof
|
|
326
|
-
* @property {
|
|
327
|
-
* @property {typeof
|
|
328
|
-
* @property {typeof
|
|
329
|
-
* @property {typeof
|
|
376
|
+
* @property {typeof readBaggage} readBaggage
|
|
377
|
+
* @property {typeof readB3} readB3
|
|
378
|
+
* @property {typeof readB3Flags} readB3Flags
|
|
379
|
+
* @property {typeof readB3Sampled} readB3Sampled
|
|
380
|
+
* @property {typeof readB3SpanId} readB3SpanId
|
|
381
|
+
* @property {typeof readB3TraceId} readB3TraceId
|
|
382
|
+
* @property {typeof readDatadogOrigin} readDatadogOrigin
|
|
383
|
+
* @property {typeof readDatadogParentId} readDatadogParentId
|
|
384
|
+
* @property {typeof readDatadogSamplingPriority} readDatadogSamplingPriority
|
|
385
|
+
* @property {typeof readDatadogTags} readDatadogTags
|
|
386
|
+
* @property {typeof readDatadogTraceId} readDatadogTraceId
|
|
387
|
+
* @property {typeof readDsmBase64} readDsmBase64
|
|
388
|
+
* @property {typeof readDsmBinary} readDsmBinary
|
|
330
389
|
* @property {(carrier: Carrier, target: Record<string, string>) => void} readLegacyBaggage
|
|
331
|
-
* @property {
|
|
332
|
-
* @property {typeof
|
|
333
|
-
* @property {typeof
|
|
390
|
+
* @property {typeof readSqsd} readSqsd
|
|
391
|
+
* @property {typeof readTraceparent} readTraceparent
|
|
392
|
+
* @property {typeof readTracestate} readTracestate
|
|
334
393
|
* @property {(carrier: Carrier, value: string) => void} writeBaggage
|
|
335
394
|
* @property {typeof b3.write} writeB3
|
|
336
395
|
* @property {typeof b3Flags.write} writeB3Flags
|