dd-trace 3.41.0 → 3.43.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 (76) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/index.d.ts +29 -0
  3. package/package.json +7 -6
  4. package/packages/datadog-instrumentations/src/aerospike.js +47 -0
  5. package/packages/datadog-instrumentations/src/apollo-server-core.js +41 -0
  6. package/packages/datadog-instrumentations/src/apollo-server.js +83 -0
  7. package/packages/datadog-instrumentations/src/graphql.js +18 -4
  8. package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
  9. package/packages/datadog-instrumentations/src/http/client.js +10 -0
  10. package/packages/datadog-instrumentations/src/jest.js +11 -5
  11. package/packages/datadog-instrumentations/src/kafkajs.js +27 -0
  12. package/packages/datadog-instrumentations/src/next.js +18 -6
  13. package/packages/datadog-instrumentations/src/restify.js +1 -1
  14. package/packages/datadog-instrumentations/src/rhea.js +15 -9
  15. package/packages/datadog-plugin-aerospike/src/index.js +113 -0
  16. package/packages/datadog-plugin-graphql/src/resolve.js +26 -18
  17. package/packages/datadog-plugin-http/src/client.js +19 -2
  18. package/packages/datadog-plugin-kafkajs/src/consumer.js +51 -0
  19. package/packages/datadog-plugin-kafkajs/src/producer.js +55 -0
  20. package/packages/datadog-plugin-next/src/index.js +40 -14
  21. package/packages/dd-trace/src/appsec/activation.js +29 -0
  22. package/packages/dd-trace/src/appsec/addresses.js +3 -1
  23. package/packages/dd-trace/src/appsec/api_security_sampler.js +48 -0
  24. package/packages/dd-trace/src/appsec/blocked_templates.js +4 -1
  25. package/packages/dd-trace/src/appsec/blocking.js +95 -43
  26. package/packages/dd-trace/src/appsec/channels.js +4 -1
  27. package/packages/dd-trace/src/appsec/graphql.js +146 -0
  28. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  29. package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +105 -0
  30. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/constants.js +7 -0
  31. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +12 -19
  32. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/header-sensitive-analyzer.js +20 -0
  33. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +6 -10
  34. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +18 -25
  35. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +79 -85
  36. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +27 -36
  37. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +14 -11
  38. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
  39. package/packages/dd-trace/src/appsec/index.js +32 -31
  40. package/packages/dd-trace/src/appsec/recommended.json +1395 -2
  41. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
  42. package/packages/dd-trace/src/appsec/remote_config/index.js +36 -15
  43. package/packages/dd-trace/src/appsec/reporter.js +19 -0
  44. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  45. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +28 -13
  46. package/packages/dd-trace/src/appsec/waf/waf_manager.js +0 -1
  47. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +17 -1
  48. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +75 -56
  49. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +15 -9
  50. package/packages/dd-trace/src/config.js +36 -2
  51. package/packages/dd-trace/src/datastreams/processor.js +107 -12
  52. package/packages/dd-trace/src/noop/proxy.js +4 -0
  53. package/packages/dd-trace/src/opentracing/span.js +2 -0
  54. package/packages/dd-trace/src/plugins/ci_plugin.js +2 -1
  55. package/packages/dd-trace/src/plugins/index.js +1 -0
  56. package/packages/dd-trace/src/plugins/util/git.js +2 -2
  57. package/packages/dd-trace/src/plugins/util/test.js +3 -2
  58. package/packages/dd-trace/src/plugins/util/user-provided-git.js +3 -2
  59. package/packages/dd-trace/src/profiler.js +5 -3
  60. package/packages/dd-trace/src/profiling/config.js +8 -0
  61. package/packages/dd-trace/src/profiling/profiler.js +17 -10
  62. package/packages/dd-trace/src/profiling/profilers/events.js +181 -83
  63. package/packages/dd-trace/src/profiling/profilers/shared.js +33 -3
  64. package/packages/dd-trace/src/profiling/profilers/space.js +2 -1
  65. package/packages/dd-trace/src/profiling/profilers/wall.js +17 -12
  66. package/packages/dd-trace/src/proxy.js +25 -1
  67. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +5 -0
  68. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
  69. package/packages/dd-trace/src/spanleak.js +98 -0
  70. package/packages/dd-trace/src/startup-log.js +7 -1
  71. package/packages/dd-trace/src/telemetry/dependencies.js +55 -9
  72. package/packages/dd-trace/src/telemetry/index.js +135 -43
  73. package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
  74. package/packages/dd-trace/src/telemetry/send-data.js +47 -5
  75. package/packages/dd-trace/src/tracer.js +4 -0
  76. package/scripts/install_plugin_modules.js +11 -3
@@ -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
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
4
+ const dc = require('dc-polyfill')
4
5
 
5
6
  const collapsedPathSym = Symbol('collapsedPaths')
6
7
 
@@ -14,8 +15,6 @@ class GraphQLResolvePlugin extends TracingPlugin {
14
15
  if (!shouldInstrument(this.config, path)) return
15
16
  const computedPathString = path.join('.')
16
17
 
17
- addResolver(context, info, args)
18
-
19
18
  if (this.config.collapse) {
20
19
  if (!context[collapsedPathSym]) {
21
20
  context[collapsedPathSym] = {}
@@ -55,6 +54,10 @@ class GraphQLResolvePlugin extends TracingPlugin {
55
54
  span.setTag(`graphql.variables.${name}`, variables[name])
56
55
  })
57
56
  }
57
+
58
+ if (this.resolverStartCh.hasSubscribers) {
59
+ this.resolverStartCh.publish({ context, resolverInfo: getResolverInfo(info, args) })
60
+ }
58
61
  }
59
62
 
60
63
  constructor (...args) {
@@ -69,6 +72,8 @@ class GraphQLResolvePlugin extends TracingPlugin {
69
72
  field.finishTime = span._getTime ? span._getTime() : 0
70
73
  field.error = field.error || err
71
74
  })
75
+
76
+ this.resolverStartCh = dc.channel('datadog:graphql:resolver:start')
72
77
  }
73
78
 
74
79
  configure (config) {
@@ -109,28 +114,31 @@ function withCollapse (responsePathAsArray) {
109
114
  }
110
115
  }
111
116
 
112
- function addResolver (context, info, args) {
113
- if (info.rootValue && !info.rootValue[info.fieldName]) {
114
- return
115
- }
117
+ function getResolverInfo (info, args) {
118
+ let resolverInfo = null
119
+ const resolverVars = {}
116
120
 
117
- if (!context.resolvers) {
118
- context.resolvers = {}
121
+ if (args && Object.keys(args).length) {
122
+ Object.assign(resolverVars, args)
119
123
  }
120
124
 
121
- const resolvers = context.resolvers
122
-
123
- if (!resolvers[info.fieldName]) {
124
- if (args && Object.keys(args).length) {
125
- resolvers[info.fieldName] = [args]
126
- } else {
127
- resolvers[info.fieldName] = []
125
+ const directives = info.fieldNodes[0].directives
126
+ for (const directive of directives) {
127
+ const argList = {}
128
+ for (const argument of directive['arguments']) {
129
+ argList[argument.name.value] = argument.value.value
128
130
  }
129
- } else {
130
- if (args && Object.keys(args).length) {
131
- resolvers[info.fieldName].push(args)
131
+
132
+ if (Object.keys(argList).length) {
133
+ resolverVars[directive.name.value] = argList
132
134
  }
133
135
  }
136
+
137
+ if (Object.keys(resolverVars).length) {
138
+ resolverInfo = { [info.fieldName]: resolverVars }
139
+ }
140
+
141
+ return resolverInfo
134
142
  }
135
143
 
136
144
  module.exports = GraphQLResolvePlugin
@@ -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`
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({
@@ -6,6 +6,8 @@ const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
6
6
  const { COMPONENT } = require('../../dd-trace/src/constants')
7
7
  const web = require('../../dd-trace/src/plugins/util/web')
8
8
 
9
+ const errorPages = ['/404', '/500', '/_error', '/_not-found']
10
+
9
11
  class NextPlugin extends ServerPlugin {
10
12
  static get id () {
11
13
  return 'next'
@@ -40,6 +42,13 @@ class NextPlugin extends ServerPlugin {
40
42
  }
41
43
 
42
44
  error ({ span, error }) {
45
+ if (!span) {
46
+ const store = storage.getStore()
47
+ if (!store) return
48
+
49
+ span = store.span
50
+ }
51
+
43
52
  this.addError(error, span)
44
53
  }
45
54
 
@@ -50,10 +59,20 @@ class NextPlugin extends ServerPlugin {
50
59
 
51
60
  const span = store.span
52
61
  const error = span.context()._tags['error']
53
-
54
- if (!this.config.validateStatus(res.statusCode) && !error) {
55
- span.setTag('error', req.error || nextRequest.error || true)
56
- web.addError(req, req.error || nextRequest.error || true)
62
+ const requestError = req.error || nextRequest.error
63
+
64
+ if (requestError) {
65
+ // prioritize user-set errors from API routes
66
+ span.setTag('error', requestError)
67
+ web.addError(req, requestError)
68
+ } else if (error) {
69
+ // general error handling
70
+ span.setTag('error', error)
71
+ web.addError(req, requestError || error)
72
+ } else if (!this.config.validateStatus(res.statusCode)) {
73
+ // where there's no error, we still need to validate status
74
+ span.setTag('error', true)
75
+ web.addError(req, true)
57
76
  }
58
77
 
59
78
  span.addTags({
@@ -65,7 +84,7 @@ class NextPlugin extends ServerPlugin {
65
84
  span.finish()
66
85
  }
67
86
 
68
- pageLoad ({ page, isAppPath = false }) {
87
+ pageLoad ({ page, isAppPath = false, isStatic = false }) {
69
88
  const store = storage.getStore()
70
89
 
71
90
  if (!store) return
@@ -73,21 +92,28 @@ class NextPlugin extends ServerPlugin {
73
92
  const span = store.span
74
93
  const req = this._requests.get(span)
75
94
 
95
+ // safeguard against missing req in complicated timeout scenarios
96
+ if (!req) return
97
+
76
98
  // Only use error page names if there's not already a name
77
99
  const current = span.context()._tags['next.page']
78
- if (current && ['/404', '/500', '/_error', '/_not-found'].includes(page)) {
100
+ const isErrorPage = errorPages.includes(page)
101
+
102
+ if (current && isErrorPage) {
79
103
  return
80
104
  }
81
105
 
82
106
  // remove ending /route or /page for appDir projects
83
- if (isAppPath) page = page.substring(0, page.lastIndexOf('/'))
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
107
+ // need to check if not an error page too, as those are marked as app directory
108
+ // in newer versions
109
+ if (isAppPath && !isErrorPage) page = page.substring(0, page.lastIndexOf('/'))
110
+
111
+ // handle static resource
112
+ if (isStatic) {
113
+ page = req.url.includes('_next/static')
114
+ ? '/_next/static/*'
115
+ : '/public/*'
116
+ }
91
117
 
92
118
  span.addTags({
93
119
  [COMPONENT]: this.constructor.id,
@@ -0,0 +1,29 @@
1
+ 'use strict'
2
+
3
+ const Activation = {
4
+ ONECLICK: 'OneClick',
5
+ ENABLED: 'Enabled',
6
+ DISABLED: 'Disabled',
7
+
8
+ fromConfig (config) {
9
+ switch (config.appsec.enabled) {
10
+ // ASM is activated by an env var DD_APPSEC_ENABLED=true
11
+ case true:
12
+ return Activation.ENABLED
13
+
14
+ // ASM is disabled by an env var DD_APPSEC_ENABLED=false
15
+ case false:
16
+ return Activation.DISABLED
17
+
18
+ // ASM is activated by one click remote config
19
+ case undefined:
20
+ return Activation.ONECLICK
21
+
22
+ // Any other value should never occur
23
+ default:
24
+ return Activation.DISABLED
25
+ }
26
+ }
27
+ }
28
+
29
+ module.exports = Activation
@@ -13,8 +13,10 @@ module.exports = {
13
13
  HTTP_INCOMING_RESPONSE_HEADERS: 'server.response.headers.no_cookies',
14
14
  // TODO: 'server.response.trailers',
15
15
  HTTP_INCOMING_GRAPHQL_RESOLVERS: 'graphql.server.all_resolvers',
16
+ HTTP_INCOMING_GRAPHQL_RESOLVER: 'graphql.server.resolver',
16
17
 
17
18
  HTTP_CLIENT_IP: 'http.client_ip',
18
19
 
19
- USER_ID: 'usr.id'
20
+ USER_ID: 'usr.id',
21
+ WAF_CONTEXT_PROCESSOR: 'waf.context.processor'
20
22
  }
@@ -0,0 +1,48 @@
1
+ 'use strict'
2
+
3
+ const log = require('../log')
4
+
5
+ let enabled
6
+ let requestSampling
7
+
8
+ function configure ({ apiSecurity }) {
9
+ enabled = apiSecurity.enabled
10
+ setRequestSampling(apiSecurity.requestSampling)
11
+ }
12
+
13
+ function disable () {
14
+ enabled = false
15
+ }
16
+
17
+ function setRequestSampling (sampling) {
18
+ requestSampling = parseRequestSampling(sampling)
19
+ }
20
+
21
+ function parseRequestSampling (requestSampling) {
22
+ let parsed = parseFloat(requestSampling)
23
+
24
+ if (isNaN(parsed)) {
25
+ log.warn(`Incorrect API Security request sampling value: ${requestSampling}`)
26
+
27
+ parsed = 0
28
+ } else {
29
+ parsed = Math.min(1, Math.max(0, parsed))
30
+ }
31
+
32
+ return parsed
33
+ }
34
+
35
+ function sampleRequest () {
36
+ if (!enabled || !requestSampling) {
37
+ return false
38
+ }
39
+
40
+ return Math.random() <= requestSampling
41
+ }
42
+
43
+ module.exports = {
44
+ configure,
45
+ disable,
46
+ setRequestSampling,
47
+ sampleRequest
48
+ }
@@ -5,7 +5,10 @@ const html = `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta n
5
5
 
6
6
  const json = `{"errors":[{"title":"You've been blocked","detail":"Sorry, you cannot access this page. Please contact the customer service team. Security provided by Datadog."}]}`
7
7
 
8
+ const graphqlJson = `{"errors":[{"message":"You've been blocked","extensions":{"detail":"Sorry, you cannot perform this operation. Please contact the customer service team. Security provided by Datadog."}}]}`
9
+
8
10
  module.exports = {
9
11
  html,
10
- json
12
+ json,
13
+ graphqlJson
11
14
  }