dd-trace 4.21.0 → 4.23.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 (68) hide show
  1. package/index.d.ts +5 -0
  2. package/package.json +6 -6
  3. package/packages/datadog-instrumentations/src/apollo-server-core.js +41 -0
  4. package/packages/datadog-instrumentations/src/apollo-server.js +83 -0
  5. package/packages/datadog-instrumentations/src/child-process.js +4 -5
  6. package/packages/datadog-instrumentations/src/couchbase.js +5 -4
  7. package/packages/datadog-instrumentations/src/crypto.js +2 -1
  8. package/packages/datadog-instrumentations/src/dns.js +2 -1
  9. package/packages/datadog-instrumentations/src/graphql.js +18 -4
  10. package/packages/datadog-instrumentations/src/helpers/hooks.js +9 -2
  11. package/packages/datadog-instrumentations/src/helpers/instrument.js +8 -3
  12. package/packages/datadog-instrumentations/src/helpers/register.js +18 -2
  13. package/packages/datadog-instrumentations/src/http/client.js +4 -16
  14. package/packages/datadog-instrumentations/src/http/server.js +7 -4
  15. package/packages/datadog-instrumentations/src/http2/client.js +3 -1
  16. package/packages/datadog-instrumentations/src/http2/server.js +3 -1
  17. package/packages/datadog-instrumentations/src/jest.js +1 -1
  18. package/packages/datadog-instrumentations/src/net.js +10 -2
  19. package/packages/datadog-instrumentations/src/next.js +15 -5
  20. package/packages/datadog-instrumentations/src/rhea.js +15 -9
  21. package/packages/datadog-plugin-cucumber/src/index.js +34 -2
  22. package/packages/datadog-plugin-cypress/src/plugin.js +60 -8
  23. package/packages/datadog-plugin-graphql/src/resolve.js +26 -18
  24. package/packages/datadog-plugin-http/src/client.js +1 -1
  25. package/packages/datadog-plugin-jest/src/index.js +38 -4
  26. package/packages/datadog-plugin-mocha/src/index.js +32 -1
  27. package/packages/datadog-plugin-next/src/index.js +32 -6
  28. package/packages/datadog-plugin-playwright/src/index.js +17 -1
  29. package/packages/dd-trace/src/appsec/activation.js +29 -0
  30. package/packages/dd-trace/src/appsec/addresses.js +1 -0
  31. package/packages/dd-trace/src/appsec/api_security_sampler.js +48 -0
  32. package/packages/dd-trace/src/appsec/blocked_templates.js +4 -1
  33. package/packages/dd-trace/src/appsec/blocking.js +95 -43
  34. package/packages/dd-trace/src/appsec/channels.js +4 -1
  35. package/packages/dd-trace/src/appsec/graphql.js +146 -0
  36. package/packages/dd-trace/src/appsec/index.js +29 -40
  37. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +6 -1
  38. package/packages/dd-trace/src/appsec/remote_config/index.js +40 -15
  39. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  40. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +25 -13
  41. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +30 -1
  42. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +30 -1
  43. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +36 -4
  44. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +18 -1
  45. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +26 -1
  46. package/packages/dd-trace/src/ci-visibility/telemetry.js +130 -0
  47. package/packages/dd-trace/src/config.js +104 -58
  48. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +14 -1
  49. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +14 -0
  50. package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +4 -0
  51. package/packages/dd-trace/src/exporters/common/form-data.js +4 -0
  52. package/packages/dd-trace/src/opentracing/tracer.js +2 -2
  53. package/packages/dd-trace/src/plugins/ci_plugin.js +44 -8
  54. package/packages/dd-trace/src/plugins/index.js +5 -0
  55. package/packages/dd-trace/src/plugins/util/exec.js +23 -2
  56. package/packages/dd-trace/src/plugins/util/git.js +94 -19
  57. package/packages/dd-trace/src/plugins/util/user-provided-git.js +3 -2
  58. package/packages/dd-trace/src/priority_sampler.js +30 -38
  59. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -0
  60. package/packages/dd-trace/src/profiling/profiler.js +7 -6
  61. package/packages/dd-trace/src/profiling/profilers/events.js +18 -13
  62. package/packages/dd-trace/src/profiling/profilers/shared.js +34 -4
  63. package/packages/dd-trace/src/profiling/profilers/space.js +2 -1
  64. package/packages/dd-trace/src/profiling/profilers/wall.js +17 -12
  65. package/packages/dd-trace/src/proxy.js +4 -0
  66. package/packages/dd-trace/src/sampling_rule.js +130 -0
  67. package/packages/dd-trace/src/span_sampler.js +6 -64
  68. package/packages/dd-trace/src/telemetry/index.js +43 -5
@@ -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,6 +13,7 @@ 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
 
@@ -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
  }
@@ -3,93 +3,142 @@
3
3
  const log = require('../log')
4
4
  const blockedTemplates = require('./blocked_templates')
5
5
 
6
+ const detectedSpecificEndpoints = {}
7
+
6
8
  let templateHtml = blockedTemplates.html
7
9
  let templateJson = blockedTemplates.json
10
+ let templateGraphqlJson = blockedTemplates.graphqlJson
8
11
  let blockingConfiguration
9
12
 
10
- function blockWithRedirect (res, rootSpan, abortController) {
11
- rootSpan.addTags({
12
- 'appsec.blocked': 'true'
13
- })
13
+ const specificBlockingTypes = {
14
+ GRAPHQL: 'graphql'
15
+ }
14
16
 
17
+ function getSpecificKey (method, url) {
18
+ return `${method}+${url}`
19
+ }
20
+
21
+ function addSpecificEndpoint (method, url, type) {
22
+ detectedSpecificEndpoints[getSpecificKey(method, url)] = type
23
+ }
24
+
25
+ function getBlockWithRedirectData (rootSpan) {
15
26
  let statusCode = blockingConfiguration.parameters.status_code
16
27
  if (!statusCode || statusCode < 300 || statusCode >= 400) {
17
28
  statusCode = 303
18
29
  }
19
-
20
- res.writeHead(statusCode, {
30
+ const headers = {
21
31
  'Location': blockingConfiguration.parameters.location
22
- }).end()
32
+ }
33
+
34
+ rootSpan.addTags({
35
+ 'appsec.blocked': 'true'
36
+ })
23
37
 
24
- if (abortController) {
25
- abortController.abort()
38
+ return { headers, statusCode }
39
+ }
40
+
41
+ function getSpecificBlockingData (type) {
42
+ switch (type) {
43
+ case specificBlockingTypes.GRAPHQL:
44
+ return {
45
+ type: 'application/json',
46
+ body: templateGraphqlJson
47
+ }
26
48
  }
27
49
  }
28
50
 
29
- function blockWithContent (req, res, rootSpan, abortController) {
51
+ function getBlockWithContentData (req, specificType, rootSpan) {
30
52
  let type
31
53
  let body
54
+ let statusCode
32
55
 
33
- // parse the Accept header, ex: Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8
34
- const accept = req.headers.accept && req.headers.accept.split(',').map((str) => str.split(';', 1)[0].trim())
56
+ const specificBlockingType = specificType || detectedSpecificEndpoints[getSpecificKey(req.method, req.url)]
57
+ if (specificBlockingType) {
58
+ const specificBlockingContent = getSpecificBlockingData(specificBlockingType)
59
+ type = specificBlockingContent?.type
60
+ body = specificBlockingContent?.body
61
+ }
35
62
 
36
- if (!blockingConfiguration || blockingConfiguration.parameters.type === 'auto') {
37
- if (accept && accept.includes('text/html') && !accept.includes('application/json')) {
38
- type = 'text/html; charset=utf-8'
39
- body = templateHtml
63
+ if (!type) {
64
+ // parse the Accept header, ex: Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8
65
+ const accept = req.headers.accept?.split(',').map((str) => str.split(';', 1)[0].trim())
66
+
67
+ if (!blockingConfiguration || blockingConfiguration.parameters.type === 'auto') {
68
+ if (accept?.includes('text/html') && !accept.includes('application/json')) {
69
+ type = 'text/html; charset=utf-8'
70
+ body = templateHtml
71
+ } else {
72
+ type = 'application/json'
73
+ body = templateJson
74
+ }
40
75
  } else {
41
- type = 'application/json'
42
- body = templateJson
76
+ if (blockingConfiguration.parameters.type === 'html') {
77
+ type = 'text/html; charset=utf-8'
78
+ body = templateHtml
79
+ } else {
80
+ type = 'application/json'
81
+ body = templateJson
82
+ }
43
83
  }
84
+ }
85
+
86
+ if (blockingConfiguration?.type === 'block_request' && blockingConfiguration.parameters.status_code) {
87
+ statusCode = blockingConfiguration.parameters.status_code
44
88
  } else {
45
- if (blockingConfiguration.parameters.type === 'html') {
46
- type = 'text/html; charset=utf-8'
47
- body = templateHtml
48
- } else {
49
- type = 'application/json'
50
- body = templateJson
51
- }
89
+ statusCode = 403
90
+ }
91
+
92
+ const headers = {
93
+ 'Content-Type': type,
94
+ 'Content-Length': Buffer.byteLength(body)
52
95
  }
53
96
 
54
97
  rootSpan.addTags({
55
98
  'appsec.blocked': 'true'
56
99
  })
57
100
 
58
- if (blockingConfiguration && blockingConfiguration.type === 'block_request' &&
59
- blockingConfiguration.parameters.status_code) {
60
- res.statusCode = blockingConfiguration.parameters.status_code
61
- } else {
62
- res.statusCode = 403
63
- }
64
- res.setHeader('Content-Type', type)
65
- res.setHeader('Content-Length', Buffer.byteLength(body))
66
- res.end(body)
101
+ return { body, statusCode, headers }
102
+ }
67
103
 
68
- if (abortController) {
69
- abortController.abort()
104
+ function getBlockingData (req, specificType, rootSpan) {
105
+ if (blockingConfiguration?.type === 'redirect_request' && blockingConfiguration.parameters.location) {
106
+ return getBlockWithRedirectData(rootSpan)
107
+ } else {
108
+ return getBlockWithContentData(req, specificType, rootSpan)
70
109
  }
71
110
  }
72
111
 
73
- function block (req, res, rootSpan, abortController) {
112
+ function block (req, res, rootSpan, abortController, type) {
74
113
  if (res.headersSent) {
75
114
  log.warn('Cannot send blocking response when headers have already been sent')
76
115
  return
77
116
  }
78
117
 
79
- if (blockingConfiguration && blockingConfiguration.type === 'redirect_request' &&
80
- blockingConfiguration.parameters.location) {
81
- blockWithRedirect(res, rootSpan, abortController)
82
- } else {
83
- blockWithContent(req, res, rootSpan, abortController)
84
- }
118
+ const { body, headers, statusCode } = getBlockingData(req, type, rootSpan)
119
+
120
+ res.writeHead(statusCode, headers).end(body)
121
+
122
+ abortController?.abort()
85
123
  }
86
124
 
87
125
  function setTemplates (config) {
88
126
  if (config.appsec.blockedTemplateHtml) {
89
127
  templateHtml = config.appsec.blockedTemplateHtml
128
+ } else {
129
+ templateHtml = blockedTemplates.html
90
130
  }
131
+
91
132
  if (config.appsec.blockedTemplateJson) {
92
133
  templateJson = config.appsec.blockedTemplateJson
134
+ } else {
135
+ templateJson = blockedTemplates.json
136
+ }
137
+
138
+ if (config.appsec.blockedTemplateGraphql) {
139
+ templateGraphqlJson = config.appsec.blockedTemplateGraphql
140
+ } else {
141
+ templateGraphqlJson = blockedTemplates.graphqlJson
93
142
  }
94
143
  }
95
144
 
@@ -98,7 +147,10 @@ function updateBlockingConfiguration (newBlockingConfiguration) {
98
147
  }
99
148
 
100
149
  module.exports = {
150
+ addSpecificEndpoint,
101
151
  block,
152
+ specificBlockingTypes,
153
+ getBlockingData,
102
154
  setTemplates,
103
155
  updateBlockingConfiguration
104
156
  }
@@ -6,7 +6,10 @@ const dc = require('dc-polyfill')
6
6
  module.exports = {
7
7
  bodyParser: dc.channel('datadog:body-parser:read:finish'),
8
8
  cookieParser: dc.channel('datadog:cookie-parser:read:finish'),
9
- graphqlFinishExecute: dc.channel('apm:graphql:execute:finish'),
9
+ startGraphqlResolve: dc.channel('datadog:graphql:resolver:start'),
10
+ graphqlMiddlewareChannel: dc.tracingChannel('datadog:apollo:middleware'),
11
+ apolloChannel: dc.tracingChannel('datadog:apollo:request'),
12
+ apolloServerCoreChannel: dc.tracingChannel('datadog:apollo-server-core:request'),
10
13
  incomingHttpRequestStart: dc.channel('dd-trace:incomingHttpRequestStart'),
11
14
  incomingHttpRequestEnd: dc.channel('dd-trace:incomingHttpRequestEnd'),
12
15
  passportVerify: dc.channel('datadog:passport:verify:finish'),
@@ -0,0 +1,146 @@
1
+ 'use strict'
2
+
3
+ const { storage } = require('../../../datadog-core')
4
+ const { addSpecificEndpoint, specificBlockingTypes, getBlockingData } = require('./blocking')
5
+ const waf = require('./waf')
6
+ const addresses = require('./addresses')
7
+ const web = require('../plugins/util/web')
8
+ const {
9
+ startGraphqlResolve,
10
+ graphqlMiddlewareChannel,
11
+ apolloChannel,
12
+ apolloServerCoreChannel
13
+ } = require('./channels')
14
+
15
+ const graphqlRequestData = new WeakMap()
16
+
17
+ function enable () {
18
+ enableApollo()
19
+ enableGraphql()
20
+ }
21
+
22
+ function disable () {
23
+ disableApollo()
24
+ disableGraphql()
25
+ }
26
+
27
+ function onGraphqlStartResolve ({ context, resolverInfo }) {
28
+ const req = storage.getStore()?.req
29
+
30
+ if (!req) return
31
+
32
+ if (!resolverInfo || typeof resolverInfo !== 'object') return
33
+
34
+ const actions = waf.run({ ephemeral: { [addresses.HTTP_INCOMING_GRAPHQL_RESOLVER]: resolverInfo } }, req)
35
+ if (actions?.includes('block')) {
36
+ const requestData = graphqlRequestData.get(req)
37
+ if (requestData?.isInGraphqlRequest) {
38
+ requestData.blocked = true
39
+ context?.abortController?.abort()
40
+ }
41
+ }
42
+ }
43
+
44
+ function enterInApolloMiddleware (data) {
45
+ const req = data?.req || storage.getStore()?.req
46
+ if (!req) return
47
+
48
+ graphqlRequestData.set(req, {
49
+ inApolloMiddleware: true,
50
+ blocked: false
51
+ })
52
+ }
53
+
54
+ function enterInApolloServerCoreRequest () {
55
+ const req = storage.getStore()?.req
56
+ if (!req) return
57
+
58
+ graphqlRequestData.set(req, {
59
+ isInGraphqlRequest: true,
60
+ blocked: false
61
+ })
62
+ }
63
+
64
+ function exitFromApolloMiddleware (data) {
65
+ const req = data?.req || storage.getStore()?.req
66
+ const requestData = graphqlRequestData.get(req)
67
+ if (requestData) requestData.inApolloMiddleware = false
68
+ }
69
+
70
+ function enterInApolloRequest () {
71
+ const req = storage.getStore()?.req
72
+
73
+ const requestData = graphqlRequestData.get(req)
74
+ if (requestData?.inApolloMiddleware) {
75
+ requestData.isInGraphqlRequest = true
76
+ addSpecificEndpoint(req.method, req.originalUrl || req.url, specificBlockingTypes.GRAPHQL)
77
+ }
78
+ }
79
+
80
+ function beforeWriteApolloGraphqlResponse ({ abortController, abortData }) {
81
+ const req = storage.getStore()?.req
82
+ if (!req) return
83
+
84
+ const requestData = graphqlRequestData.get(req)
85
+
86
+ if (requestData?.blocked) {
87
+ const rootSpan = web.root(req)
88
+ if (!rootSpan) return
89
+
90
+ const blockingData = getBlockingData(req, specificBlockingTypes.GRAPHQL, rootSpan)
91
+ abortData.statusCode = blockingData.statusCode
92
+ abortData.headers = blockingData.headers
93
+ abortData.message = blockingData.body
94
+
95
+ abortController?.abort()
96
+ }
97
+
98
+ graphqlRequestData.delete(req)
99
+ }
100
+
101
+ function enableApollo () {
102
+ graphqlMiddlewareChannel.subscribe({
103
+ start: enterInApolloMiddleware,
104
+ end: exitFromApolloMiddleware
105
+ })
106
+
107
+ apolloServerCoreChannel.subscribe({
108
+ start: enterInApolloServerCoreRequest,
109
+ asyncEnd: beforeWriteApolloGraphqlResponse
110
+ })
111
+
112
+ apolloChannel.subscribe({
113
+ start: enterInApolloRequest,
114
+ asyncEnd: beforeWriteApolloGraphqlResponse
115
+ })
116
+ }
117
+
118
+ function disableApollo () {
119
+ graphqlMiddlewareChannel.unsubscribe({
120
+ start: enterInApolloMiddleware,
121
+ end: exitFromApolloMiddleware
122
+ })
123
+
124
+ apolloServerCoreChannel.unsubscribe({
125
+ start: enterInApolloServerCoreRequest,
126
+ asyncEnd: beforeWriteApolloGraphqlResponse
127
+ })
128
+
129
+ apolloChannel.unsubscribe({
130
+ start: enterInApolloRequest,
131
+ asyncEnd: beforeWriteApolloGraphqlResponse
132
+ })
133
+ }
134
+
135
+ function enableGraphql () {
136
+ startGraphqlResolve.subscribe(onGraphqlStartResolve)
137
+ }
138
+
139
+ function disableGraphql () {
140
+ if (startGraphqlResolve.hasSubscribers) startGraphqlResolve.unsubscribe(onGraphqlStartResolve)
141
+ }
142
+
143
+ module.exports = {
144
+ enable,
145
+ disable
146
+ }
@@ -6,7 +6,6 @@ const remoteConfig = require('./remote_config')
6
6
  const {
7
7
  bodyParser,
8
8
  cookieParser,
9
- graphqlFinishExecute,
10
9
  incomingHttpRequestStart,
11
10
  incomingHttpRequestEnd,
12
11
  passportVerify,
@@ -18,29 +17,24 @@ const waf = require('./waf')
18
17
  const addresses = require('./addresses')
19
18
  const Reporter = require('./reporter')
20
19
  const appsecTelemetry = require('./telemetry')
20
+ const apiSecuritySampler = require('./api_security_sampler')
21
21
  const web = require('../plugins/util/web')
22
22
  const { extractIp } = require('../plugins/util/ip_extractor')
23
23
  const { HTTP_CLIENT_IP } = require('../../../../ext/tags')
24
24
  const { block, setTemplates } = require('./blocking')
25
25
  const { passportTrackEvent } = require('./passport')
26
26
  const { storage } = require('../../../datadog-core')
27
+ const graphql = require('./graphql')
27
28
 
28
29
  let isEnabled = false
29
30
  let config
30
31
 
31
- function sampleRequest ({ enabled, requestSampling }) {
32
- if (!enabled || !requestSampling) {
33
- return false
34
- }
35
-
36
- return Math.random() <= requestSampling
37
- }
38
-
39
32
  function enable (_config) {
40
33
  if (isEnabled) return
41
34
 
42
35
  try {
43
36
  appsecTelemetry.enable(_config.telemetry)
37
+ graphql.enable()
44
38
 
45
39
  setTemplates(_config)
46
40
 
@@ -50,6 +44,8 @@ function enable (_config) {
50
44
 
51
45
  Reporter.setRateLimit(_config.appsec.rateLimit)
52
46
 
47
+ apiSecuritySampler.configure(_config.appsec)
48
+
53
49
  incomingHttpRequestStart.subscribe(incomingHttpStartTranslator)
54
50
  incomingHttpRequestEnd.subscribe(incomingHttpEndTranslator)
55
51
  bodyParser.subscribe(onRequestBodyParsed)
@@ -57,7 +53,6 @@ function enable (_config) {
57
53
  nextQueryParsed.subscribe(onRequestQueryParsed)
58
54
  queryParser.subscribe(onRequestQueryParsed)
59
55
  cookieParser.subscribe(onRequestCookieParser)
60
- graphqlFinishExecute.subscribe(onGraphqlFinishExecute)
61
56
 
62
57
  if (_config.appsec.eventTracking.enabled) {
63
58
  passportVerify.subscribe(onPassportVerify)
@@ -88,21 +83,21 @@ function incomingHttpStartTranslator ({ req, res, abortController }) {
88
83
  const requestHeaders = Object.assign({}, req.headers)
89
84
  delete requestHeaders.cookie
90
85
 
91
- const payload = {
86
+ const persistent = {
92
87
  [addresses.HTTP_INCOMING_URL]: req.url,
93
88
  [addresses.HTTP_INCOMING_HEADERS]: requestHeaders,
94
89
  [addresses.HTTP_INCOMING_METHOD]: req.method
95
90
  }
96
91
 
97
92
  if (clientIp) {
98
- payload[addresses.HTTP_CLIENT_IP] = clientIp
93
+ persistent[addresses.HTTP_CLIENT_IP] = clientIp
99
94
  }
100
95
 
101
- if (sampleRequest(config.appsec.apiSecurity)) {
102
- payload[addresses.WAF_CONTEXT_PROCESSOR] = { 'extract-schema': true }
96
+ if (apiSecuritySampler.sampleRequest()) {
97
+ persistent[addresses.WAF_CONTEXT_PROCESSOR] = { 'extract-schema': true }
103
98
  }
104
99
 
105
- const actions = waf.run(payload, req)
100
+ const actions = waf.run({ persistent }, req)
106
101
 
107
102
  handleResults(actions, req, res, rootSpan, abortController)
108
103
  }
@@ -112,7 +107,7 @@ function incomingHttpEndTranslator ({ req, res }) {
112
107
  const responseHeaders = Object.assign({}, res.getHeaders())
113
108
  delete responseHeaders['set-cookie']
114
109
 
115
- const payload = {
110
+ const persistent = {
116
111
  [addresses.HTTP_INCOMING_RESPONSE_CODE]: '' + res.statusCode,
117
112
  [addresses.HTTP_INCOMING_RESPONSE_HEADERS]: responseHeaders
118
113
  }
@@ -120,24 +115,24 @@ function incomingHttpEndTranslator ({ req, res }) {
120
115
  // we need to keep this to support other body parsers
121
116
  // TODO: no need to analyze it if it was already done by the body-parser hook
122
117
  if (req.body !== undefined && req.body !== null) {
123
- payload[addresses.HTTP_INCOMING_BODY] = req.body
118
+ persistent[addresses.HTTP_INCOMING_BODY] = req.body
124
119
  }
125
120
 
126
121
  // TODO: temporary express instrumentation, will use express plugin later
127
122
  if (req.params && typeof req.params === 'object') {
128
- payload[addresses.HTTP_INCOMING_PARAMS] = req.params
123
+ persistent[addresses.HTTP_INCOMING_PARAMS] = req.params
129
124
  }
130
125
 
131
126
  // we need to keep this to support other cookie parsers
132
127
  if (req.cookies && typeof req.cookies === 'object') {
133
- payload[addresses.HTTP_INCOMING_COOKIES] = req.cookies
128
+ persistent[addresses.HTTP_INCOMING_COOKIES] = req.cookies
134
129
  }
135
130
 
136
131
  if (req.query && typeof req.query === 'object') {
137
- payload[addresses.HTTP_INCOMING_QUERY] = req.query
132
+ persistent[addresses.HTTP_INCOMING_QUERY] = req.query
138
133
  }
139
134
 
140
- waf.run(payload, req)
135
+ waf.run({ persistent }, req)
141
136
 
142
137
  waf.disposeContext(req)
143
138
 
@@ -156,7 +151,9 @@ function onRequestBodyParsed ({ req, res, body, abortController }) {
156
151
  if (!rootSpan) return
157
152
 
158
153
  const results = waf.run({
159
- [addresses.HTTP_INCOMING_BODY]: body
154
+ persistent: {
155
+ [addresses.HTTP_INCOMING_BODY]: body
156
+ }
160
157
  }, req)
161
158
 
162
159
  handleResults(results, req, res, rootSpan, abortController)
@@ -174,7 +171,9 @@ function onRequestQueryParsed ({ req, res, query, abortController }) {
174
171
  if (!rootSpan) return
175
172
 
176
173
  const results = waf.run({
177
- [addresses.HTTP_INCOMING_QUERY]: query
174
+ persistent: {
175
+ [addresses.HTTP_INCOMING_QUERY]: query
176
+ }
178
177
  }, req)
179
178
 
180
179
  handleResults(results, req, res, rootSpan, abortController)
@@ -187,7 +186,9 @@ function onRequestCookieParser ({ req, res, abortController, cookies }) {
187
186
  if (!rootSpan) return
188
187
 
189
188
  const results = waf.run({
190
- [addresses.HTTP_INCOMING_COOKIES]: cookies
189
+ persistent: {
190
+ [addresses.HTTP_INCOMING_COOKIES]: cookies
191
+ }
191
192
  }, req)
192
193
 
193
194
  handleResults(results, req, res, rootSpan, abortController)
@@ -195,7 +196,7 @@ function onRequestCookieParser ({ req, res, abortController, cookies }) {
195
196
 
196
197
  function onPassportVerify ({ credentials, user }) {
197
198
  const store = storage.getStore()
198
- const rootSpan = store && store.req && web.root(store.req)
199
+ const rootSpan = store?.req && web.root(store.req)
199
200
 
200
201
  if (!rootSpan) {
201
202
  log.warn('No rootSpan found in onPassportVerify')
@@ -205,20 +206,6 @@ function onPassportVerify ({ credentials, user }) {
205
206
  passportTrackEvent(credentials, user, rootSpan, config.appsec.eventTracking.mode)
206
207
  }
207
208
 
208
- function onGraphqlFinishExecute ({ context }) {
209
- const store = storage.getStore()
210
- const req = store?.req
211
-
212
- if (!req) return
213
-
214
- const resolvers = context?.resolvers
215
-
216
- if (!resolvers || typeof resolvers !== 'object') return
217
-
218
- // Don't collect blocking result because it only works in monitor mode.
219
- waf.run({ [addresses.HTTP_INCOMING_GRAPHQL_RESOLVERS]: resolvers }, req)
220
- }
221
-
222
209
  function handleResults (actions, req, res, rootSpan, abortController) {
223
210
  if (!actions || !req || !res || !rootSpan || !abortController) return
224
211
 
@@ -234,12 +221,14 @@ function disable () {
234
221
  RuleManager.clearAllRules()
235
222
 
236
223
  appsecTelemetry.disable()
224
+ graphql.disable()
237
225
 
238
226
  remoteConfig.disableWafUpdate()
239
227
 
228
+ apiSecuritySampler.disable()
229
+
240
230
  // Channel#unsubscribe() is undefined for non active channels
241
231
  if (bodyParser.hasSubscribers) bodyParser.unsubscribe(onRequestBodyParsed)
242
- if (graphqlFinishExecute.hasSubscribers) graphqlFinishExecute.unsubscribe(onGraphqlFinishExecute)
243
232
  if (incomingHttpRequestStart.hasSubscribers) incomingHttpRequestStart.unsubscribe(incomingHttpStartTranslator)
244
233
  if (incomingHttpRequestEnd.hasSubscribers) incomingHttpRequestEnd.unsubscribe(incomingHttpEndTranslator)
245
234
  if (queryParser.hasSubscribers) queryParser.unsubscribe(onRequestQueryParsed)
@@ -9,5 +9,10 @@ module.exports = {
9
9
  ASM_USER_BLOCKING: 1n << 7n,
10
10
  ASM_CUSTOM_RULES: 1n << 8n,
11
11
  ASM_CUSTOM_BLOCKING_RESPONSE: 1n << 9n,
12
- ASM_TRUSTED_IPS: 1n << 10n
12
+ ASM_TRUSTED_IPS: 1n << 10n,
13
+ ASM_API_SECURITY_SAMPLE_RATE: 1n << 11n,
14
+ APM_TRACING_SAMPLE_RATE: 1n << 12n,
15
+ APM_TRACING_LOGS_INJECTION: 1n << 13n,
16
+ APM_TRACING_HTTP_HEADER_TAGS: 1n << 14n,
17
+ APM_TRACING_CUSTOM_TAGS: 1n << 15n
13
18
  }