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.
Files changed (36) hide show
  1. package/index.d.ts +4 -0
  2. package/package.json +2 -2
  3. package/packages/datadog-plugin-cucumber/src/index.js +2 -2
  4. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +2 -2
  5. package/packages/datadog-plugin-jest/src/index.js +2 -2
  6. package/packages/datadog-plugin-mocha/src/index.js +2 -2
  7. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +23 -12
  8. package/packages/dd-trace/src/appsec/iast/path-line.js +9 -6
  9. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +1 -1
  10. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +6 -2
  11. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-telemetry.js +1 -1
  12. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +6 -7
  13. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +1 -1
  14. package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +34 -27
  15. package/packages/dd-trace/src/appsec/iast/telemetry/namespaces.js +39 -11
  16. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +7 -6
  17. package/packages/dd-trace/src/appsec/reporter.js +24 -11
  18. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +6 -1
  19. package/packages/dd-trace/src/lambda/runtime/ritm.js +1 -1
  20. package/packages/dd-trace/src/opentracing/propagation/text_map.js +21 -2
  21. package/packages/dd-trace/src/opentracing/span.js +2 -0
  22. package/packages/dd-trace/src/opentracing/span_context.js +1 -0
  23. package/packages/dd-trace/src/plugins/util/test.js +2 -2
  24. package/packages/dd-trace/src/priority_sampler.js +11 -6
  25. package/packages/dd-trace/src/telemetry/index.js +15 -4
  26. package/CONTRIBUTING.md +0 -171
  27. package/MIGRATING.md +0 -224
  28. package/packages/dd-trace/src/external-logger/test/index.spec.js +0 -147
  29. package/scripts/check-proposal-labels.js +0 -71
  30. package/scripts/check_licenses.js +0 -69
  31. package/scripts/helpers/color.js +0 -8
  32. package/scripts/helpers/exec.js +0 -22
  33. package/scripts/helpers/title.js +0 -15
  34. package/scripts/install_plugin_modules.js +0 -248
  35. package/scripts/publish_docs.js +0 -21
  36. 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.6.0",
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.2.3",
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
- TEST_EARLY_FLAKE_IS_RETRY
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(TEST_EARLY_FLAKE_IS_RETRY, 'true')
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
- TEST_EARLY_FLAKE_IS_RETRY,
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(TEST_EARLY_FLAKE_IS_RETRY, 'true')
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
- TEST_EARLY_FLAKE_IS_RETRY,
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[TEST_EARLY_FLAKE_IS_RETRY] = 'true'
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
- TEST_EARLY_FLAKE_IS_RETRY,
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[TEST_EARLY_FLAKE_IS_RETRY] = 'true'
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 } = require('./telemetry/iast-metric')
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, tag, tagKey = TagKey.VULNERABILITY_TYPE) {
24
+ constructor (moduleName, channelName, tagValues, tagKey = TagKey.VULNERABILITY_TYPE) {
24
25
  this.moduleName = moduleName
25
26
  this.channelName = channelName
26
- this.tag = tag
27
- this.tagKey = tagKey
28
- this.executedMetric = getExecutedMetric(this.tagKey)
29
- this.instrumentedMetric = getInstrumentedMetric(this.tagKey)
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) return
38
+ if (!this.moduleInstrumented) {
39
+ this.moduleInstrumented = true
35
40
 
36
- this.moduleInstrumented = true
37
- this.instrumentedMetric.inc(this.tag)
41
+ this.tags.forEach(tag => this.instrumentedMetric.inc(undefined, tag))
42
+ }
38
43
  }
39
44
 
40
45
  increaseExecuted (iastContext) {
41
- this.executedMetric.inc(this.tag, iastContext)
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, tag, iastContext = getIastContext(storage.getStore()) }) {
84
+ _execHandlerAndIncMetric ({ handler, metric, tags, iastContext = getIastContext(storage.getStore()) }) {
80
85
  try {
81
86
  const result = handler()
82
- iastTelemetry.isEnabled() && metric.inc(tag, iastContext)
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
- Error.prepareStackTrace = function (_, callsites) {
33
- callsiteList = callsites
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.add(metrics.requestCount, null, iastContext)
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
- tag: [HTTP_REQUEST_HEADER_VALUE, HTTP_REQUEST_HEADER_NAME],
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
- tag: [HTTP_REQUEST_URI],
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.add(metrics.instrumentedPropagation)
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
- actualPrepareStackTrace = getPrepareStackTrace(originalPrepareStackTrace)
73
+ let actual = getPrepareStackTrace(originalPrepareStackTrace)
73
74
  return {
74
75
  configurable: true,
75
76
  get () {
76
- return actualPrepareStackTrace
77
+ return actual
77
78
  },
78
79
  set (value) {
79
- actualPrepareStackTrace = getPrepareStackTrace(value)
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 (!actualPrepareStackTrace) return
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
  }
@@ -40,7 +40,7 @@ function getContextDefault () {
40
40
 
41
41
  function getContextDebug () {
42
42
  const iastContext = getContextDefault()
43
- EXECUTED_PROPAGATION.inc(null, iastContext)
43
+ EXECUTED_PROPAGATION.inc(iastContext)
44
44
  return iastContext
45
45
  }
46
46
 
@@ -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
- getNamespace (context) {
30
- return getNamespaceFromContext(context) || globalNamespace
42
+ formatTags (...tags) {
43
+ return formatTags(tags, this.tagKey)
31
44
  }
32
45
 
33
- getTag (tagValue) {
34
- return tagValue ? { [this.tagKey]: tagValue } : undefined
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
- addValue (value, tagValue, context) {
38
- this.getNamespace(context)
39
- .count(this.name, this.getTag(tagValue))
40
- .inc(value)
41
- }
52
+ class NoTaggedIastMetric extends IastMetric {
53
+ constructor (name, scope) {
54
+ super(name, scope)
42
55
 
43
- add (value, tagValue, context) {
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 (tagValue, context) {
52
- this.add(1, tagValue, context)
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 IastMetric('instrumented.propagation', Scope.GLOBAL)
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 IastMetric('request.tainted', Scope.REQUEST)
80
+ const REQUEST_TAINTED = new NoTaggedIastMetric('request.tainted', Scope.REQUEST)
72
81
 
73
82
  // DEBUG using metrics
74
- const EXECUTED_PROPAGATION = new IastMetric('executed.propagation', Scope.REQUEST)
75
- const EXECUTED_TAINTED = new IastMetric('executed.tainted', Scope.REQUEST)
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 Namespace('iast')
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 && context[DD_IAST_METRICS_NAMESPACE]
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 => metric.points.forEach(point => {
44
- globalNamespace
45
- .count(metric.metric, getTagsObject(metric.tags))
46
- .inc(point[1])
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
- reset () {
62
- this.metrics.clear()
63
- this.distributions.clear()
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 || !rootSpan.addTags || !metrics) return
4
+ if (!rootSpan?.addTags || !metrics) return
5
5
 
6
6
  const flattenMap = new Map()
7
7
  metrics
8
- .filter(data => data && data.metric)
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
- return metricData.points && metricData.points.map(point => point[1]).reduce((total, value) => total + value, 0)
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 = data.tags && filterTags(data.tags)
36
- return !tags || !tags.length
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.filter(tag => !tag.startsWith('lib_language') && !tag.startsWith('version'))
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
- 'accept',
29
- 'accept-encoding',
30
- 'accept-language',
31
- 'host',
29
+ ...ipHeaderList,
32
30
  'forwarded',
33
- 'user-agent',
34
31
  'via',
35
- 'x-amzn-trace-id',
32
+ ...contentHeaderList,
33
+ 'host',
34
+ 'user-agent',
35
+ 'accept',
36
+ 'accept-encoding',
37
+ 'accept-language'
38
+ ], 'http.request.headers.')
36
39
 
37
- ...ipHeaderList,
38
- ...contentHeaderList
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
- const latestEvpProxyVersion = getLatestEvpProxyVersion(err, agentInfo)
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