dd-trace 4.19.0 → 4.21.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 (62) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/index.d.ts +24 -0
  3. package/package.json +6 -5
  4. package/packages/datadog-instrumentations/src/aerospike.js +47 -0
  5. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  6. package/packages/datadog-instrumentations/src/http/client.js +22 -0
  7. package/packages/datadog-instrumentations/src/jest.js +11 -5
  8. package/packages/datadog-instrumentations/src/kafkajs.js +27 -0
  9. package/packages/datadog-instrumentations/src/next.js +3 -1
  10. package/packages/datadog-instrumentations/src/restify.js +1 -1
  11. package/packages/datadog-plugin-aerospike/src/index.js +113 -0
  12. package/packages/datadog-plugin-http/src/client.js +19 -2
  13. package/packages/datadog-plugin-kafkajs/src/consumer.js +51 -0
  14. package/packages/datadog-plugin-kafkajs/src/producer.js +55 -0
  15. package/packages/datadog-plugin-next/src/index.js +7 -7
  16. package/packages/dd-trace/src/appsec/addresses.js +2 -1
  17. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  18. package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +105 -0
  19. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/constants.js +7 -0
  20. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +12 -19
  21. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/header-sensitive-analyzer.js +20 -0
  22. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +6 -10
  23. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +18 -25
  24. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +79 -85
  25. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +27 -36
  26. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +14 -11
  27. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
  28. package/packages/dd-trace/src/appsec/index.js +14 -2
  29. package/packages/dd-trace/src/appsec/recommended.json +1395 -2
  30. package/packages/dd-trace/src/appsec/reporter.js +19 -0
  31. package/packages/dd-trace/src/appsec/rule_manager.js +9 -6
  32. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +6 -3
  33. package/packages/dd-trace/src/appsec/waf/waf_manager.js +0 -1
  34. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +17 -1
  35. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +75 -56
  36. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +15 -9
  37. package/packages/dd-trace/src/config.js +37 -3
  38. package/packages/dd-trace/src/datastreams/processor.js +107 -12
  39. package/packages/dd-trace/src/id.js +12 -0
  40. package/packages/dd-trace/src/noop/proxy.js +4 -0
  41. package/packages/dd-trace/src/opentracing/propagation/text_map.js +14 -5
  42. package/packages/dd-trace/src/opentracing/span.js +2 -0
  43. package/packages/dd-trace/src/plugins/ci_plugin.js +2 -1
  44. package/packages/dd-trace/src/plugins/index.js +1 -0
  45. package/packages/dd-trace/src/plugins/util/git.js +2 -2
  46. package/packages/dd-trace/src/plugins/util/test.js +3 -2
  47. package/packages/dd-trace/src/profiler.js +5 -3
  48. package/packages/dd-trace/src/profiling/config.js +17 -10
  49. package/packages/dd-trace/src/profiling/profiler.js +10 -4
  50. package/packages/dd-trace/src/profiling/profilers/events.js +171 -73
  51. package/packages/dd-trace/src/profiling/profilers/wall.js +93 -67
  52. package/packages/dd-trace/src/proxy.js +21 -1
  53. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +5 -0
  54. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
  55. package/packages/dd-trace/src/spanleak.js +98 -0
  56. package/packages/dd-trace/src/startup-log.js +7 -1
  57. package/packages/dd-trace/src/telemetry/dependencies.js +55 -9
  58. package/packages/dd-trace/src/telemetry/index.js +135 -43
  59. package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
  60. package/packages/dd-trace/src/telemetry/send-data.js +47 -5
  61. package/packages/dd-trace/src/tracer.js +4 -0
  62. package/scripts/install_plugin_modules.js +11 -3
@@ -30,6 +30,7 @@ require,opentracing,MIT,Copyright 2016 Resonance Labs Inc
30
30
  require,path-to-regexp,MIT,Copyright 2014 Blake Embrey
31
31
  require,pprof-format,MIT,Copyright 2022 Stephen Belanger
32
32
  require,protobufjs,BSD-3-Clause,Copyright 2016 Daniel Wirtz
33
+ require,tlhunter-sorted-set,MIT,Copyright (c) 2023 Datadog Inc.
33
34
  require,retry,MIT,Copyright 2011 Tim Koschützki Felix Geisendörfer
34
35
  require,semver,ISC,Copyright Isaac Z. Schlueter and Contributors
35
36
  dev,@types/node,MIT,Copyright Authors
package/index.d.ts CHANGED
@@ -578,6 +578,22 @@ export declare interface TracerOptions {
578
578
  * @default 'safe'
579
579
  */
580
580
  mode?: 'safe' | 'extended' | 'disabled'
581
+ },
582
+
583
+ /**
584
+ * Configuration for Api Security sampling
585
+ */
586
+ apiSecurity?: {
587
+ /** Whether to enable Api Security.
588
+ * @default false
589
+ */
590
+ enabled?: boolean,
591
+
592
+ /** Controls the request sampling rate (between 0 and 1) in which Api Security is triggered.
593
+ * The value will be coerced back if it's outside of the 0-1 range.
594
+ * @default 0.1
595
+ */
596
+ requestSampling?: number
581
597
  }
582
598
  };
583
599
 
@@ -927,6 +943,14 @@ declare namespace plugins {
927
943
  * @default code => code < 500
928
944
  */
929
945
  validateStatus?: (code: number) => boolean;
946
+
947
+ /**
948
+ * Enable injection of tracing headers into requests signed with AWS IAM headers.
949
+ * Disable this if you get AWS signature errors (HTTP 403).
950
+ *
951
+ * @default false
952
+ */
953
+ enablePropagationWithAmazonHeaders?: boolean;
930
954
  }
931
955
 
932
956
  /** @hidden */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "4.19.0",
3
+ "version": "4.21.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -68,11 +68,11 @@
68
68
  "node": ">=16"
69
69
  },
70
70
  "dependencies": {
71
- "@datadog/native-appsec": "4.0.0",
71
+ "@datadog/native-appsec": "5.0.0",
72
72
  "@datadog/native-iast-rewriter": "2.2.1",
73
73
  "@datadog/native-iast-taint-tracking": "1.6.4",
74
74
  "@datadog/native-metrics": "^2.0.0",
75
- "@datadog/pprof": "4.0.1",
75
+ "@datadog/pprof": "4.1.0",
76
76
  "@datadog/sketches-js": "^2.1.0",
77
77
  "@opentelemetry/api": "^1.0.0",
78
78
  "@opentelemetry/core": "^1.14.0",
@@ -97,8 +97,9 @@
97
97
  "node-abort-controller": "^3.1.1",
98
98
  "opentracing": ">=0.12.1",
99
99
  "path-to-regexp": "^0.1.2",
100
- "pprof-format": "^2.0.7",
101
- "protobufjs": "^7.2.4",
100
+ "pprof-format": "^2.0.7",
101
+ "protobufjs": "^7.2.5",
102
+ "tlhunter-sorted-set": "^0.1.0",
102
103
  "retry": "^0.13.1",
103
104
  "semver": "^7.5.4"
104
105
  },
@@ -0,0 +1,47 @@
1
+ 'use strict'
2
+
3
+ const {
4
+ addHook
5
+ } = require('./helpers/instrument')
6
+ const shimmer = require('../../datadog-shimmer')
7
+
8
+ const tracingChannel = require('dc-polyfill').tracingChannel
9
+ const ch = tracingChannel('apm:aerospike:command')
10
+
11
+ function wrapCreateCommand (createCommand) {
12
+ if (typeof createCommand !== 'function') return createCommand
13
+
14
+ return function commandWithTrace () {
15
+ const CommandClass = createCommand.apply(this, arguments)
16
+
17
+ if (!CommandClass) return CommandClass
18
+
19
+ shimmer.wrap(CommandClass.prototype, 'process', wrapProcess)
20
+
21
+ return CommandClass
22
+ }
23
+ }
24
+
25
+ function wrapProcess (process) {
26
+ return function (...args) {
27
+ const cb = args[0]
28
+ if (typeof cb !== 'function') return process.apply(this, args)
29
+
30
+ const ctx = {
31
+ commandName: this.constructor.name,
32
+ commandArgs: this.args,
33
+ clientConfig: this.client.config
34
+ }
35
+
36
+ return ch.traceCallback(process, -1, ctx, this, ...args)
37
+ }
38
+ }
39
+
40
+ addHook({
41
+ name: 'aerospike',
42
+ file: 'lib/commands/command.js',
43
+ versions: ['^3.16.2', '4', '5']
44
+ },
45
+ commandFactory => {
46
+ return shimmer.wrap(commandFactory, wrapCreateCommand(commandFactory))
47
+ })
@@ -20,6 +20,7 @@ module.exports = {
20
20
  '@opentelemetry/sdk-trace-node': () => require('../otel-sdk-trace'),
21
21
  '@redis/client': () => require('../redis'),
22
22
  '@smithy/smithy-client': () => require('../aws-sdk'),
23
+ 'aerospike': () => require('../aerospike'),
23
24
  'amqp10': () => require('../amqp10'),
24
25
  'amqplib': () => require('../amqplib'),
25
26
  'aws-sdk': () => require('../aws-sdk'),
@@ -58,6 +58,7 @@ function patch (http, methodName) {
58
58
  }
59
59
 
60
60
  const options = args.options
61
+
61
62
  const finish = () => {
62
63
  if (!finished) {
63
64
  finished = true
@@ -69,8 +70,28 @@ function patch (http, methodName) {
69
70
  const req = request.call(this, options, callback)
70
71
  const emit = req.emit
71
72
 
73
+ const requestSetTimeout = req.setTimeout
74
+
72
75
  ctx.req = req
73
76
 
77
+ // tracked to accurately discern custom request socket timeout
78
+ let customRequestTimeout = false
79
+
80
+ req.setTimeout = function () {
81
+ customRequestTimeout = true
82
+ return requestSetTimeout.apply(this, arguments)
83
+ }
84
+
85
+ req.on('socket', socket => {
86
+ if (socket) {
87
+ const socketSetTimeout = socket.setTimeout
88
+ socket.setTimeout = function () {
89
+ customRequestTimeout = true
90
+ return socketSetTimeout.apply(this, arguments)
91
+ }
92
+ }
93
+ })
94
+
74
95
  req.emit = function (eventName, arg) {
75
96
  switch (eventName) {
76
97
  case 'response': {
@@ -88,6 +109,7 @@ function patch (http, methodName) {
88
109
  case 'error':
89
110
  case 'timeout':
90
111
  ctx.error = arg
112
+ ctx.customRequestTimeout = customRequestTimeout
91
113
  errorChannel.publish(ctx)
92
114
  case 'abort': // deprecated and replaced by `close` in node 17
93
115
  case 'close':
@@ -44,6 +44,7 @@ const itrSkippedSuitesCh = channel('ci:jest:itr:skipped-suites')
44
44
  let skippableSuites = []
45
45
  let isCodeCoverageEnabled = false
46
46
  let isSuitesSkippingEnabled = false
47
+ let isUserCodeCoverageEnabled = false
47
48
  let isSuitesSkipped = false
48
49
  let numSkippedSuites = 0
49
50
  let hasUnskippableSuites = false
@@ -289,11 +290,14 @@ function cliWrapper (cli, jestVersion) {
289
290
  } = result
290
291
 
291
292
  let testCodeCoverageLinesTotal
292
- try {
293
- const { pct, total } = coverageMap.getCoverageSummary().lines
294
- testCodeCoverageLinesTotal = total !== 0 ? pct : 0
295
- } catch (e) {
296
- // ignore errors
293
+
294
+ if (isUserCodeCoverageEnabled) {
295
+ try {
296
+ const { pct, total } = coverageMap.getCoverageSummary().lines
297
+ testCodeCoverageLinesTotal = total !== 0 ? pct : 0
298
+ } catch (e) {
299
+ // ignore errors
300
+ }
297
301
  }
298
302
  let status, error
299
303
 
@@ -436,6 +440,8 @@ function configureTestEnvironment (readConfigsResult) {
436
440
  config.testEnvironmentOptions._ddTestCodeCoverageEnabled = isCodeCoverageEnabled
437
441
  })
438
442
 
443
+ isUserCodeCoverageEnabled = !!readConfigsResult.globalConfig.collectCoverage
444
+
439
445
  if (isCodeCoverageEnabled) {
440
446
  const globalConfig = {
441
447
  ...readConfigsResult.globalConfig,
@@ -8,13 +8,31 @@ const {
8
8
  const shimmer = require('../../datadog-shimmer')
9
9
 
10
10
  const producerStartCh = channel('apm:kafkajs:produce:start')
11
+ const producerCommitCh = channel('apm:kafkajs:produce:commit')
11
12
  const producerFinishCh = channel('apm:kafkajs:produce:finish')
12
13
  const producerErrorCh = channel('apm:kafkajs:produce:error')
13
14
 
14
15
  const consumerStartCh = channel('apm:kafkajs:consume:start')
16
+ const consumerCommitCh = channel('apm:kafkajs:consume:commit')
15
17
  const consumerFinishCh = channel('apm:kafkajs:consume:finish')
16
18
  const consumerErrorCh = channel('apm:kafkajs:consume:error')
17
19
 
20
+ function commitsFromEvent (event) {
21
+ const { payload: { groupId, topics } } = event
22
+ const commitList = []
23
+ for (const { topic, partitions } of topics) {
24
+ for (const { partition, offset } of partitions) {
25
+ commitList.push({
26
+ groupId,
27
+ partition,
28
+ offset,
29
+ topic
30
+ })
31
+ }
32
+ }
33
+ consumerCommitCh.publish(commitList)
34
+ }
35
+
18
36
  addHook({ name: 'kafkajs', file: 'src/index.js', versions: ['>=1.4'] }, (BaseKafka) => {
19
37
  class Kafka extends BaseKafka {
20
38
  constructor (options) {
@@ -58,6 +76,12 @@ addHook({ name: 'kafkajs', file: 'src/index.js', versions: ['>=1.4'] }, (BaseKaf
58
76
  })
59
77
  )
60
78
 
79
+ result.then(res => {
80
+ if (producerCommitCh.hasSubscribers) {
81
+ producerCommitCh.publish(res)
82
+ }
83
+ })
84
+
61
85
  return result
62
86
  } catch (e) {
63
87
  producerErrorCh.publish(e)
@@ -75,6 +99,9 @@ addHook({ name: 'kafkajs', file: 'src/index.js', versions: ['>=1.4'] }, (BaseKaf
75
99
  }
76
100
 
77
101
  const consumer = createConsumer.apply(this, arguments)
102
+
103
+ consumer.on(consumer.events.COMMIT_OFFSETS, commitsFromEvent)
104
+
78
105
  const run = consumer.run
79
106
 
80
107
  const groupId = arguments[0].groupId
@@ -144,7 +144,9 @@ function instrument (req, res, handler) {
144
144
  function wrapServeStatic (serveStatic) {
145
145
  return function (req, res, path) {
146
146
  return instrument(req, res, () => {
147
- if (pageLoadChannel.hasSubscribers && path) pageLoadChannel.publish({ page: path })
147
+ if (pageLoadChannel.hasSubscribers && path) {
148
+ pageLoadChannel.publish({ page: path, isStatic: true })
149
+ }
148
150
 
149
151
  return serveStatic.apply(this, arguments)
150
152
  })
@@ -55,7 +55,7 @@ function wrapFn (fn) {
55
55
  return result.then(function () {
56
56
  nextChannel.publish({ req })
57
57
  finishChannel.publish({ req })
58
- return arguments
58
+ return arguments[0]
59
59
  }).catch(function (error) {
60
60
  errorChannel.publish({ req, error })
61
61
  nextChannel.publish({ req })
@@ -0,0 +1,113 @@
1
+ 'use strict'
2
+
3
+ const { storage } = require('../../datadog-core')
4
+ const DatabasePlugin = require('../../dd-trace/src/plugins/database')
5
+
6
+ const AEROSPIKE_PEER_SERVICE = 'aerospike.namespace'
7
+
8
+ class AerospikePlugin extends DatabasePlugin {
9
+ static get id () { return 'aerospike' }
10
+ static get operation () { return 'command' }
11
+ static get system () { return 'aerospike' }
12
+ static get prefix () {
13
+ return 'tracing:apm:aerospike:command'
14
+ }
15
+
16
+ static get peerServicePrecursors () {
17
+ return [AEROSPIKE_PEER_SERVICE]
18
+ }
19
+
20
+ bindStart (ctx) {
21
+ const { commandName, commandArgs } = ctx
22
+ const resourceName = commandName.slice(0, commandName.indexOf('Command'))
23
+ const store = storage.getStore()
24
+ const childOf = store ? store.span : null
25
+ const meta = getMeta(resourceName, commandArgs)
26
+
27
+ const span = this.startSpan(this.operationName(), {
28
+ childOf,
29
+ service: this.serviceName({ pluginConfig: this.config }),
30
+ type: 'aerospike',
31
+ kind: 'client',
32
+ resource: resourceName,
33
+ meta
34
+ }, false)
35
+
36
+ ctx.parentStore = store
37
+ ctx.currentStore = { ...store, span }
38
+
39
+ return ctx.currentStore
40
+ }
41
+
42
+ bindAsyncStart (ctx) {
43
+ if (ctx.currentStore) {
44
+ // have to manually trigger peer service calculation when using tracing channel
45
+ this.tagPeerService(ctx.currentStore.span)
46
+ ctx.currentStore.span.finish()
47
+ }
48
+ return ctx.parentStore
49
+ }
50
+
51
+ end (ctx) {
52
+ if (ctx.result) {
53
+ // have to manually trigger peer service calculation when using tracing channel
54
+ this.tagPeerService(ctx.currentStore.span)
55
+ ctx.currentStore.span.finish()
56
+ }
57
+ }
58
+
59
+ error (ctx) {
60
+ if (ctx.error) {
61
+ const error = ctx.error
62
+ const span = ctx.currentStore.span
63
+ span.setTag('error', error)
64
+ }
65
+ }
66
+ }
67
+
68
+ function getMeta (resourceName, commandArgs) {
69
+ let meta = {}
70
+ if (resourceName.includes('Index')) {
71
+ const [ns, set, bin, index] = commandArgs
72
+ meta = getMetaForIndex(ns, set, bin, index)
73
+ } else if (resourceName === 'Query') {
74
+ const { ns, set } = commandArgs[2]
75
+ meta = getMetaForQuery({ ns, set })
76
+ } else if (isKeyObject(commandArgs[0])) {
77
+ const { ns, set, key } = commandArgs[0]
78
+ meta = getMetaForKey(ns, set, key)
79
+ }
80
+ return meta
81
+ }
82
+
83
+ function getMetaForIndex (ns, set, bin, index) {
84
+ return {
85
+ [AEROSPIKE_PEER_SERVICE]: ns,
86
+ 'aerospike.setname': set,
87
+ 'aerospike.bin': bin,
88
+ 'aerospike.index': index
89
+ }
90
+ }
91
+
92
+ function getMetaForKey (ns, set, key) {
93
+ return {
94
+ 'aerospike.key': `${ns}:${set}:${key}`,
95
+ [AEROSPIKE_PEER_SERVICE]: ns,
96
+ 'aerospike.setname': set,
97
+ 'aerospike.userkey': key
98
+ }
99
+ }
100
+
101
+ function getMetaForQuery (queryObj) {
102
+ const { ns, set } = queryObj
103
+ return {
104
+ [AEROSPIKE_PEER_SERVICE]: ns,
105
+ 'aerospike.setname': set
106
+ }
107
+ }
108
+
109
+ function isKeyObject (obj) {
110
+ return obj && obj.ns !== undefined && obj.set !== undefined && obj.key !== undefined
111
+ }
112
+
113
+ module.exports = AerospikePlugin
@@ -58,7 +58,7 @@ class HttpClientPlugin extends ClientPlugin {
58
58
  span._spanContext._trace.record = false
59
59
  }
60
60
 
61
- if (!(hasAmazonSignature(options) || !this.config.propagationFilter(uri))) {
61
+ if (this.shouldInjectTraceHeaders(options, uri)) {
62
62
  this.tracer.inject(span, HTTP_HEADERS, options.headers)
63
63
  }
64
64
 
@@ -71,6 +71,18 @@ class HttpClientPlugin extends ClientPlugin {
71
71
  return message.currentStore
72
72
  }
73
73
 
74
+ shouldInjectTraceHeaders (options, uri) {
75
+ if (hasAmazonSignature(options) && !this.config.enablePropagationWithAmazonHeaders) {
76
+ return false
77
+ }
78
+
79
+ if (!this.config.propagationFilter(uri)) {
80
+ return false
81
+ }
82
+
83
+ return true
84
+ }
85
+
74
86
  bindAsyncStart ({ parentStore }) {
75
87
  return parentStore
76
88
  }
@@ -98,7 +110,7 @@ class HttpClientPlugin extends ClientPlugin {
98
110
  span.finish()
99
111
  }
100
112
 
101
- error ({ span, error }) {
113
+ error ({ span, error, args, customRequestTimeout }) {
102
114
  if (!span) return
103
115
  if (error) {
104
116
  span.addTags({
@@ -107,6 +119,11 @@ class HttpClientPlugin extends ClientPlugin {
107
119
  [ERROR_STACK]: error.stack
108
120
  })
109
121
  } else {
122
+ // conditions for no error:
123
+ // 1. not using a custom agent instance with custom timeout specified
124
+ // 2. no invocation of `req.setTimeout` or `socket.setTimeout`
125
+ if (!args.options.agent?.options.timeout && !customRequestTimeout) return
126
+
110
127
  span.setTag('error', 1)
111
128
  }
112
129
  }
@@ -7,6 +7,57 @@ class KafkajsConsumerPlugin extends ConsumerPlugin {
7
7
  static get id () { return 'kafkajs' }
8
8
  static get operation () { return 'consume' }
9
9
 
10
+ constructor () {
11
+ super(...arguments)
12
+ this.addSub('apm:kafkajs:consume:commit', message => this.commit(message))
13
+ }
14
+
15
+ /**
16
+ * Transform individual commit details sent by kafkajs' event reporter
17
+ * into actionable backlog items for DSM
18
+ *
19
+ * @typedef {object} ConsumerBacklog
20
+ * @property {number} type
21
+ * @property {string} consumer_group
22
+ * @property {string} topic
23
+ * @property {number} partition
24
+ * @property {number} offset
25
+ *
26
+ * @typedef {object} CommitEventItem
27
+ * @property {string} groupId
28
+ * @property {string} topic
29
+ * @property {number} partition
30
+ * @property {import('kafkajs/utils/long').Long} offset
31
+ *
32
+ * @param {CommitEventItem} commit
33
+ * @returns {ConsumerBacklog}
34
+ */
35
+ transformCommit (commit) {
36
+ const { groupId, partition, offset, topic } = commit
37
+ return {
38
+ partition,
39
+ topic,
40
+ type: 'kafka_commit',
41
+ offset: Number(offset),
42
+ consumer_group: groupId
43
+ }
44
+ }
45
+
46
+ commit (commitList) {
47
+ if (!this.config.dsmEnabled) return
48
+ const keys = [
49
+ 'consumer_group',
50
+ 'type',
51
+ 'partition',
52
+ 'offset',
53
+ 'topic'
54
+ ]
55
+ for (const commit of commitList.map(this.transformCommit)) {
56
+ if (keys.some(key => !commit.hasOwnProperty(key))) continue
57
+ this.tracer.setOffset(commit)
58
+ }
59
+ }
60
+
10
61
  start ({ topic, partition, message, groupId }) {
11
62
  const childOf = extract(this.tracer, message.headers)
12
63
  const span = this.startSpan({
@@ -11,6 +11,61 @@ class KafkajsProducerPlugin extends ProducerPlugin {
11
11
  static get operation () { return 'produce' }
12
12
  static get peerServicePrecursors () { return [BOOTSTRAP_SERVERS_KEY] }
13
13
 
14
+ constructor () {
15
+ super(...arguments)
16
+ this.addSub('apm:kafkajs:produce:commit', message => this.commit(message))
17
+ }
18
+
19
+ /**
20
+ * Transform individual commit details sent by kafkajs' event reporter
21
+ * into actionable backlog items for DSM
22
+ *
23
+ * @typedef {object} ProducerBacklog
24
+ * @property {number} type
25
+ * @property {string} topic
26
+ * @property {number} partition
27
+ * @property {number} offset
28
+ *
29
+ * @typedef {object} ProducerResponseItem
30
+ * @property {string} topic
31
+ * @property {number} partition
32
+ * @property {import('kafkajs/utils/long').Long} [offset]
33
+ * @property {import('kafkajs/utils/long').Long} [baseOffset]
34
+ *
35
+ * @param {ProducerResponseItem} response
36
+ * @returns {ProducerBacklog}
37
+ */
38
+ transformProduceResponse (response) {
39
+ // In produce protocol >=v3, the offset key changes from `offset` to `baseOffset`
40
+ const { topicName: topic, partition, offset, baseOffset } = response
41
+ const offsetAsLong = offset || baseOffset
42
+ return {
43
+ type: 'kafka_produce',
44
+ partition,
45
+ offset: offsetAsLong ? Number(offsetAsLong) : undefined,
46
+ topic
47
+ }
48
+ }
49
+
50
+ /**
51
+ *
52
+ * @param {ProducerResponseItem[]} commitList
53
+ * @returns {void}
54
+ */
55
+ commit (commitList) {
56
+ if (!this.config.dsmEnabled) return
57
+ const keys = [
58
+ 'type',
59
+ 'partition',
60
+ 'offset',
61
+ 'topic'
62
+ ]
63
+ for (const commit of commitList.map(this.transformProduceResponse)) {
64
+ if (keys.some(key => !commit.hasOwnProperty(key))) continue
65
+ this.tracer.setOffset(commit)
66
+ }
67
+ }
68
+
14
69
  start ({ topic, messages, bootstrapServers }) {
15
70
  let pathwayCtx
16
71
  const span = this.startSpan({
@@ -65,7 +65,7 @@ class NextPlugin extends ServerPlugin {
65
65
  span.finish()
66
66
  }
67
67
 
68
- pageLoad ({ page, isAppPath = false }) {
68
+ pageLoad ({ page, isAppPath = false, isStatic = false }) {
69
69
  const store = storage.getStore()
70
70
 
71
71
  if (!store) return
@@ -82,12 +82,12 @@ class NextPlugin extends ServerPlugin {
82
82
  // remove ending /route or /page for appDir projects
83
83
  if (isAppPath) page = page.substring(0, page.lastIndexOf('/'))
84
84
 
85
- // This is for static files whose 'page' includes the whole file path
86
- // For normal page matches, like /api/hello/[name] and a req.url like /api/hello/world,
87
- // nothing should happen
88
- // For page matches like /User/something/public/text.txt and req.url like /text.txt,
89
- // it should disregard the extra absolute path Next.js sometimes sets
90
- if (page.includes(req.url)) page = req.url
85
+ // handle static resource
86
+ if (isStatic) {
87
+ page = req.url.includes('_next/static')
88
+ ? '/_next/static/*'
89
+ : '/public/*'
90
+ }
91
91
 
92
92
  span.addTags({
93
93
  [COMPONENT]: this.constructor.id,
@@ -16,5 +16,6 @@ module.exports = {
16
16
 
17
17
  HTTP_CLIENT_IP: 'http.client_ip',
18
18
 
19
- USER_ID: 'usr.id'
19
+ USER_ID: 'usr.id',
20
+ WAF_CONTEXT_PROCESSOR: 'waf.context.processor'
20
21
  }
@@ -3,6 +3,7 @@
3
3
  module.exports = {
4
4
  'COMMAND_INJECTION_ANALYZER': require('./command-injection-analyzer'),
5
5
  'HARCODED_SECRET_ANALYZER': require('./hardcoded-secret-analyzer'),
6
+ 'HEADER_INJECTION_ANALYZER': require('./header-injection-analyzer'),
6
7
  'HSTS_HEADER_MISSING_ANALYZER': require('./hsts-header-missing-analyzer'),
7
8
  'INSECURE_COOKIE_ANALYZER': require('./insecure-cookie-analyzer'),
8
9
  'LDAP_ANALYZER': require('./ldap-injection-analyzer'),