dd-trace 2.40.0 → 2.41.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 (56) hide show
  1. package/package.json +3 -3
  2. package/packages/datadog-instrumentations/src/cucumber.js +5 -2
  3. package/packages/datadog-instrumentations/src/grpc/client.js +44 -42
  4. package/packages/datadog-instrumentations/src/grpc/server.js +69 -60
  5. package/packages/datadog-instrumentations/src/http2/client.js +25 -26
  6. package/packages/datadog-instrumentations/src/jest.js +3 -1
  7. package/packages/datadog-instrumentations/src/mocha.js +5 -3
  8. package/packages/datadog-plugin-cypress/src/plugin.js +4 -2
  9. package/packages/datadog-plugin-grpc/src/client.js +29 -11
  10. package/packages/datadog-plugin-grpc/src/server.js +22 -6
  11. package/packages/datadog-plugin-http2/src/client.js +46 -29
  12. package/packages/datadog-plugin-router/src/index.js +1 -1
  13. package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +3 -0
  14. package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +7 -1
  15. package/packages/dd-trace/src/appsec/iast/analyzers/ldap-injection-analyzer.js +3 -0
  16. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +19 -15
  17. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +5 -2
  18. package/packages/dd-trace/src/appsec/iast/analyzers/ssrf-analyzer.js +2 -0
  19. package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +3 -1
  20. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +18 -19
  21. package/packages/dd-trace/src/appsec/iast/analyzers/weak-cipher-analyzer.js +3 -0
  22. package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +3 -0
  23. package/packages/dd-trace/src/appsec/iast/iast-log.js +1 -1
  24. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +205 -0
  25. package/packages/dd-trace/src/appsec/iast/index.js +6 -5
  26. package/packages/dd-trace/src/appsec/iast/tags.js +2 -1
  27. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +6 -6
  28. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +3 -3
  29. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +23 -4
  30. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +32 -16
  31. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-telemetry.js +33 -0
  32. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +23 -16
  33. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +76 -37
  34. package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +101 -0
  35. package/packages/dd-trace/src/appsec/iast/telemetry/index.js +45 -0
  36. package/packages/dd-trace/src/appsec/iast/telemetry/{logs.js → log/index.js} +5 -5
  37. package/packages/dd-trace/src/appsec/iast/telemetry/{log_collector.js → log/log-collector.js} +1 -1
  38. package/packages/dd-trace/src/appsec/iast/telemetry/namespaces.js +76 -0
  39. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +53 -0
  40. package/packages/dd-trace/src/appsec/iast/telemetry/verbosity.js +42 -0
  41. package/packages/dd-trace/src/config.js +21 -2
  42. package/packages/dd-trace/src/constants.js +1 -0
  43. package/packages/dd-trace/src/opentracing/tracer.js +1 -0
  44. package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
  45. package/packages/dd-trace/src/plugins/outbound.js +29 -12
  46. package/packages/dd-trace/src/plugins/plugin.js +28 -0
  47. package/packages/dd-trace/src/plugins/tracing.js +33 -16
  48. package/packages/dd-trace/src/plugins/util/ci.js +1 -1
  49. package/packages/dd-trace/src/plugins/util/test.js +55 -11
  50. package/packages/dd-trace/src/plugins/util/user-provided-git.js +1 -22
  51. package/packages/dd-trace/src/profiling/config.js +0 -3
  52. package/packages/dd-trace/src/profiling/index.js +0 -2
  53. package/packages/dd-trace/src/profiling/profilers/wall.js +23 -11
  54. package/packages/diagnostics_channel/src/index.js +64 -0
  55. package/packages/dd-trace/src/profiling/profilers/cpu.js +0 -126
  56. /package/packages/dd-trace/src/appsec/iast/taint-tracking/{origin-types.js → source-types.js} +0 -0
@@ -1,9 +1,9 @@
1
1
  'use strict'
2
2
 
3
- const dc = require('../../../../../diagnostics_channel')
4
- const logCollector = require('./log_collector')
5
- const { sendData } = require('../../../telemetry/send-data')
6
- const log = require('../../../log')
3
+ const dc = require('../../../../../../diagnostics_channel')
4
+ const logCollector = require('./log-collector')
5
+ const { sendData } = require('../../../../telemetry/send-data')
6
+ const log = require('../../../../log')
7
7
 
8
8
  const telemetryStartChannel = dc.channel('datadog:telemetry:start')
9
9
  const telemetryStopChannel = dc.channel('datadog:telemetry:stop')
@@ -37,7 +37,7 @@ function isLogCollectionEnabled (config) {
37
37
 
38
38
  function onTelemetryStart (msg) {
39
39
  if (!msg || !isLogCollectionEnabled(msg.config)) {
40
- log.info('IAST telemetry logs start received but log collection is not enabled or configuration is incorrect')
40
+ log.info('IAST telemetry logs start event received but log collection is not enabled or configuration is incorrect')
41
41
  return false
42
42
  }
43
43
 
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const log = require('../../../log')
3
+ const log = require('../../../../log')
4
4
 
5
5
  const logs = new Map()
6
6
 
@@ -0,0 +1,76 @@
1
+ 'use strict'
2
+
3
+ const log = require('../../../log')
4
+ const { Namespace } = require('../../../telemetry/metrics')
5
+ const { addMetricsToSpan, filterTags } = require('./span-tags')
6
+ const { IAST_TRACE_METRIC_PREFIX } = require('../tags')
7
+
8
+ const DD_IAST_METRICS_NAMESPACE = Symbol('_dd.iast.request.metrics.namespace')
9
+
10
+ function initRequestNamespace (context) {
11
+ if (!context) return
12
+
13
+ const namespace = new Namespace('iast')
14
+ context[DD_IAST_METRICS_NAMESPACE] = namespace
15
+ return namespace
16
+ }
17
+
18
+ function getNamespaceFromContext (context) {
19
+ return context && context[DD_IAST_METRICS_NAMESPACE]
20
+ }
21
+
22
+ function finalizeRequestNamespace (context, rootSpan) {
23
+ try {
24
+ const namespace = getNamespaceFromContext(context)
25
+ if (!namespace) return
26
+
27
+ const metrics = [...namespace.metrics.values()]
28
+ namespace.metrics.clear()
29
+
30
+ addMetricsToSpan(rootSpan, metrics, IAST_TRACE_METRIC_PREFIX)
31
+
32
+ merge(metrics)
33
+ } catch (e) {
34
+ log.error(e)
35
+ } finally {
36
+ if (context) {
37
+ delete context[DD_IAST_METRICS_NAMESPACE]
38
+ }
39
+ }
40
+ }
41
+
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
+ }))
48
+ }
49
+
50
+ function getTagsObject (tags) {
51
+ if (tags && tags.length > 0) {
52
+ return filterTags(tags)
53
+ }
54
+ }
55
+
56
+ class IastNamespace extends Namespace {
57
+ constructor () {
58
+ super('iast')
59
+ }
60
+
61
+ reset () {
62
+ this.metrics.clear()
63
+ this.distributions.clear()
64
+ }
65
+ }
66
+
67
+ const globalNamespace = new IastNamespace()
68
+
69
+ module.exports = {
70
+ initRequestNamespace,
71
+ getNamespaceFromContext,
72
+ finalizeRequestNamespace,
73
+ globalNamespace,
74
+
75
+ DD_IAST_METRICS_NAMESPACE
76
+ }
@@ -0,0 +1,53 @@
1
+ 'use strict'
2
+
3
+ function addMetricsToSpan (rootSpan, metrics, tagPrefix) {
4
+ if (!rootSpan || !rootSpan.addTags || !metrics) return
5
+
6
+ const flattenMap = new Map()
7
+ metrics
8
+ .filter(data => data && data.metric)
9
+ .forEach(data => {
10
+ const name = taggedMetricName(data)
11
+ let total = flattenMap.get(name)
12
+ const value = flatten(data)
13
+ if (!total) {
14
+ total = value
15
+ } else {
16
+ total += value
17
+ }
18
+ flattenMap.set(name, total)
19
+ })
20
+
21
+ for (const [key, value] of flattenMap) {
22
+ const tagName = `${tagPrefix}.${key}`
23
+ rootSpan.addTags({
24
+ [tagName]: value
25
+ })
26
+ }
27
+ }
28
+
29
+ function flatten (metricData) {
30
+ return metricData.points && metricData.points.map(point => point[1]).reduce((total, value) => total + value, 0)
31
+ }
32
+
33
+ function taggedMetricName (data) {
34
+ const metric = data.metric
35
+ const tags = data.tags && filterTags(data.tags)
36
+ return !tags || !tags.length
37
+ ? metric
38
+ : `${metric}.${processTagValue(tags)}`
39
+ }
40
+
41
+ function filterTags (tags) {
42
+ return tags.filter(tag => !tag.startsWith('lib_language') && !tag.startsWith('version'))
43
+ }
44
+
45
+ function processTagValue (tags) {
46
+ return tags.map(tag => tag.includes(':') ? tag.split(':')[1] : tag)
47
+ .join('_').replace(/\./g, '_')
48
+ }
49
+
50
+ module.exports = {
51
+ addMetricsToSpan,
52
+ filterTags
53
+ }
@@ -0,0 +1,42 @@
1
+ 'use strict'
2
+
3
+ const Verbosity = {
4
+ OFF: 0,
5
+ MANDATORY: 1,
6
+ INFORMATION: 2,
7
+ DEBUG: 3
8
+ }
9
+
10
+ function isDebugAllowed (value) {
11
+ return value >= Verbosity.DEBUG
12
+ }
13
+
14
+ function isInfoAllowed (value) {
15
+ return value >= Verbosity.INFORMATION
16
+ }
17
+
18
+ function getVerbosity (verbosity) {
19
+ if (verbosity) {
20
+ verbosity = verbosity.toUpperCase()
21
+ return Verbosity[verbosity] !== undefined ? Verbosity[verbosity] : Verbosity.INFORMATION
22
+ } else {
23
+ return Verbosity.INFORMATION
24
+ }
25
+ }
26
+
27
+ function getName (verbosityValue) {
28
+ for (const name in Verbosity) {
29
+ if (Verbosity[name] === verbosityValue) {
30
+ return name
31
+ }
32
+ }
33
+ return 'OFF'
34
+ }
35
+
36
+ module.exports = {
37
+ Verbosity,
38
+ isDebugAllowed,
39
+ isInfoAllowed,
40
+ getVerbosity,
41
+ getName
42
+ }
@@ -244,6 +244,10 @@ class Config {
244
244
  process.env.DD_TELEMETRY_DEBUG,
245
245
  false
246
246
  )
247
+ const DD_TELEMETRY_METRICS_ENABLED = coalesce(
248
+ process.env.DD_TELEMETRY_METRICS_ENABLED,
249
+ false
250
+ )
247
251
  const DD_TRACE_AGENT_PROTOCOL_VERSION = coalesce(
248
252
  options.protocolVersion,
249
253
  process.env.DD_TRACE_AGENT_PROTOCOL_VERSION,
@@ -316,6 +320,12 @@ class Config {
316
320
  const DD_TRACE_SPAN_ATTRIBUTE_SCHEMA = validateNamingVersion(
317
321
  process.env.DD_TRACE_SPAN_ATTRIBUTE_SCHEMA
318
322
  )
323
+ const DD_TRACE_PEER_SERVICE_MAPPING = coalesce(
324
+ options.peerServiceMapping,
325
+ process.env.DD_TRACE_PEER_SERVICE_MAPPING ? fromEntries(
326
+ process.env.DD_TRACE_PEER_SERVICE_MAPPING.split(',').map(x => x.trim().split(':'))
327
+ ) : {}
328
+ )
319
329
  const DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED = process.env.DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED
320
330
 
321
331
  const DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED = coalesce(
@@ -458,6 +468,12 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
458
468
  true
459
469
  )
460
470
 
471
+ const DD_IAST_TELEMETRY_VERBOSITY = coalesce(
472
+ iastOptions && iastOptions.telemetryVerbosity,
473
+ process.env.DD_IAST_TELEMETRY_VERBOSITY,
474
+ 'INFORMATION'
475
+ )
476
+
461
477
  const DD_CIVISIBILITY_GIT_UPLOAD_ENABLED = coalesce(
462
478
  process.env.DD_CIVISIBILITY_GIT_UPLOAD_ENABLED,
463
479
  true
@@ -551,6 +567,7 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
551
567
  : true
552
568
  )
553
569
  this.traceRemoveIntegrationServiceNamesEnabled = DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED
570
+ this.peerServiceMapping = DD_TRACE_PEER_SERVICE_MAPPING
554
571
  this.lookup = options.lookup
555
572
  this.startupLogs = isTrue(DD_TRACE_STARTUP_LOGS)
556
573
  // Disabled for CI Visibility's agentless
@@ -558,7 +575,8 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
558
575
  enabled: DD_TRACE_EXPORTER !== 'datadog' && isTrue(DD_TRACE_TELEMETRY_ENABLED),
559
576
  heartbeatInterval: DD_TELEMETRY_HEARTBEAT_INTERVAL,
560
577
  logCollection: isTrue(DD_TELEMETRY_LOG_COLLECTION_ENABLED),
561
- debug: isTrue(DD_TELEMETRY_DEBUG)
578
+ debug: isTrue(DD_TELEMETRY_DEBUG),
579
+ metrics: isTrue(DD_TELEMETRY_METRICS_ENABLED)
562
580
  }
563
581
  this.protocolVersion = DD_TRACE_AGENT_PROTOCOL_VERSION
564
582
  this.tagsHeaderMaxLength = parseInt(DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH)
@@ -587,7 +605,8 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
587
605
  maxConcurrentRequests: DD_IAST_MAX_CONCURRENT_REQUESTS,
588
606
  maxContextOperations: DD_IAST_MAX_CONTEXT_OPERATIONS,
589
607
  deduplicationEnabled: DD_IAST_DEDUPLICATION_ENABLED,
590
- redactionEnabled: DD_IAST_REDACTION_ENABLED
608
+ redactionEnabled: DD_IAST_REDACTION_ENABLED,
609
+ telemetryVerbosity: DD_IAST_TELEMETRY_VERBOSITY
591
610
  }
592
611
 
593
612
  this.isCiVisibility = isTrue(DD_IS_CIVISIBILITY)
@@ -28,6 +28,7 @@ module.exports = {
28
28
  CLIENT_PORT_KEY: 'network.destination.port',
29
29
  PEER_SERVICE_KEY: 'peer.service',
30
30
  PEER_SERVICE_SOURCE_KEY: '_dd.peer.service.source',
31
+ PEER_SERVICE_REMAP_KEY: '_dd.peer.service.remapped_from',
31
32
  SCI_REPOSITORY_URL: '_dd.git.repository_url',
32
33
  SCI_COMMIT_SHA: '_dd.git.commit.sha'
33
34
  }
@@ -27,6 +27,7 @@ class DatadogTracer {
27
27
  this._env = config.env
28
28
  this._tags = config.tags
29
29
  this._computePeerService = config.spanComputePeerService
30
+ this._peerServiceMapping = config.peerServiceMapping
30
31
  this._logInjection = config.logInjection
31
32
  this._debug = config.debug
32
33
  this._prioritySampler = new PrioritySampler(config.env, config.sampler)
@@ -111,7 +111,12 @@ module.exports = class CiPlugin extends Plugin {
111
111
  const childOf = getTestParentSpan(this.tracer)
112
112
 
113
113
  let testTags = {
114
- ...getTestCommonTags(testName, testSuite, this.frameworkVersion),
114
+ ...getTestCommonTags(
115
+ testName,
116
+ testSuite,
117
+ this.frameworkVersion,
118
+ this.constructor.id
119
+ ),
115
120
  [COMPONENT]: this.constructor.id,
116
121
  ...extraTags
117
122
  }
@@ -3,7 +3,8 @@
3
3
  const {
4
4
  CLIENT_PORT_KEY,
5
5
  PEER_SERVICE_KEY,
6
- PEER_SERVICE_SOURCE_KEY
6
+ PEER_SERVICE_SOURCE_KEY,
7
+ PEER_SERVICE_REMAP_KEY
7
8
  } = require('../constants')
8
9
  const TracingPlugin = require('./tracing')
9
10
 
@@ -34,9 +35,11 @@ class OutboundPlugin extends TracingPlugin {
34
35
  * - If `peer.service` was defined _before_ we compute it (for example in custom instrumentation),
35
36
  * `_dd.peer.service.source`'s value is `peer.service`
36
37
  */
37
-
38
- if (tags['peer.service'] !== undefined) {
39
- return { [PEER_SERVICE_SOURCE_KEY]: 'peer.service' }
38
+ if (tags[PEER_SERVICE_KEY] !== undefined) {
39
+ return {
40
+ [PEER_SERVICE_KEY]: tags[PEER_SERVICE_KEY],
41
+ [PEER_SERVICE_SOURCE_KEY]: PEER_SERVICE_KEY
42
+ }
40
43
  }
41
44
 
42
45
  const sourceTags = [
@@ -52,23 +55,37 @@ class OutboundPlugin extends TracingPlugin {
52
55
  }
53
56
  }
54
57
  }
55
- return {}
58
+ return undefined
56
59
  }
57
60
 
58
- startSpan (name, options) {
59
- const span = super.startSpan(name, options)
60
- return span
61
+ getPeerServiceRemap (peerData) {
62
+ /**
63
+ * If DD_TRACE_PEER_SERVICE_MAPPING is matched, we need to override the existing
64
+ * peer service and add the value we overrode.
65
+ */
66
+ const peerService = peerData[PEER_SERVICE_KEY]
67
+ if (peerService && this.tracer._peerServiceMapping[peerService]) {
68
+ return {
69
+ ...peerData,
70
+ [PEER_SERVICE_KEY]: this.tracer._peerServiceMapping[peerService],
71
+ [PEER_SERVICE_REMAP_KEY]: peerService
72
+ }
73
+ }
74
+ return peerData
61
75
  }
62
76
 
63
77
  finish () {
64
- const span = this.activeSpan
78
+ this.tagPeerService(this.activeSpan)
79
+ super.finish(...arguments)
80
+ }
81
+
82
+ tagPeerService (span) {
65
83
  if (this.tracer._computePeerService) {
66
84
  const peerData = this.getPeerService(span.context()._tags)
67
- if (peerData) {
68
- span.addTags(peerData)
85
+ if (peerData !== undefined) {
86
+ span.addTags(this.getPeerServiceRemap(peerData))
69
87
  }
70
88
  }
71
- super.finish(...arguments)
72
89
  }
73
90
 
74
91
  connect (url) {
@@ -25,9 +25,31 @@ class Subscription {
25
25
  }
26
26
  }
27
27
 
28
+ class StoreBinding {
29
+ constructor (event, transform) {
30
+ this._channel = dc.channel(event)
31
+ this._transform = data => {
32
+ const store = storage.getStore()
33
+
34
+ return !store || !store.noop
35
+ ? transform(data)
36
+ : store
37
+ }
38
+ }
39
+
40
+ enable () {
41
+ this._channel.bindStore(storage, this._transform)
42
+ }
43
+
44
+ disable () {
45
+ this._channel.unbindStore(storage, this._transform)
46
+ }
47
+ }
48
+
28
49
  module.exports = class Plugin {
29
50
  constructor (tracer, tracerConfig) {
30
51
  this._subscriptions = []
52
+ this._bindings = []
31
53
  this._enabled = false
32
54
  this._tracer = tracer
33
55
  this.config = {} // plugin-specific configuration, unset until .configure() is called
@@ -53,6 +75,10 @@ module.exports = class Plugin {
53
75
  this._subscriptions.push(new Subscription(channelName, handler))
54
76
  }
55
77
 
78
+ addBind (channelName, transform) {
79
+ this._bindings.push(new StoreBinding(channelName, transform))
80
+ }
81
+
56
82
  addError (error) {
57
83
  const store = storage.getStore()
58
84
 
@@ -71,9 +97,11 @@ module.exports = class Plugin {
71
97
  if (config.enabled && !this._enabled) {
72
98
  this._enabled = true
73
99
  this._subscriptions.forEach(sub => sub.enable())
100
+ this._bindings.forEach(sub => sub.enable())
74
101
  } else if (!config.enabled && this._enabled) {
75
102
  this._enabled = false
76
103
  this._subscriptions.forEach(sub => sub.disable())
104
+ this._bindings.forEach(sub => sub.disable())
77
105
  }
78
106
  }
79
107
  }
@@ -13,17 +13,7 @@ class TracingPlugin extends Plugin {
13
13
  this.component = this.constructor.component || this.constructor.id
14
14
  this.operation = this.constructor.operation
15
15
 
16
- this.addTraceSub('start', message => {
17
- this.start(message)
18
- })
19
-
20
- this.addTraceSub('error', err => {
21
- this.error(err)
22
- })
23
-
24
- this.addTraceSub('finish', message => {
25
- this.finish(message)
26
- })
16
+ this.addTraceSubs()
27
17
  }
28
18
 
29
19
  get activeSpan () {
@@ -65,19 +55,43 @@ class TracingPlugin extends Plugin {
65
55
  this.addError(error)
66
56
  }
67
57
 
58
+ addTraceSubs () {
59
+ const events = ['start', 'end', 'asyncStart', 'asyncEnd', 'error', 'finish']
60
+
61
+ for (const event of events) {
62
+ const bindName = `bind${event.charAt(0).toUpperCase()}${event.slice(1)}`
63
+
64
+ if (this[event]) {
65
+ this.addTraceSub(event, message => {
66
+ this[event](message)
67
+ })
68
+ }
69
+
70
+ if (this[bindName]) {
71
+ this.addTraceBind(event, message => this[bindName](message))
72
+ }
73
+ }
74
+ }
75
+
68
76
  addTraceSub (eventName, handler) {
69
- this.addSub(`apm:${this.component}:${this.operation}:${eventName}`, handler)
77
+ const prefix = this.constructor.prefix || `apm:${this.component}:${this.operation}`
78
+ this.addSub(`${prefix}:${eventName}`, handler)
70
79
  }
71
80
 
72
- addError (error) {
73
- const span = this.activeSpan
81
+ addTraceBind (eventName, transform) {
82
+ const prefix = this.constructor.prefix || `apm:${this.component}:${this.operation}`
83
+ this.addBind(`${prefix}:${eventName}`, transform)
84
+ }
74
85
 
86
+ addError (error, span = this.activeSpan) {
75
87
  if (!span._spanContext._tags['error']) {
88
+ // Errors may be wrapped in a context.
89
+ error = (error && error.error) || error
76
90
  span.setTag('error', error || 1)
77
91
  }
78
92
  }
79
93
 
80
- startSpan (name, { childOf, kind, meta, metrics, service, resource, type } = {}) {
94
+ startSpan (name, { childOf, kind, meta, metrics, service, resource, type } = {}, enter = true) {
81
95
  const store = storage.getStore()
82
96
 
83
97
  if (store && childOf === undefined) {
@@ -100,7 +114,10 @@ class TracingPlugin extends Plugin {
100
114
 
101
115
  analyticsSampler.sample(span, this.config.measured)
102
116
 
103
- storage.enterWith({ ...store, span })
117
+ // TODO: Remove this after migration to TracingChannel is done.
118
+ if (enter) {
119
+ storage.enterWith({ ...store, span })
120
+ }
104
121
 
105
122
  return span
106
123
  }
@@ -77,7 +77,7 @@ function filterSensitiveInfoFromRepository (repositoryUrl) {
77
77
 
78
78
  return `${protocol}//${hostname}${pathname}`
79
79
  } catch (e) {
80
- return repositoryUrl
80
+ return ''
81
81
  }
82
82
  }
83
83
 
@@ -1,11 +1,13 @@
1
1
  const path = require('path')
2
2
  const fs = require('fs')
3
+ const { URL } = require('url')
4
+ const log = require('../../log')
3
5
 
4
6
  const istanbul = require('istanbul-lib-coverage')
5
7
  const ignore = require('ignore')
6
8
 
7
9
  const { getGitMetadata } = require('./git')
8
- const { getUserProviderGitMetadata } = require('./user-provided-git')
10
+ const { getUserProviderGitMetadata, validateGitRepositoryUrl, validateGitCommitSha } = require('./user-provided-git')
9
11
  const { getCIMetadata } = require('./ci')
10
12
  const { getRuntimeAndOSMetadata } = require('./env')
11
13
  const {
@@ -16,7 +18,8 @@ const {
16
18
  GIT_COMMIT_AUTHOR_EMAIL,
17
19
  GIT_COMMIT_AUTHOR_NAME,
18
20
  GIT_COMMIT_MESSAGE,
19
- CI_WORKSPACE_PATH
21
+ CI_WORKSPACE_PATH,
22
+ CI_PIPELINE_URL
20
23
  } = require('./tags')
21
24
  const id = require('../../id')
22
25
 
@@ -104,7 +107,8 @@ module.exports = {
104
107
  mergeCoverage,
105
108
  fromCoverageMapToCoverage,
106
109
  getTestLineStart,
107
- getCallSites
110
+ getCallSites,
111
+ removeInvalidMetadata
108
112
  }
109
113
 
110
114
  // Returns pkg manager and its version, separated by '-', e.g. npm-8.15.0 or yarn-1.22.19
@@ -116,6 +120,39 @@ function getPkgManager () {
116
120
  }
117
121
  }
118
122
 
123
+ function validateUrl (url) {
124
+ try {
125
+ const urlObject = new URL(url)
126
+ return (urlObject.protocol === 'https:' || urlObject.protocol === 'http:')
127
+ } catch (e) {
128
+ return false
129
+ }
130
+ }
131
+
132
+ function removeInvalidMetadata (metadata) {
133
+ return Object.keys(metadata).reduce((filteredTags, tag) => {
134
+ if (tag === GIT_REPOSITORY_URL) {
135
+ if (!validateGitRepositoryUrl(metadata[GIT_REPOSITORY_URL])) {
136
+ log.error('DD_GIT_REPOSITORY_URL must be a valid URL')
137
+ return filteredTags
138
+ }
139
+ }
140
+ if (tag === GIT_COMMIT_SHA) {
141
+ if (!validateGitCommitSha(metadata[GIT_COMMIT_SHA])) {
142
+ log.error('DD_GIT_COMMIT_SHA must be a full-length git SHA')
143
+ return filteredTags
144
+ }
145
+ }
146
+ if (tag === CI_PIPELINE_URL) {
147
+ if (!validateUrl(metadata[CI_PIPELINE_URL])) {
148
+ return filteredTags
149
+ }
150
+ }
151
+ filteredTags[tag] = metadata[tag]
152
+ return filteredTags
153
+ }, {})
154
+ }
155
+
119
156
  function getTestEnvironmentMetadata (testFramework, config) {
120
157
  // TODO: eventually these will come from the tracer (generally available)
121
158
  const ciMetadata = getCIMetadata()
@@ -155,7 +192,7 @@ function getTestEnvironmentMetadata (testFramework, config) {
155
192
  if (config && config.service) {
156
193
  metadata['service.name'] = config.service
157
194
  }
158
- return metadata
195
+ return removeInvalidMetadata(metadata)
159
196
  }
160
197
 
161
198
  function getTestParametersString (parametersByTestName, testName) {
@@ -173,6 +210,13 @@ function getTestParametersString (parametersByTestName, testName) {
173
210
  }
174
211
  }
175
212
 
213
+ function getTestTypeFromFramework (testFramework) {
214
+ if (testFramework === 'playwright' || testFramework === 'cypress') {
215
+ return 'browser'
216
+ }
217
+ return 'test'
218
+ }
219
+
176
220
  function finishAllTraceSpans (span) {
177
221
  span.context()._trace.started.forEach(traceSpan => {
178
222
  if (traceSpan !== span) {
@@ -188,10 +232,10 @@ function getTestParentSpan (tracer) {
188
232
  })
189
233
  }
190
234
 
191
- function getTestCommonTags (name, suite, version) {
235
+ function getTestCommonTags (name, suite, version, testFramework) {
192
236
  return {
193
237
  [SPAN_TYPE]: 'test',
194
- [TEST_TYPE]: 'test',
238
+ [TEST_TYPE]: getTestTypeFromFramework(testFramework),
195
239
  [SAMPLING_RULE_DECISION]: 1,
196
240
  [SAMPLING_PRIORITY]: AUTO_KEEP,
197
241
  [TEST_NAME]: name,
@@ -269,12 +313,12 @@ function getCodeOwnersForFilename (filename, entries) {
269
313
  return null
270
314
  }
271
315
 
272
- function getTestLevelCommonTags (command, testFrameworkVersion) {
316
+ function getTestLevelCommonTags (command, testFrameworkVersion, testFramework) {
273
317
  return {
274
318
  [TEST_FRAMEWORK_VERSION]: testFrameworkVersion,
275
319
  [LIBRARY_VERSION]: ddTraceVersion,
276
320
  [TEST_COMMAND]: command,
277
- [TEST_TYPE]: 'test'
321
+ [TEST_TYPE]: getTestTypeFromFramework(testFramework)
278
322
  }
279
323
  }
280
324
 
@@ -284,7 +328,7 @@ function getTestSessionCommonTags (command, testFrameworkVersion, testFramework)
284
328
  [RESOURCE_NAME]: `test_session.${command}`,
285
329
  [TEST_MODULE]: testFramework,
286
330
  [TEST_TOOLCHAIN]: getPkgManager(),
287
- ...getTestLevelCommonTags(command, testFrameworkVersion)
331
+ ...getTestLevelCommonTags(command, testFrameworkVersion, testFramework)
288
332
  }
289
333
  }
290
334
 
@@ -293,7 +337,7 @@ function getTestModuleCommonTags (command, testFrameworkVersion, testFramework)
293
337
  [SPAN_TYPE]: 'test_module_end',
294
338
  [RESOURCE_NAME]: `test_module.${command}`,
295
339
  [TEST_MODULE]: testFramework,
296
- ...getTestLevelCommonTags(command, testFrameworkVersion)
340
+ ...getTestLevelCommonTags(command, testFrameworkVersion, testFramework)
297
341
  }
298
342
  }
299
343
 
@@ -303,7 +347,7 @@ function getTestSuiteCommonTags (command, testFrameworkVersion, testSuite, testF
303
347
  [RESOURCE_NAME]: `test_suite.${testSuite}`,
304
348
  [TEST_MODULE]: testFramework,
305
349
  [TEST_SUITE]: testSuite,
306
- ...getTestLevelCommonTags(command, testFrameworkVersion)
350
+ ...getTestLevelCommonTags(command, testFrameworkVersion, testFramework)
307
351
  }
308
352
  }
309
353