dd-trace 4.20.0 → 4.22.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 +8 -7
  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
@@ -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
+ }
@@ -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'),
@@ -0,0 +1,105 @@
1
+ 'use strict'
2
+
3
+ const InjectionAnalyzer = require('./injection-analyzer')
4
+ const { HEADER_INJECTION } = require('../vulnerabilities')
5
+ const { getNodeModulesPaths } = require('../path-line')
6
+ const { HEADER_NAME_VALUE_SEPARATOR } = require('../vulnerabilities-formatter/constants')
7
+ const { getRanges } = require('../taint-tracking/operations')
8
+ const {
9
+ HTTP_REQUEST_COOKIE_NAME,
10
+ HTTP_REQUEST_COOKIE_VALUE,
11
+ HTTP_REQUEST_HEADER_VALUE
12
+ } = require('../taint-tracking/source-types')
13
+
14
+ const EXCLUDED_PATHS = getNodeModulesPaths('express')
15
+ const EXCLUDED_HEADER_NAMES = [
16
+ 'location',
17
+ 'sec-websocket-location',
18
+ 'sec-websocket-accept',
19
+ 'upgrade',
20
+ 'connection'
21
+ ]
22
+
23
+ class HeaderInjectionAnalyzer extends InjectionAnalyzer {
24
+ constructor () {
25
+ super(HEADER_INJECTION)
26
+ }
27
+
28
+ onConfigure () {
29
+ this.addSub('datadog:http:server:response:set-header:finish', ({ name, value }) => {
30
+ if (Array.isArray(value)) {
31
+ for (let i = 0; i < value.length; i++) {
32
+ const headerValue = value[i]
33
+
34
+ this.analyze({ name, value: headerValue })
35
+ }
36
+ } else {
37
+ this.analyze({ name, value })
38
+ }
39
+ })
40
+ }
41
+
42
+ _isVulnerable ({ name, value }, iastContext) {
43
+ const lowerCasedHeaderName = name?.trim().toLowerCase()
44
+
45
+ if (this.isExcludedHeaderName(lowerCasedHeaderName) || typeof value !== 'string') return
46
+
47
+ const ranges = getRanges(iastContext, value)
48
+ if (ranges?.length > 0) {
49
+ return !(this.isCookieExclusion(lowerCasedHeaderName, ranges) ||
50
+ this.isSameHeaderExclusion(lowerCasedHeaderName, ranges) ||
51
+ this.isAccessControlAllowOriginExclusion(lowerCasedHeaderName, ranges))
52
+ }
53
+
54
+ return false
55
+ }
56
+
57
+ _getEvidence (headerInfo, iastContext) {
58
+ const prefix = headerInfo.name + HEADER_NAME_VALUE_SEPARATOR
59
+ const prefixLength = prefix.length
60
+
61
+ const evidence = super._getEvidence(headerInfo.value, iastContext)
62
+ evidence.value = prefix + evidence.value
63
+ evidence.ranges = evidence.ranges.map(range => {
64
+ return {
65
+ ...range,
66
+ start: range.start + prefixLength,
67
+ end: range.end + prefixLength
68
+ }
69
+ })
70
+
71
+ return evidence
72
+ }
73
+
74
+ isExcludedHeaderName (name) {
75
+ return EXCLUDED_HEADER_NAMES.includes(name)
76
+ }
77
+
78
+ isCookieExclusion (name, ranges) {
79
+ if (name === 'set-cookie') {
80
+ return ranges
81
+ .every(range => range.iinfo.type === HTTP_REQUEST_COOKIE_VALUE || range.iinfo.type === HTTP_REQUEST_COOKIE_NAME)
82
+ }
83
+
84
+ return false
85
+ }
86
+
87
+ isAccessControlAllowOriginExclusion (name, ranges) {
88
+ if (name === 'access-control-allow-origin') {
89
+ return ranges
90
+ .every(range => range.iinfo.type === HTTP_REQUEST_HEADER_VALUE)
91
+ }
92
+
93
+ return false
94
+ }
95
+
96
+ isSameHeaderExclusion (name, ranges) {
97
+ return ranges.length === 1 && name === ranges[0].iinfo.parameterName?.toLowerCase()
98
+ }
99
+
100
+ _getExcludedPaths () {
101
+ return EXCLUDED_PATHS
102
+ }
103
+ }
104
+
105
+ module.exports = new HeaderInjectionAnalyzer()
@@ -0,0 +1,7 @@
1
+ 'use strict'
2
+
3
+ const HEADER_NAME_VALUE_SEPARATOR = ': '
4
+
5
+ module.exports = {
6
+ HEADER_NAME_VALUE_SEPARATOR
7
+ }
@@ -3,27 +3,20 @@
3
3
  const iastLog = require('../../../iast-log')
4
4
 
5
5
  const COMMAND_PATTERN = '^(?:\\s*(?:sudo|doas)\\s+)?\\b\\S+\\b\\s(.*)'
6
+ const pattern = new RegExp(COMMAND_PATTERN, 'gmi')
6
7
 
7
- class CommandSensitiveAnalyzer {
8
- constructor () {
9
- this._pattern = new RegExp(COMMAND_PATTERN, 'gmi')
10
- }
11
-
12
- extractSensitiveRanges (evidence) {
13
- try {
14
- this._pattern.lastIndex = 0
8
+ module.exports = function extractSensitiveRanges (evidence) {
9
+ try {
10
+ pattern.lastIndex = 0
15
11
 
16
- const regexResult = this._pattern.exec(evidence.value)
17
- if (regexResult && regexResult.length > 1) {
18
- const start = regexResult.index + (regexResult[0].length - regexResult[1].length)
19
- const end = start + regexResult[1].length
20
- return [{ start, end }]
21
- }
22
- } catch (e) {
23
- iastLog.debug(e)
12
+ const regexResult = pattern.exec(evidence.value)
13
+ if (regexResult && regexResult.length > 1) {
14
+ const start = regexResult.index + (regexResult[0].length - regexResult[1].length)
15
+ const end = start + regexResult[1].length
16
+ return [{ start, end }]
24
17
  }
25
- return []
18
+ } catch (e) {
19
+ iastLog.debug(e)
26
20
  }
21
+ return []
27
22
  }
28
-
29
- module.exports = CommandSensitiveAnalyzer
@@ -0,0 +1,20 @@
1
+ 'use strict'
2
+
3
+ const { HEADER_NAME_VALUE_SEPARATOR } = require('../../constants')
4
+
5
+ module.exports = function extractSensitiveRanges (evidence, namePattern, valuePattern) {
6
+ const evidenceValue = evidence.value
7
+ const sections = evidenceValue.split(HEADER_NAME_VALUE_SEPARATOR)
8
+ const headerName = sections[0]
9
+ const headerValue = sections.slice(1).join(HEADER_NAME_VALUE_SEPARATOR)
10
+ namePattern.lastIndex = 0
11
+ valuePattern.lastIndex = 0
12
+ if (namePattern.test(headerName) || valuePattern.test(headerValue)) {
13
+ return [{
14
+ start: headerName.length + HEADER_NAME_VALUE_SEPARATOR.length,
15
+ end: evidenceValue.length
16
+ }]
17
+ }
18
+
19
+ return []
20
+ }
@@ -2,15 +2,11 @@
2
2
 
3
3
  const { stringifyWithRanges } = require('../../utils')
4
4
 
5
- class JsonSensitiveAnalyzer {
6
- extractSensitiveRanges (evidence) {
7
- // expect object evidence
8
- const { value, ranges, sensitiveRanges } = stringifyWithRanges(evidence.value, evidence.rangesToApply, true)
9
- evidence.value = value
10
- evidence.ranges = ranges
5
+ module.exports = function extractSensitiveRanges (evidence) {
6
+ // expect object evidence
7
+ const { value, ranges, sensitiveRanges } = stringifyWithRanges(evidence.value, evidence.rangesToApply, true)
8
+ evidence.value = value
9
+ evidence.ranges = ranges
11
10
 
12
- return sensitiveRanges
13
- }
11
+ return sensitiveRanges
14
12
  }
15
-
16
- module.exports = JsonSensitiveAnalyzer
@@ -3,33 +3,26 @@
3
3
  const iastLog = require('../../../iast-log')
4
4
 
5
5
  const LDAP_PATTERN = '\\(.*?(?:~=|=|<=|>=)(?<LITERAL>[^)]+)\\)'
6
+ const pattern = new RegExp(LDAP_PATTERN, 'gmi')
6
7
 
7
- class LdapSensitiveAnalyzer {
8
- constructor () {
9
- this._pattern = new RegExp(LDAP_PATTERN, 'gmi')
10
- }
11
-
12
- extractSensitiveRanges (evidence) {
13
- try {
14
- this._pattern.lastIndex = 0
15
- const tokens = []
8
+ module.exports = function extractSensitiveRanges (evidence) {
9
+ try {
10
+ pattern.lastIndex = 0
11
+ const tokens = []
16
12
 
17
- let regexResult = this._pattern.exec(evidence.value)
18
- while (regexResult != null) {
19
- if (!regexResult.groups.LITERAL) continue
20
- // Computing indices manually since NodeJs 12 does not support d flag on regular expressions
21
- // TODO Get indices from group by adding d flag in regular expression
22
- const start = regexResult.index + (regexResult[0].length - regexResult.groups.LITERAL.length - 1)
23
- const end = start + regexResult.groups.LITERAL.length
24
- tokens.push({ start, end })
25
- regexResult = this._pattern.exec(evidence.value)
26
- }
27
- return tokens
28
- } catch (e) {
29
- iastLog.debug(e)
13
+ let regexResult = pattern.exec(evidence.value)
14
+ while (regexResult != null) {
15
+ if (!regexResult.groups.LITERAL) continue
16
+ // Computing indices manually since NodeJs 12 does not support d flag on regular expressions
17
+ // TODO Get indices from group by adding d flag in regular expression
18
+ const start = regexResult.index + (regexResult[0].length - regexResult.groups.LITERAL.length - 1)
19
+ const end = start + regexResult.groups.LITERAL.length
20
+ tokens.push({ start, end })
21
+ regexResult = pattern.exec(evidence.value)
30
22
  }
31
- return []
23
+ return tokens
24
+ } catch (e) {
25
+ iastLog.debug(e)
32
26
  }
27
+ return []
33
28
  }
34
-
35
- module.exports = LdapSensitiveAnalyzer