dd-trace 5.6.0 → 5.7.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/index.d.ts +4 -0
- package/package.json +2 -2
- package/packages/datadog-plugin-cucumber/src/index.js +2 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +2 -2
- package/packages/datadog-plugin-jest/src/index.js +2 -2
- package/packages/datadog-plugin-mocha/src/index.js +2 -2
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +23 -12
- package/packages/dd-trace/src/appsec/iast/path-line.js +9 -6
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +6 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-telemetry.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +6 -7
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +1 -1
- package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +34 -27
- package/packages/dd-trace/src/appsec/iast/telemetry/namespaces.js +39 -11
- package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +7 -6
- package/packages/dd-trace/src/appsec/reporter.js +24 -11
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +6 -1
- package/packages/dd-trace/src/lambda/runtime/ritm.js +1 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +21 -2
- package/packages/dd-trace/src/opentracing/span.js +2 -0
- package/packages/dd-trace/src/opentracing/span_context.js +1 -0
- package/packages/dd-trace/src/plugins/util/test.js +2 -2
- package/packages/dd-trace/src/priority_sampler.js +11 -6
- package/packages/dd-trace/src/telemetry/index.js +15 -4
- package/CONTRIBUTING.md +0 -171
- package/MIGRATING.md +0 -224
- package/packages/dd-trace/src/external-logger/test/index.spec.js +0 -147
- package/scripts/check-proposal-labels.js +0 -71
- package/scripts/check_licenses.js +0 -69
- package/scripts/helpers/color.js +0 -8
- package/scripts/helpers/exec.js +0 -22
- package/scripts/helpers/title.js +0 -15
- package/scripts/install_plugin_modules.js +0 -248
- package/scripts/publish_docs.js +0 -21
- package/scripts/st.js +0 -105
package/index.d.ts
CHANGED
|
@@ -1624,6 +1624,10 @@ declare namespace tracer {
|
|
|
1624
1624
|
* The service name to be used for this plugin. If a function is used, it will be passed the connection parameters and its return value will be used as the service name.
|
|
1625
1625
|
*/
|
|
1626
1626
|
service?: string | ((params: any) => string);
|
|
1627
|
+
/**
|
|
1628
|
+
* The database monitoring propagation mode to be used for this plugin.
|
|
1629
|
+
*/
|
|
1630
|
+
dbmPropagationMode?: string;
|
|
1627
1631
|
}
|
|
1628
1632
|
|
|
1629
1633
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.7.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
72
|
"@datadog/native-appsec": "7.1.0",
|
|
73
|
-
"@datadog/native-iast-rewriter": "2.
|
|
73
|
+
"@datadog/native-iast-rewriter": "2.3.0",
|
|
74
74
|
"@datadog/native-iast-taint-tracking": "1.7.0",
|
|
75
75
|
"@datadog/native-metrics": "^2.0.0",
|
|
76
76
|
"@datadog/pprof": "5.1.0",
|
|
@@ -18,7 +18,7 @@ const {
|
|
|
18
18
|
TEST_SOURCE_FILE,
|
|
19
19
|
TEST_EARLY_FLAKE_IS_ENABLED,
|
|
20
20
|
TEST_IS_NEW,
|
|
21
|
-
|
|
21
|
+
TEST_IS_RETRY
|
|
22
22
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
23
23
|
const { RESOURCE_NAME } = require('../../../ext/tags')
|
|
24
24
|
const { COMPONENT, ERROR_MESSAGE } = require('../../dd-trace/src/constants')
|
|
@@ -181,7 +181,7 @@ class CucumberPlugin extends CiPlugin {
|
|
|
181
181
|
if (isNew) {
|
|
182
182
|
span.setTag(TEST_IS_NEW, 'true')
|
|
183
183
|
if (isEfdRetry) {
|
|
184
|
-
span.setTag(
|
|
184
|
+
span.setTag(TEST_IS_RETRY, 'true')
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
187
|
|
|
@@ -27,7 +27,7 @@ const {
|
|
|
27
27
|
ITR_CORRELATION_ID,
|
|
28
28
|
TEST_SOURCE_FILE,
|
|
29
29
|
TEST_IS_NEW,
|
|
30
|
-
|
|
30
|
+
TEST_IS_RETRY,
|
|
31
31
|
TEST_EARLY_FLAKE_IS_ENABLED
|
|
32
32
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
33
33
|
const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util')
|
|
@@ -592,7 +592,7 @@ class CypressPlugin {
|
|
|
592
592
|
if (isNew) {
|
|
593
593
|
this.activeTestSpan.setTag(TEST_IS_NEW, 'true')
|
|
594
594
|
if (isEfdRetry) {
|
|
595
|
-
this.activeTestSpan.setTag(
|
|
595
|
+
this.activeTestSpan.setTag(TEST_IS_RETRY, 'true')
|
|
596
596
|
}
|
|
597
597
|
}
|
|
598
598
|
const finishedTest = {
|
|
@@ -17,7 +17,7 @@ const {
|
|
|
17
17
|
ITR_CORRELATION_ID,
|
|
18
18
|
TEST_SOURCE_FILE,
|
|
19
19
|
TEST_IS_NEW,
|
|
20
|
-
|
|
20
|
+
TEST_IS_RETRY,
|
|
21
21
|
TEST_EARLY_FLAKE_IS_ENABLED,
|
|
22
22
|
JEST_DISPLAY_NAME
|
|
23
23
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
@@ -339,7 +339,7 @@ class JestPlugin extends CiPlugin {
|
|
|
339
339
|
if (isNew) {
|
|
340
340
|
extraTags[TEST_IS_NEW] = 'true'
|
|
341
341
|
if (isEfdRetry) {
|
|
342
|
-
extraTags[
|
|
342
|
+
extraTags[TEST_IS_RETRY] = 'true'
|
|
343
343
|
}
|
|
344
344
|
}
|
|
345
345
|
|
|
@@ -19,7 +19,7 @@ const {
|
|
|
19
19
|
TEST_SOURCE_FILE,
|
|
20
20
|
removeEfdStringFromTestName,
|
|
21
21
|
TEST_IS_NEW,
|
|
22
|
-
|
|
22
|
+
TEST_IS_RETRY,
|
|
23
23
|
TEST_EARLY_FLAKE_IS_ENABLED
|
|
24
24
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
25
25
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
@@ -270,7 +270,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
270
270
|
if (isNew) {
|
|
271
271
|
extraTags[TEST_IS_NEW] = 'true'
|
|
272
272
|
if (isEfdRetry) {
|
|
273
|
-
extraTags[
|
|
273
|
+
extraTags[TEST_IS_RETRY] = 'true'
|
|
274
274
|
}
|
|
275
275
|
}
|
|
276
276
|
|
|
@@ -5,7 +5,8 @@ const { channel } = require('dc-polyfill')
|
|
|
5
5
|
const iastLog = require('./iast-log')
|
|
6
6
|
const Plugin = require('../../plugins/plugin')
|
|
7
7
|
const iastTelemetry = require('./telemetry')
|
|
8
|
-
const { getInstrumentedMetric, getExecutedMetric, TagKey, EXECUTED_SOURCE } =
|
|
8
|
+
const { getInstrumentedMetric, getExecutedMetric, TagKey, EXECUTED_SOURCE, formatTags } =
|
|
9
|
+
require('./telemetry/iast-metric')
|
|
9
10
|
const { storage } = require('../../../../datadog-core')
|
|
10
11
|
const { getIastContext } = require('./iast-context')
|
|
11
12
|
const instrumentations = require('../../../../datadog-instrumentations/src/helpers/instrumentations')
|
|
@@ -20,25 +21,29 @@ const instrumentations = require('../../../../datadog-instrumentations/src/helpe
|
|
|
20
21
|
* - tagKey can be only SOURCE_TYPE (Source) or VULNERABILITY_TYPE (Sink)
|
|
21
22
|
*/
|
|
22
23
|
class IastPluginSubscription {
|
|
23
|
-
constructor (moduleName, channelName,
|
|
24
|
+
constructor (moduleName, channelName, tagValues, tagKey = TagKey.VULNERABILITY_TYPE) {
|
|
24
25
|
this.moduleName = moduleName
|
|
25
26
|
this.channelName = channelName
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
this.
|
|
29
|
-
|
|
27
|
+
|
|
28
|
+
tagValues = Array.isArray(tagValues) ? tagValues : [tagValues]
|
|
29
|
+
this.tags = formatTags(tagValues, tagKey)
|
|
30
|
+
|
|
31
|
+
this.executedMetric = getExecutedMetric(tagKey)
|
|
32
|
+
this.instrumentedMetric = getInstrumentedMetric(tagKey)
|
|
33
|
+
|
|
30
34
|
this.moduleInstrumented = false
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
increaseInstrumented () {
|
|
34
|
-
if (this.moduleInstrumented)
|
|
38
|
+
if (!this.moduleInstrumented) {
|
|
39
|
+
this.moduleInstrumented = true
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
this.tags.forEach(tag => this.instrumentedMetric.inc(undefined, tag))
|
|
42
|
+
}
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
increaseExecuted (iastContext) {
|
|
41
|
-
this.executedMetric.inc(
|
|
46
|
+
this.tags.forEach(tag => this.executedMetric.inc(iastContext, tag))
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
matchesModuleInstrumented (name) {
|
|
@@ -76,10 +81,16 @@ class IastPlugin extends Plugin {
|
|
|
76
81
|
}
|
|
77
82
|
}
|
|
78
83
|
|
|
79
|
-
_execHandlerAndIncMetric ({ handler, metric,
|
|
84
|
+
_execHandlerAndIncMetric ({ handler, metric, tags, iastContext = getIastContext(storage.getStore()) }) {
|
|
80
85
|
try {
|
|
81
86
|
const result = handler()
|
|
82
|
-
iastTelemetry.isEnabled()
|
|
87
|
+
if (iastTelemetry.isEnabled()) {
|
|
88
|
+
if (Array.isArray(tags)) {
|
|
89
|
+
tags.forEach(tag => metric.inc(iastContext, tag))
|
|
90
|
+
} else {
|
|
91
|
+
metric.inc(iastContext, tags)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
83
94
|
return result
|
|
84
95
|
} catch (e) {
|
|
85
96
|
iastLog.errorAndPublish(e)
|
|
@@ -29,13 +29,16 @@ function getCallSiteInfo () {
|
|
|
29
29
|
const previousStackTraceLimit = Error.stackTraceLimit
|
|
30
30
|
let callsiteList
|
|
31
31
|
Error.stackTraceLimit = 100
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
try {
|
|
33
|
+
Error.prepareStackTrace = function (_, callsites) {
|
|
34
|
+
callsiteList = callsites
|
|
35
|
+
}
|
|
36
|
+
const e = new Error()
|
|
37
|
+
e.stack
|
|
38
|
+
} finally {
|
|
39
|
+
Error.prepareStackTrace = previousPrepareStackTrace
|
|
40
|
+
Error.stackTraceLimit = previousStackTraceLimit
|
|
34
41
|
}
|
|
35
|
-
const e = new Error()
|
|
36
|
-
e.stack
|
|
37
|
-
Error.prepareStackTrace = previousPrepareStackTrace
|
|
38
|
-
Error.stackTraceLimit = previousStackTraceLimit
|
|
39
42
|
return callsiteList
|
|
40
43
|
}
|
|
41
44
|
|
|
@@ -19,7 +19,7 @@ let onRemoveTransaction = (transactionId, iastContext) => {}
|
|
|
19
19
|
function onRemoveTransactionInformationTelemetry (transactionId, iastContext) {
|
|
20
20
|
const metrics = TaintedUtils.getMetrics(transactionId, iastTelemetry.verbosity)
|
|
21
21
|
if (metrics?.requestCount) {
|
|
22
|
-
REQUEST_TAINTED.
|
|
22
|
+
REQUEST_TAINTED.inc(iastContext, metrics.requestCount)
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -14,6 +14,10 @@ const {
|
|
|
14
14
|
HTTP_REQUEST_PATH_PARAM,
|
|
15
15
|
HTTP_REQUEST_URI
|
|
16
16
|
} = require('./source-types')
|
|
17
|
+
const { EXECUTED_SOURCE } = require('../telemetry/iast-metric')
|
|
18
|
+
|
|
19
|
+
const REQ_HEADER_TAGS = EXECUTED_SOURCE.formatTags(HTTP_REQUEST_HEADER_VALUE, HTTP_REQUEST_HEADER_NAME)
|
|
20
|
+
const REQ_URI_TAGS = EXECUTED_SOURCE.formatTags(HTTP_REQUEST_URI)
|
|
17
21
|
|
|
18
22
|
class TaintTrackingPlugin extends SourceIastPlugin {
|
|
19
23
|
constructor () {
|
|
@@ -97,7 +101,7 @@ class TaintTrackingPlugin extends SourceIastPlugin {
|
|
|
97
101
|
taintHeaders (headers, iastContext) {
|
|
98
102
|
this.execSource({
|
|
99
103
|
handler: () => taintObject(iastContext, headers, HTTP_REQUEST_HEADER_VALUE, true, HTTP_REQUEST_HEADER_NAME),
|
|
100
|
-
|
|
104
|
+
tags: REQ_HEADER_TAGS,
|
|
101
105
|
iastContext
|
|
102
106
|
})
|
|
103
107
|
}
|
|
@@ -107,7 +111,7 @@ class TaintTrackingPlugin extends SourceIastPlugin {
|
|
|
107
111
|
handler: function () {
|
|
108
112
|
req.url = newTaintedString(iastContext, req.url, HTTP_REQUEST_URI, HTTP_REQUEST_URI)
|
|
109
113
|
},
|
|
110
|
-
|
|
114
|
+
tags: REQ_URI_TAGS,
|
|
111
115
|
iastContext
|
|
112
116
|
})
|
|
113
117
|
}
|
|
@@ -14,7 +14,7 @@ const telemetryRewriter = {
|
|
|
14
14
|
|
|
15
15
|
const metrics = response.metrics
|
|
16
16
|
if (metrics && metrics.instrumentedPropagation) {
|
|
17
|
-
INSTRUMENTED_PROPAGATION.
|
|
17
|
+
INSTRUMENTED_PROPAGATION.inc(undefined, metrics.instrumentedPropagation)
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
return response
|
|
@@ -12,6 +12,7 @@ const dc = require('dc-polyfill')
|
|
|
12
12
|
const hardcodedSecretCh = dc.channel('datadog:secrets:result')
|
|
13
13
|
let rewriter
|
|
14
14
|
let getPrepareStackTrace
|
|
15
|
+
let kSymbolPrepareStackTrace
|
|
15
16
|
|
|
16
17
|
let getRewriterOriginalPathAndLineFromSourceMap = function (path, line, column) {
|
|
17
18
|
return { path, line, column }
|
|
@@ -44,6 +45,7 @@ function getRewriter (telemetryVerbosity) {
|
|
|
44
45
|
const iastRewriter = require('@datadog/native-iast-rewriter')
|
|
45
46
|
const Rewriter = iastRewriter.Rewriter
|
|
46
47
|
getPrepareStackTrace = iastRewriter.getPrepareStackTrace
|
|
48
|
+
kSymbolPrepareStackTrace = iastRewriter.kSymbolPrepareStackTrace
|
|
47
49
|
|
|
48
50
|
const chainSourceMap = isFlagPresent('--enable-source-maps')
|
|
49
51
|
const getOriginalPathAndLineFromSourceMap = iastRewriter.getOriginalPathAndLineFromSourceMap
|
|
@@ -66,17 +68,16 @@ function getRewriter (telemetryVerbosity) {
|
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
let originalPrepareStackTrace
|
|
69
|
-
let actualPrepareStackTrace
|
|
70
71
|
function getPrepareStackTraceAccessor () {
|
|
71
72
|
originalPrepareStackTrace = Error.prepareStackTrace
|
|
72
|
-
|
|
73
|
+
let actual = getPrepareStackTrace(originalPrepareStackTrace)
|
|
73
74
|
return {
|
|
74
75
|
configurable: true,
|
|
75
76
|
get () {
|
|
76
|
-
return
|
|
77
|
+
return actual
|
|
77
78
|
},
|
|
78
79
|
set (value) {
|
|
79
|
-
|
|
80
|
+
actual = getPrepareStackTrace(value)
|
|
80
81
|
originalPrepareStackTrace = value
|
|
81
82
|
}
|
|
82
83
|
}
|
|
@@ -124,14 +125,12 @@ function enableRewriter (telemetryVerbosity) {
|
|
|
124
125
|
function disableRewriter () {
|
|
125
126
|
shimmer.unwrap(Module.prototype, '_compile')
|
|
126
127
|
|
|
127
|
-
if (!
|
|
128
|
+
if (!Error.prepareStackTrace?.[kSymbolPrepareStackTrace]) return
|
|
128
129
|
|
|
129
130
|
try {
|
|
130
131
|
delete Error.prepareStackTrace
|
|
131
132
|
|
|
132
133
|
Error.prepareStackTrace = originalPrepareStackTrace
|
|
133
|
-
|
|
134
|
-
actualPrepareStackTrace = undefined
|
|
135
134
|
} catch (e) {
|
|
136
135
|
iastLog.warn(e)
|
|
137
136
|
}
|
|
@@ -19,6 +19,19 @@ const TagKey = {
|
|
|
19
19
|
PROPAGATION_TYPE: 'propagation_type'
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
function formatTags (tags, tagKey) {
|
|
23
|
+
return tags.map(tagValue => tagValue ? [`${tagKey}:${tagValue.toLowerCase()}`] : undefined)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getNamespace (scope, context) {
|
|
27
|
+
let namespace = globalNamespace
|
|
28
|
+
|
|
29
|
+
if (scope === Scope.REQUEST) {
|
|
30
|
+
namespace = getNamespaceFromContext(context) || globalNamespace
|
|
31
|
+
}
|
|
32
|
+
return namespace
|
|
33
|
+
}
|
|
34
|
+
|
|
22
35
|
class IastMetric {
|
|
23
36
|
constructor (name, scope, tagKey) {
|
|
24
37
|
this.name = name
|
|
@@ -26,30 +39,26 @@ class IastMetric {
|
|
|
26
39
|
this.tagKey = tagKey
|
|
27
40
|
}
|
|
28
41
|
|
|
29
|
-
|
|
30
|
-
return
|
|
42
|
+
formatTags (...tags) {
|
|
43
|
+
return formatTags(tags, this.tagKey)
|
|
31
44
|
}
|
|
32
45
|
|
|
33
|
-
|
|
34
|
-
|
|
46
|
+
inc (context, tags, value = 1) {
|
|
47
|
+
const namespace = getNamespace(this.scope, context)
|
|
48
|
+
namespace.count(this.name, tags).inc(value)
|
|
35
49
|
}
|
|
50
|
+
}
|
|
36
51
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
.inc(value)
|
|
41
|
-
}
|
|
52
|
+
class NoTaggedIastMetric extends IastMetric {
|
|
53
|
+
constructor (name, scope) {
|
|
54
|
+
super(name, scope)
|
|
42
55
|
|
|
43
|
-
|
|
44
|
-
if (Array.isArray(tagValue)) {
|
|
45
|
-
tagValue.forEach(tag => this.addValue(value, tag, context))
|
|
46
|
-
} else {
|
|
47
|
-
this.addValue(value, tagValue, context)
|
|
48
|
-
}
|
|
56
|
+
this.tags = []
|
|
49
57
|
}
|
|
50
58
|
|
|
51
|
-
inc (
|
|
52
|
-
this.
|
|
59
|
+
inc (context, value = 1) {
|
|
60
|
+
const namespace = getNamespace(this.scope, context)
|
|
61
|
+
namespace.count(this.name, this.tags).inc(value)
|
|
53
62
|
}
|
|
54
63
|
}
|
|
55
64
|
|
|
@@ -61,21 +70,18 @@ function getInstrumentedMetric (tagKey) {
|
|
|
61
70
|
return tagKey === TagKey.VULNERABILITY_TYPE ? INSTRUMENTED_SINK : INSTRUMENTED_SOURCE
|
|
62
71
|
}
|
|
63
72
|
|
|
64
|
-
const INSTRUMENTED_PROPAGATION = new
|
|
73
|
+
const INSTRUMENTED_PROPAGATION = new NoTaggedIastMetric('instrumented.propagation', Scope.GLOBAL)
|
|
65
74
|
const INSTRUMENTED_SOURCE = new IastMetric('instrumented.source', Scope.GLOBAL, TagKey.SOURCE_TYPE)
|
|
66
75
|
const INSTRUMENTED_SINK = new IastMetric('instrumented.sink', Scope.GLOBAL, TagKey.VULNERABILITY_TYPE)
|
|
67
76
|
|
|
68
77
|
const EXECUTED_SOURCE = new IastMetric('executed.source', Scope.REQUEST, TagKey.SOURCE_TYPE)
|
|
69
78
|
const EXECUTED_SINK = new IastMetric('executed.sink', Scope.REQUEST, TagKey.VULNERABILITY_TYPE)
|
|
70
79
|
|
|
71
|
-
const REQUEST_TAINTED = new
|
|
80
|
+
const REQUEST_TAINTED = new NoTaggedIastMetric('request.tainted', Scope.REQUEST)
|
|
72
81
|
|
|
73
82
|
// DEBUG using metrics
|
|
74
|
-
const EXECUTED_PROPAGATION = new
|
|
75
|
-
const EXECUTED_TAINTED = new
|
|
76
|
-
|
|
77
|
-
// DEBUG using distribution endpoint
|
|
78
|
-
const INSTRUMENTATION_TIME = new IastMetric('instrumentation.time', Scope.GLOBAL)
|
|
83
|
+
const EXECUTED_PROPAGATION = new NoTaggedIastMetric('executed.propagation', Scope.REQUEST)
|
|
84
|
+
const EXECUTED_TAINTED = new NoTaggedIastMetric('executed.tainted', Scope.REQUEST)
|
|
79
85
|
|
|
80
86
|
module.exports = {
|
|
81
87
|
INSTRUMENTED_PROPAGATION,
|
|
@@ -89,13 +95,14 @@ module.exports = {
|
|
|
89
95
|
|
|
90
96
|
REQUEST_TAINTED,
|
|
91
97
|
|
|
92
|
-
INSTRUMENTATION_TIME,
|
|
93
|
-
|
|
94
98
|
PropagationType,
|
|
95
99
|
TagKey,
|
|
96
100
|
|
|
97
101
|
IastMetric,
|
|
102
|
+
NoTaggedIastMetric,
|
|
98
103
|
|
|
99
104
|
getExecutedMetric,
|
|
100
|
-
getInstrumentedMetric
|
|
105
|
+
getInstrumentedMetric,
|
|
106
|
+
|
|
107
|
+
formatTags
|
|
101
108
|
}
|
|
@@ -10,13 +10,13 @@ const DD_IAST_METRICS_NAMESPACE = Symbol('_dd.iast.request.metrics.namespace')
|
|
|
10
10
|
function initRequestNamespace (context) {
|
|
11
11
|
if (!context) return
|
|
12
12
|
|
|
13
|
-
const namespace = new
|
|
13
|
+
const namespace = new IastNamespace()
|
|
14
14
|
context[DD_IAST_METRICS_NAMESPACE] = namespace
|
|
15
15
|
return namespace
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
function getNamespaceFromContext (context) {
|
|
19
|
-
return context
|
|
19
|
+
return context?.[DD_IAST_METRICS_NAMESPACE]
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
function finalizeRequestNamespace (context, rootSpan) {
|
|
@@ -40,11 +40,14 @@ function finalizeRequestNamespace (context, rootSpan) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
function merge (metrics) {
|
|
43
|
-
metrics.forEach(metric =>
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
metrics.forEach(metric => {
|
|
44
|
+
const { metric: metricName, type, tags, points } = metric
|
|
45
|
+
|
|
46
|
+
if (points?.length && type === 'count') {
|
|
47
|
+
const gMetric = globalNamespace.count(metricName, getTagsObject(tags))
|
|
48
|
+
points.forEach(point => gMetric.inc(point[1]))
|
|
49
|
+
}
|
|
50
|
+
})
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
function getTagsObject (tags) {
|
|
@@ -56,11 +59,34 @@ function getTagsObject (tags) {
|
|
|
56
59
|
class IastNamespace extends Namespace {
|
|
57
60
|
constructor () {
|
|
58
61
|
super('iast')
|
|
62
|
+
|
|
63
|
+
this.iastMetrics = new Map()
|
|
59
64
|
}
|
|
60
65
|
|
|
61
|
-
|
|
62
|
-
this.
|
|
63
|
-
|
|
66
|
+
getIastMetrics (name) {
|
|
67
|
+
let metrics = this.iastMetrics.get(name)
|
|
68
|
+
if (!metrics) {
|
|
69
|
+
metrics = new Map()
|
|
70
|
+
this.iastMetrics.set(name, metrics)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return metrics
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
getMetric (name, tags, type = 'count') {
|
|
77
|
+
const metrics = this.getIastMetrics(name)
|
|
78
|
+
|
|
79
|
+
let metric = metrics.get(tags)
|
|
80
|
+
if (!metric) {
|
|
81
|
+
metric = super[type](name, Array.isArray(tags) ? [...tags] : tags)
|
|
82
|
+
metrics.set(tags, metric)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return metric
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
count (name, tags) {
|
|
89
|
+
return this.getMetric(name, tags, 'count')
|
|
64
90
|
}
|
|
65
91
|
}
|
|
66
92
|
|
|
@@ -72,5 +98,7 @@ module.exports = {
|
|
|
72
98
|
finalizeRequestNamespace,
|
|
73
99
|
globalNamespace,
|
|
74
100
|
|
|
75
|
-
DD_IAST_METRICS_NAMESPACE
|
|
101
|
+
DD_IAST_METRICS_NAMESPACE,
|
|
102
|
+
|
|
103
|
+
IastNamespace
|
|
76
104
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
function addMetricsToSpan (rootSpan, metrics, tagPrefix) {
|
|
4
|
-
if (!rootSpan
|
|
4
|
+
if (!rootSpan?.addTags || !metrics) return
|
|
5
5
|
|
|
6
6
|
const flattenMap = new Map()
|
|
7
7
|
metrics
|
|
8
|
-
.filter(data => data
|
|
8
|
+
.filter(data => data?.metric)
|
|
9
9
|
.forEach(data => {
|
|
10
10
|
const name = taggedMetricName(data)
|
|
11
11
|
let total = flattenMap.get(name)
|
|
@@ -27,19 +27,20 @@ function addMetricsToSpan (rootSpan, metrics, tagPrefix) {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
function flatten (metricData) {
|
|
30
|
-
|
|
30
|
+
const { points } = metricData
|
|
31
|
+
return points ? points.map(point => point[1]).reduce((total, value) => total + value, 0) : 0
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
function taggedMetricName (data) {
|
|
34
35
|
const metric = data.metric
|
|
35
|
-
const tags =
|
|
36
|
-
return !tags
|
|
36
|
+
const tags = filterTags(data.tags)
|
|
37
|
+
return !tags?.length
|
|
37
38
|
? metric
|
|
38
39
|
: `${metric}.${processTagValue(tags)}`
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
function filterTags (tags) {
|
|
42
|
-
return tags
|
|
43
|
+
return tags?.filter(tag => !tag.startsWith('lib_language') && !tag.startsWith('version'))
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
function processTagValue (tags) {
|
|
@@ -17,25 +17,35 @@ let limiter = new Limiter(100)
|
|
|
17
17
|
|
|
18
18
|
const metricsQueue = new Map()
|
|
19
19
|
|
|
20
|
+
// following header lists are ordered in the same way the spec orders them, it doesn't matter but it's easier to compare
|
|
20
21
|
const contentHeaderList = [
|
|
21
|
-
'content-encoding',
|
|
22
|
-
'content-language',
|
|
23
22
|
'content-length',
|
|
24
|
-
'content-type'
|
|
23
|
+
'content-type',
|
|
24
|
+
'content-encoding',
|
|
25
|
+
'content-language'
|
|
25
26
|
]
|
|
26
27
|
|
|
27
28
|
const REQUEST_HEADERS_MAP = mapHeaderAndTags([
|
|
28
|
-
|
|
29
|
-
'accept-encoding',
|
|
30
|
-
'accept-language',
|
|
31
|
-
'host',
|
|
29
|
+
...ipHeaderList,
|
|
32
30
|
'forwarded',
|
|
33
|
-
'user-agent',
|
|
34
31
|
'via',
|
|
35
|
-
|
|
32
|
+
...contentHeaderList,
|
|
33
|
+
'host',
|
|
34
|
+
'user-agent',
|
|
35
|
+
'accept',
|
|
36
|
+
'accept-encoding',
|
|
37
|
+
'accept-language'
|
|
38
|
+
], 'http.request.headers.')
|
|
36
39
|
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
const IDENTIFICATION_HEADERS_MAP = mapHeaderAndTags([
|
|
41
|
+
'x-amzn-trace-id',
|
|
42
|
+
'cloudfront-viewer-ja3-fingerprint',
|
|
43
|
+
'cf-ray',
|
|
44
|
+
'x-cloud-trace-context',
|
|
45
|
+
'x-appgw-trace-id',
|
|
46
|
+
'x-sigsci-requestid',
|
|
47
|
+
'x-sigsci-tags',
|
|
48
|
+
'akamai-user-risk'
|
|
39
49
|
], 'http.request.headers.')
|
|
40
50
|
|
|
41
51
|
const RESPONSE_HEADERS_MAP = mapHeaderAndTags(contentHeaderList, 'http.response.headers.')
|
|
@@ -171,6 +181,9 @@ function finishRequest (req, res) {
|
|
|
171
181
|
|
|
172
182
|
incrementWafRequestsMetric(req)
|
|
173
183
|
|
|
184
|
+
// collect some headers even when no attack is detected
|
|
185
|
+
rootSpan.addTags(filterHeaders(req.headers, IDENTIFICATION_HEADERS_MAP))
|
|
186
|
+
|
|
174
187
|
if (!rootSpan.context()._tags['appsec.event']) return
|
|
175
188
|
|
|
176
189
|
const newTags = filterHeaders(res.getHeaders(), RESPONSE_HEADERS_MAP)
|
|
@@ -38,10 +38,15 @@ class AgentProxyCiVisibilityExporter extends CiVisibilityExporter {
|
|
|
38
38
|
|
|
39
39
|
this.getAgentInfo((err, agentInfo) => {
|
|
40
40
|
this._isInitialized = true
|
|
41
|
-
|
|
41
|
+
let latestEvpProxyVersion = getLatestEvpProxyVersion(err, agentInfo)
|
|
42
42
|
const isEvpCompatible = latestEvpProxyVersion >= 2
|
|
43
43
|
const isGzipCompatible = latestEvpProxyVersion >= 4
|
|
44
44
|
|
|
45
|
+
// v3 does not work well citestcycle, so we downgrade to v2
|
|
46
|
+
if (latestEvpProxyVersion === 3) {
|
|
47
|
+
latestEvpProxyVersion = 2
|
|
48
|
+
}
|
|
49
|
+
|
|
45
50
|
const evpProxyPrefix = `${AGENT_EVP_PROXY_PATH_PREFIX}${latestEvpProxyVersion}`
|
|
46
51
|
if (isEvpCompatible) {
|
|
47
52
|
this._isUsingEvpProxy = true
|
|
@@ -87,7 +87,7 @@ const registerLambdaHook = () => {
|
|
|
87
87
|
const lambdaTaskRoot = process.env.LAMBDA_TASK_ROOT
|
|
88
88
|
const originalLambdaHandler = process.env.DD_LAMBDA_HANDLER
|
|
89
89
|
|
|
90
|
-
if (originalLambdaHandler !== undefined) {
|
|
90
|
+
if (originalLambdaHandler !== undefined && lambdaTaskRoot !== undefined) {
|
|
91
91
|
const [moduleRoot, moduleAndHandler] = _extractModuleRootAndHandler(originalLambdaHandler)
|
|
92
92
|
const [_module] = _extractModuleNameAndHandlerPath(moduleAndHandler)
|
|
93
93
|
|