dd-trace 3.26.0 → 3.28.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.
- package/package.json +3 -3
- package/packages/datadog-instrumentations/src/cucumber.js +5 -2
- package/packages/datadog-instrumentations/src/grpc/client.js +44 -42
- package/packages/datadog-instrumentations/src/grpc/server.js +69 -60
- package/packages/datadog-instrumentations/src/http2/client.js +25 -26
- package/packages/datadog-instrumentations/src/jest.js +3 -1
- package/packages/datadog-instrumentations/src/kafkajs.js +11 -2
- package/packages/datadog-instrumentations/src/mocha.js +5 -3
- package/packages/datadog-instrumentations/src/redis.js +48 -5
- package/packages/datadog-plugin-cypress/src/plugin.js +4 -2
- package/packages/datadog-plugin-grpc/src/client.js +29 -11
- package/packages/datadog-plugin-grpc/src/server.js +22 -6
- package/packages/datadog-plugin-http2/src/client.js +46 -29
- package/packages/datadog-plugin-kafkajs/src/producer.js +6 -1
- package/packages/datadog-plugin-openai/src/services.js +14 -10
- package/packages/datadog-plugin-router/src/index.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +3 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +7 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/ldap-injection-analyzer.js +3 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +19 -15
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +5 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/ssrf-analyzer.js +2 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +3 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +18 -19
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-cipher-analyzer.js +3 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +3 -0
- package/packages/dd-trace/src/appsec/iast/iast-log.js +1 -1
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +205 -0
- package/packages/dd-trace/src/appsec/iast/index.js +6 -5
- package/packages/dd-trace/src/appsec/iast/tags.js +2 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +6 -6
- package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +3 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +23 -4
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +32 -16
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-telemetry.js +33 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +23 -16
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +76 -37
- package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +101 -0
- package/packages/dd-trace/src/appsec/iast/telemetry/index.js +45 -0
- package/packages/dd-trace/src/appsec/iast/telemetry/{logs.js → log/index.js} +5 -5
- package/packages/dd-trace/src/appsec/iast/telemetry/{log_collector.js → log/log-collector.js} +1 -1
- package/packages/dd-trace/src/appsec/iast/telemetry/namespaces.js +76 -0
- package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +53 -0
- package/packages/dd-trace/src/appsec/iast/telemetry/verbosity.js +42 -0
- package/packages/dd-trace/src/config.js +21 -2
- package/packages/dd-trace/src/constants.js +1 -0
- package/packages/dd-trace/src/dogstatsd.js +14 -1
- package/packages/dd-trace/src/metrics.js +2 -2
- package/packages/dd-trace/src/opentracing/tracer.js +1 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
- package/packages/dd-trace/src/plugins/outbound.js +29 -12
- package/packages/dd-trace/src/plugins/plugin.js +28 -0
- package/packages/dd-trace/src/plugins/tracing.js +33 -16
- package/packages/dd-trace/src/plugins/util/ci.js +1 -1
- package/packages/dd-trace/src/plugins/util/test.js +55 -11
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +1 -22
- package/packages/dd-trace/src/profiling/config.js +0 -3
- package/packages/dd-trace/src/profiling/index.js +0 -2
- package/packages/dd-trace/src/profiling/profilers/wall.js +23 -11
- package/packages/diagnostics_channel/src/index.js +64 -0
- package/packages/dd-trace/src/profiling/profilers/cpu.js +0 -126
- /package/packages/dd-trace/src/appsec/iast/taint-tracking/{origin-types.js → source-types.js} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { storage } = require('../../datadog-core')
|
|
3
4
|
const ClientPlugin = require('../../dd-trace/src/plugins/client')
|
|
4
5
|
const { TEXT_MAP } = require('../../../ext/formats')
|
|
5
6
|
const { addMetadataTags, getFilter, getMethodMetadata } = require('./util')
|
|
@@ -7,9 +8,20 @@ const { addMetadataTags, getFilter, getMethodMetadata } = require('./util')
|
|
|
7
8
|
class GrpcClientPlugin extends ClientPlugin {
|
|
8
9
|
static get id () { return 'grpc' }
|
|
9
10
|
static get operation () { return 'client:request' }
|
|
11
|
+
static get prefix () { return `apm:grpc:client:request` }
|
|
10
12
|
static get peerServicePrecursors () { return ['rpc.service'] }
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
constructor (...args) {
|
|
15
|
+
super(...args)
|
|
16
|
+
|
|
17
|
+
this.addTraceBind('emit', ({ parentStore }) => {
|
|
18
|
+
return parentStore
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
bindStart (message) {
|
|
23
|
+
const store = storage.getStore()
|
|
24
|
+
const { metadata, path, type } = message
|
|
13
25
|
const metadataFilter = this.config.metadataFilter
|
|
14
26
|
const method = getMethodMetadata(path, type)
|
|
15
27
|
const span = this.startSpan(this.operationName(), {
|
|
@@ -28,7 +40,7 @@ class GrpcClientPlugin extends ClientPlugin {
|
|
|
28
40
|
metrics: {
|
|
29
41
|
'grpc.status.code': 0
|
|
30
42
|
}
|
|
31
|
-
})
|
|
43
|
+
}, false)
|
|
32
44
|
|
|
33
45
|
// needed as precursor for peer.service
|
|
34
46
|
if (method.service && method.package) {
|
|
@@ -39,22 +51,27 @@ class GrpcClientPlugin extends ClientPlugin {
|
|
|
39
51
|
addMetadataTags(span, metadata, metadataFilter, 'request')
|
|
40
52
|
inject(this.tracer, span, metadata)
|
|
41
53
|
}
|
|
42
|
-
}
|
|
43
54
|
|
|
44
|
-
|
|
45
|
-
|
|
55
|
+
message.span = span
|
|
56
|
+
message.parentStore = store
|
|
57
|
+
message.currentStore = { ...store, span }
|
|
46
58
|
|
|
47
|
-
|
|
59
|
+
return message.currentStore
|
|
60
|
+
}
|
|
48
61
|
|
|
49
|
-
|
|
50
|
-
|
|
62
|
+
bindAsyncStart ({ parentStore }) {
|
|
63
|
+
return parentStore
|
|
51
64
|
}
|
|
52
65
|
|
|
53
|
-
|
|
54
|
-
|
|
66
|
+
error ({ span, error }) {
|
|
67
|
+
this.addCode(span, error.code)
|
|
68
|
+
this.addError(error, span)
|
|
69
|
+
}
|
|
55
70
|
|
|
71
|
+
finish ({ span, result }) {
|
|
56
72
|
if (!span) return
|
|
57
73
|
|
|
74
|
+
const { code, metadata } = result || {}
|
|
58
75
|
const metadataFilter = this.config.metadataFilter
|
|
59
76
|
|
|
60
77
|
this.addCode(span, code)
|
|
@@ -63,7 +80,8 @@ class GrpcClientPlugin extends ClientPlugin {
|
|
|
63
80
|
addMetadataTags(span, metadata, metadataFilter, 'response')
|
|
64
81
|
}
|
|
65
82
|
|
|
66
|
-
|
|
83
|
+
this.tagPeerService(span)
|
|
84
|
+
span.finish()
|
|
67
85
|
}
|
|
68
86
|
|
|
69
87
|
configure (config) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { storage } = require('../../datadog-core')
|
|
3
4
|
const ServerPlugin = require('../../dd-trace/src/plugins/server')
|
|
4
5
|
const { TEXT_MAP } = require('../../../ext/formats')
|
|
5
6
|
const { addMetadataTags, getFilter, getMethodMetadata } = require('./util')
|
|
@@ -7,6 +8,7 @@ const { addMetadataTags, getFilter, getMethodMetadata } = require('./util')
|
|
|
7
8
|
class GrpcServerPlugin extends ServerPlugin {
|
|
8
9
|
static get id () { return 'grpc' }
|
|
9
10
|
static get operation () { return 'server:request' }
|
|
11
|
+
static get prefix () { return `apm:grpc:server:request` }
|
|
10
12
|
|
|
11
13
|
constructor (...args) {
|
|
12
14
|
super(...args)
|
|
@@ -18,9 +20,15 @@ class GrpcServerPlugin extends ServerPlugin {
|
|
|
18
20
|
|
|
19
21
|
this.addCode(span, code)
|
|
20
22
|
})
|
|
23
|
+
|
|
24
|
+
this.addTraceBind('emit', ({ currentStore }) => {
|
|
25
|
+
return currentStore
|
|
26
|
+
})
|
|
21
27
|
}
|
|
22
28
|
|
|
23
|
-
|
|
29
|
+
bindStart (message) {
|
|
30
|
+
const store = storage.getStore()
|
|
31
|
+
const { name, metadata, type } = message
|
|
24
32
|
const metadataFilter = this.config.metadataFilter
|
|
25
33
|
const childOf = extract(this.tracer, metadata)
|
|
26
34
|
const method = getMethodMetadata(name, type)
|
|
@@ -44,9 +52,19 @@ class GrpcServerPlugin extends ServerPlugin {
|
|
|
44
52
|
})
|
|
45
53
|
|
|
46
54
|
addMetadataTags(span, metadata, metadataFilter, 'request')
|
|
55
|
+
|
|
56
|
+
message.span = span
|
|
57
|
+
message.parentStore = store
|
|
58
|
+
message.currentStore = { ...store, span }
|
|
59
|
+
|
|
60
|
+
return message.currentStore
|
|
47
61
|
}
|
|
48
62
|
|
|
49
|
-
|
|
63
|
+
bindAsyncStart ({ parentStore }) {
|
|
64
|
+
return parentStore
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
error ({ error }) {
|
|
50
68
|
const span = this.activeSpan
|
|
51
69
|
|
|
52
70
|
if (!span) return
|
|
@@ -55,9 +73,7 @@ class GrpcServerPlugin extends ServerPlugin {
|
|
|
55
73
|
this.addError(error)
|
|
56
74
|
}
|
|
57
75
|
|
|
58
|
-
finish ({ code, trailer }
|
|
59
|
-
const span = this.activeSpan
|
|
60
|
-
|
|
76
|
+
finish ({ span, code, trailer }) {
|
|
61
77
|
if (!span) return
|
|
62
78
|
|
|
63
79
|
const metadataFilter = this.config.metadataFilter
|
|
@@ -68,7 +84,7 @@ class GrpcServerPlugin extends ServerPlugin {
|
|
|
68
84
|
addMetadataTags(span, trailer, metadataFilter, 'response')
|
|
69
85
|
}
|
|
70
86
|
|
|
71
|
-
|
|
87
|
+
span.finish()
|
|
72
88
|
}
|
|
73
89
|
|
|
74
90
|
configure (config) {
|
|
@@ -8,7 +8,6 @@ const log = require('../../dd-trace/src/log')
|
|
|
8
8
|
const tags = require('../../../ext/tags')
|
|
9
9
|
const kinds = require('../../../ext/kinds')
|
|
10
10
|
const formats = require('../../../ext/formats')
|
|
11
|
-
const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
|
|
12
11
|
const { COMPONENT, CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
|
|
13
12
|
const urlFilter = require('../../dd-trace/src/plugins/util/urlfilter')
|
|
14
13
|
|
|
@@ -25,32 +24,11 @@ const HTTP2_HEADER_STATUS = ':status'
|
|
|
25
24
|
const HTTP2_METHOD_GET = 'GET'
|
|
26
25
|
|
|
27
26
|
class Http2ClientPlugin extends ClientPlugin {
|
|
28
|
-
static get id () {
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
constructor (...args) {
|
|
33
|
-
super(...args)
|
|
34
|
-
|
|
35
|
-
this.addSub('apm:http2:client:response', (headers) => {
|
|
36
|
-
const span = storage.getStore().span
|
|
37
|
-
const status = headers && headers[HTTP2_HEADER_STATUS]
|
|
38
|
-
|
|
39
|
-
span.setTag(HTTP_STATUS_CODE, status)
|
|
40
|
-
|
|
41
|
-
if (!this.config.validateStatus(status)) {
|
|
42
|
-
this.addError()
|
|
43
|
-
}
|
|
27
|
+
static get id () { return 'http2' }
|
|
28
|
+
static get prefix () { return `apm:http2:client:request` }
|
|
44
29
|
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
addTraceSub (eventName, handler) {
|
|
50
|
-
this.addSub(`apm:${this.constructor.id}:client:${this.operation}:${eventName}`, handler)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
start ({ authority, options, headers = {} }) {
|
|
30
|
+
bindStart (message) {
|
|
31
|
+
const { authority, options, headers = {} } = message
|
|
54
32
|
const sessionDetails = extractSessionDetails(authority, options)
|
|
55
33
|
const path = headers[HTTP2_HEADER_PATH] || '/'
|
|
56
34
|
const pathname = path.split(/[?#]/)[0]
|
|
@@ -75,7 +53,7 @@ class Http2ClientPlugin extends ClientPlugin {
|
|
|
75
53
|
metrics: {
|
|
76
54
|
[CLIENT_PORT_KEY]: parseInt(sessionDetails.port)
|
|
77
55
|
}
|
|
78
|
-
})
|
|
56
|
+
}, false)
|
|
79
57
|
|
|
80
58
|
// TODO: Figure out a better way to do this for any span.
|
|
81
59
|
if (!allowed) {
|
|
@@ -88,14 +66,53 @@ class Http2ClientPlugin extends ClientPlugin {
|
|
|
88
66
|
this.tracer.inject(span, HTTP_HEADERS, headers)
|
|
89
67
|
}
|
|
90
68
|
|
|
91
|
-
|
|
69
|
+
message.parentStore = store
|
|
70
|
+
message.currentStore = { ...store, span }
|
|
71
|
+
|
|
72
|
+
return message.currentStore
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
bindAsyncStart ({ eventName, eventData, currentStore, parentStore }) {
|
|
76
|
+
switch (eventName) {
|
|
77
|
+
case 'response':
|
|
78
|
+
this._onResponse(currentStore, eventData)
|
|
79
|
+
return parentStore
|
|
80
|
+
case 'error':
|
|
81
|
+
this._onError(currentStore, eventData)
|
|
82
|
+
return parentStore
|
|
83
|
+
case 'close':
|
|
84
|
+
this._onClose(currentStore, eventData)
|
|
85
|
+
return parentStore
|
|
86
|
+
}
|
|
92
87
|
|
|
93
|
-
|
|
88
|
+
return storage.getStore()
|
|
94
89
|
}
|
|
95
90
|
|
|
96
91
|
configure (config) {
|
|
97
92
|
return super.configure(normalizeConfig(config))
|
|
98
93
|
}
|
|
94
|
+
|
|
95
|
+
_onResponse (store, headers) {
|
|
96
|
+
const status = headers && headers[HTTP2_HEADER_STATUS]
|
|
97
|
+
|
|
98
|
+
store.span.setTag(HTTP_STATUS_CODE, status)
|
|
99
|
+
|
|
100
|
+
if (!this.config.validateStatus(status)) {
|
|
101
|
+
storage.run(store, () => this.addError())
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
addHeaderTags(store.span, headers, HTTP_RESPONSE_HEADERS, this.config)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
_onError ({ span }, error) {
|
|
108
|
+
span.setTag('error', error)
|
|
109
|
+
span.finish()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
_onClose ({ span }) {
|
|
113
|
+
this.tagPeerService(span)
|
|
114
|
+
span.finish()
|
|
115
|
+
}
|
|
99
116
|
}
|
|
100
117
|
|
|
101
118
|
function extractSessionDetails (authority, options) {
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
const ProducerPlugin = require('../../dd-trace/src/plugins/producer')
|
|
4
4
|
const { encodePathwayContext } = require('../../dd-trace/src/datastreams/pathway')
|
|
5
|
+
const BOOTSTRAP_SERVERS_KEY = 'messaging.kafka.bootstrap.servers'
|
|
5
6
|
|
|
6
7
|
class KafkajsProducerPlugin extends ProducerPlugin {
|
|
7
8
|
static get id () { return 'kafkajs' }
|
|
8
9
|
static get operation () { return 'produce' }
|
|
10
|
+
static get peerServicePrecursors () { return [BOOTSTRAP_SERVERS_KEY] }
|
|
9
11
|
|
|
10
|
-
start ({ topic, messages }) {
|
|
12
|
+
start ({ topic, messages, bootstrapServers }) {
|
|
11
13
|
let pathwayCtx
|
|
12
14
|
if (this.config.dsmEnabled) {
|
|
13
15
|
const dataStreamsContext = this.tracer
|
|
@@ -24,6 +26,9 @@ class KafkajsProducerPlugin extends ProducerPlugin {
|
|
|
24
26
|
'kafka.batch_size': messages.length
|
|
25
27
|
}
|
|
26
28
|
})
|
|
29
|
+
if (bootstrapServers) {
|
|
30
|
+
span.setTag(BOOTSTRAP_SERVERS_KEY, bootstrapServers)
|
|
31
|
+
}
|
|
27
32
|
for (const message of messages) {
|
|
28
33
|
if (typeof message === 'object') {
|
|
29
34
|
if (this.config.dsmEnabled) message.headers['dd-pathway-ctx'] = pathwayCtx
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const DogStatsDClient = require('../../dd-trace/src/dogstatsd')
|
|
3
|
+
const { DogStatsDClient, NoopDogStatsDClient } = require('../../dd-trace/src/dogstatsd')
|
|
4
4
|
const ExternalLogger = require('../../dd-trace/src/external-logger/src')
|
|
5
5
|
|
|
6
6
|
const FLUSH_INTERVAL = 10 * 1000
|
|
@@ -10,15 +10,19 @@ let logger = null
|
|
|
10
10
|
let interval = null
|
|
11
11
|
|
|
12
12
|
module.exports.init = function (tracerConfig) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
if (tracerConfig.dogstatsd) {
|
|
14
|
+
metrics = new DogStatsDClient({
|
|
15
|
+
host: tracerConfig.dogstatsd.hostname,
|
|
16
|
+
port: tracerConfig.dogstatsd.port,
|
|
17
|
+
tags: [
|
|
18
|
+
`service:${tracerConfig.tags.service}`,
|
|
19
|
+
`env:${tracerConfig.tags.env}`,
|
|
20
|
+
`version:${tracerConfig.tags.version}`
|
|
21
|
+
]
|
|
22
|
+
})
|
|
23
|
+
} else {
|
|
24
|
+
metrics = new NoopDogStatsDClient()
|
|
25
|
+
}
|
|
22
26
|
|
|
23
27
|
logger = new ExternalLogger({
|
|
24
28
|
ddsource: 'openai',
|
|
@@ -5,6 +5,9 @@ const { COMMAND_INJECTION } = require('../vulnerabilities')
|
|
|
5
5
|
class CommandInjectionAnalyzer extends InjectionAnalyzer {
|
|
6
6
|
constructor () {
|
|
7
7
|
super(COMMAND_INJECTION)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
onConfigure () {
|
|
8
11
|
this.addSub('datadog:child_process:execution:start', ({ command }) => this.analyze(command))
|
|
9
12
|
}
|
|
10
13
|
}
|
|
@@ -9,7 +9,13 @@ class CookieAnalyzer extends Analyzer {
|
|
|
9
9
|
constructor (type, propertyToBeSafe) {
|
|
10
10
|
super(type)
|
|
11
11
|
this.propertyToBeSafe = propertyToBeSafe.toLowerCase()
|
|
12
|
-
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
onConfigure () {
|
|
15
|
+
this.addSub(
|
|
16
|
+
{ channelName: 'datadog:iast:set-cookie', moduleName: 'http' },
|
|
17
|
+
(cookieInfo) => this.analyze(cookieInfo)
|
|
18
|
+
)
|
|
13
19
|
}
|
|
14
20
|
|
|
15
21
|
_isVulnerable ({ cookieProperties, cookieValue }) {
|
|
@@ -5,6 +5,9 @@ const { LDAP_INJECTION } = require('../vulnerabilities')
|
|
|
5
5
|
class LdapInjectionAnalyzer extends InjectionAnalyzer {
|
|
6
6
|
constructor () {
|
|
7
7
|
super(LDAP_INJECTION)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
onConfigure () {
|
|
8
11
|
this.addSub('datadog:ldapjs:client:search', ({ base, filter }) => this.analyzeAll(base, filter))
|
|
9
12
|
}
|
|
10
13
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const path = require('path')
|
|
4
|
+
|
|
5
|
+
const InjectionAnalyzer = require('./injection-analyzer')
|
|
4
6
|
const { getIastContext } = require('../iast-context')
|
|
5
7
|
const { storage } = require('../../../../../datadog-core')
|
|
6
|
-
const InjectionAnalyzer = require('./injection-analyzer')
|
|
7
8
|
const { PATH_TRAVERSAL } = require('../vulnerabilities')
|
|
8
9
|
|
|
9
10
|
const ignoredOperations = ['dir.close', 'close']
|
|
@@ -11,7 +12,23 @@ const ignoredOperations = ['dir.close', 'close']
|
|
|
11
12
|
class PathTraversalAnalyzer extends InjectionAnalyzer {
|
|
12
13
|
constructor () {
|
|
13
14
|
super(PATH_TRAVERSAL)
|
|
14
|
-
|
|
15
|
+
|
|
16
|
+
this.exclusionList = [
|
|
17
|
+
path.join('node_modules', 'send') + path.sep
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
this.internalExclusionList = [
|
|
21
|
+
'node:fs',
|
|
22
|
+
'node:internal/fs',
|
|
23
|
+
'node:internal\\fs',
|
|
24
|
+
'fs.js',
|
|
25
|
+
'internal/fs',
|
|
26
|
+
'internal\\fs'
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
onConfigure () {
|
|
31
|
+
this.addSub('apm:fs:operation:start', (obj) => {
|
|
15
32
|
if (ignoredOperations.includes(obj.operation)) return
|
|
16
33
|
|
|
17
34
|
const pathArguments = []
|
|
@@ -44,19 +61,6 @@ class PathTraversalAnalyzer extends InjectionAnalyzer {
|
|
|
44
61
|
}
|
|
45
62
|
this.analyze(pathArguments)
|
|
46
63
|
})
|
|
47
|
-
|
|
48
|
-
this.exclusionList = [
|
|
49
|
-
path.join('node_modules', 'send') + path.sep
|
|
50
|
-
]
|
|
51
|
-
|
|
52
|
-
this.internalExclusionList = [
|
|
53
|
-
'node:fs',
|
|
54
|
-
'node:internal/fs',
|
|
55
|
-
'node:internal\\fs',
|
|
56
|
-
'fs.js',
|
|
57
|
-
'internal/fs',
|
|
58
|
-
'internal\\fs'
|
|
59
|
-
]
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
_isExcluded (location) {
|
|
@@ -13,6 +13,9 @@ const EXCLUDED_PATHS = getNodeModulesPaths('mysql2', 'sequelize')
|
|
|
13
13
|
class SqlInjectionAnalyzer extends InjectionAnalyzer {
|
|
14
14
|
constructor () {
|
|
15
15
|
super(SQL_INJECTION)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
onConfigure () {
|
|
16
19
|
this.addSub('apm:mysql:query:start', ({ sql }) => this.analyze(sql, 'MYSQL'))
|
|
17
20
|
this.addSub('apm:mysql2:query:start', ({ sql }) => this.analyze(sql, 'MYSQL'))
|
|
18
21
|
this.addSub('apm:pg:query:start', ({ query }) => this.analyze(query.text, 'POSTGRES'))
|
|
@@ -41,10 +44,9 @@ class SqlInjectionAnalyzer extends InjectionAnalyzer {
|
|
|
41
44
|
|
|
42
45
|
analyze (value, dialect) {
|
|
43
46
|
const store = storage.getStore()
|
|
44
|
-
|
|
45
47
|
if (!(store && store.sqlAnalyzed)) {
|
|
46
48
|
const iastContext = getIastContext(store)
|
|
47
|
-
if (store
|
|
49
|
+
if (this._isInvalidContext(store, iastContext)) return
|
|
48
50
|
this._reportIfVulnerable(value, iastContext, dialect)
|
|
49
51
|
}
|
|
50
52
|
}
|
|
@@ -66,6 +68,7 @@ class SqlInjectionAnalyzer extends InjectionAnalyzer {
|
|
|
66
68
|
addVulnerability(context, vulnerability)
|
|
67
69
|
}
|
|
68
70
|
}
|
|
71
|
+
|
|
69
72
|
_getExcludedPaths () {
|
|
70
73
|
return EXCLUDED_PATHS
|
|
71
74
|
}
|
|
@@ -6,7 +6,9 @@ const { SSRF } = require('../vulnerabilities')
|
|
|
6
6
|
class SSRFAnalyzer extends InjectionAnalyzer {
|
|
7
7
|
constructor () {
|
|
8
8
|
super(SSRF)
|
|
9
|
+
}
|
|
9
10
|
|
|
11
|
+
onConfigure () {
|
|
10
12
|
this.addSub('apm:http:client:request:start', ({ args }) => {
|
|
11
13
|
if (typeof args.originalUrl === 'string') {
|
|
12
14
|
this.analyze(args.originalUrl)
|
|
@@ -4,14 +4,16 @@ const InjectionAnalyzer = require('./injection-analyzer')
|
|
|
4
4
|
const { UNVALIDATED_REDIRECT } = require('../vulnerabilities')
|
|
5
5
|
const { getNodeModulesPaths } = require('../path-line')
|
|
6
6
|
const { getRanges } = require('../taint-tracking/operations')
|
|
7
|
-
const { HTTP_REQUEST_HEADER_VALUE } = require('../taint-tracking/
|
|
7
|
+
const { HTTP_REQUEST_HEADER_VALUE } = require('../taint-tracking/source-types')
|
|
8
8
|
|
|
9
9
|
const EXCLUDED_PATHS = getNodeModulesPaths('express/lib/response.js')
|
|
10
10
|
|
|
11
11
|
class UnvalidatedRedirectAnalyzer extends InjectionAnalyzer {
|
|
12
12
|
constructor () {
|
|
13
13
|
super(UNVALIDATED_REDIRECT)
|
|
14
|
+
}
|
|
14
15
|
|
|
16
|
+
onConfigure () {
|
|
15
17
|
this.addSub('datadog:http:server:response:set-header:finish', ({ name, value }) => this.analyze(name, value))
|
|
16
18
|
}
|
|
17
19
|
|
|
@@ -1,33 +1,18 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const Plugin = require('../../../../src/plugins/plugin')
|
|
4
3
|
const { storage } = require('../../../../../datadog-core')
|
|
5
|
-
const iastLog = require('../iast-log')
|
|
6
4
|
const { getFirstNonDDPathAndLine } = require('../path-line')
|
|
7
5
|
const { addVulnerability } = require('../vulnerability-reporter')
|
|
8
6
|
const { getIastContext } = require('../iast-context')
|
|
9
7
|
const overheadController = require('../overhead-controller')
|
|
8
|
+
const { SinkIastPlugin } = require('../iast-plugin')
|
|
10
9
|
|
|
11
|
-
class Analyzer extends
|
|
10
|
+
class Analyzer extends SinkIastPlugin {
|
|
12
11
|
constructor (type) {
|
|
13
12
|
super()
|
|
14
13
|
this._type = type
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
_wrapHandler (handler) {
|
|
18
|
-
return (message, name) => {
|
|
19
|
-
try {
|
|
20
|
-
handler(message, name)
|
|
21
|
-
} catch (e) {
|
|
22
|
-
iastLog.errorAndPublish(e)
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
addSub (channelName, handler) {
|
|
28
|
-
super.addSub(channelName, this._wrapHandler(handler))
|
|
29
|
-
}
|
|
30
|
-
|
|
31
16
|
_isVulnerable (value, context) {
|
|
32
17
|
return false
|
|
33
18
|
}
|
|
@@ -64,17 +49,23 @@ class Analyzer extends Plugin {
|
|
|
64
49
|
|
|
65
50
|
_getExcludedPaths () {}
|
|
66
51
|
|
|
52
|
+
_isInvalidContext (store, iastContext) {
|
|
53
|
+
return store && !iastContext
|
|
54
|
+
}
|
|
55
|
+
|
|
67
56
|
analyze (value) {
|
|
68
57
|
const store = storage.getStore()
|
|
69
58
|
const iastContext = getIastContext(store)
|
|
70
|
-
if (store
|
|
59
|
+
if (this._isInvalidContext(store, iastContext)) return
|
|
60
|
+
|
|
71
61
|
this._reportIfVulnerable(value, iastContext)
|
|
72
62
|
}
|
|
73
63
|
|
|
74
64
|
analyzeAll (...values) {
|
|
75
65
|
const store = storage.getStore()
|
|
76
66
|
const iastContext = getIastContext(store)
|
|
77
|
-
if (store
|
|
67
|
+
if (this._isInvalidContext(store, iastContext)) return
|
|
68
|
+
|
|
78
69
|
for (let i = 0; i < values.length; i++) {
|
|
79
70
|
const value = values[i]
|
|
80
71
|
if (this._isVulnerable(value, iastContext)) {
|
|
@@ -119,6 +110,14 @@ class Analyzer extends Plugin {
|
|
|
119
110
|
}
|
|
120
111
|
return hash
|
|
121
112
|
}
|
|
113
|
+
|
|
114
|
+
addSub (iastSubOrChannelName, handler) {
|
|
115
|
+
const iastSub = typeof iastSubOrChannelName === 'string'
|
|
116
|
+
? { channelName: iastSubOrChannelName }
|
|
117
|
+
: iastSubOrChannelName
|
|
118
|
+
|
|
119
|
+
super.addSub({ tag: this._type, ...iastSub }, handler)
|
|
120
|
+
}
|
|
122
121
|
}
|
|
123
122
|
|
|
124
123
|
module.exports = Analyzer
|