dd-trace 3.27.0 → 3.29.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/README.md +2 -2
- package/package.json +4 -4
- package/packages/datadog-core/src/storage/async_resource.js +4 -0
- package/packages/datadog-instrumentations/src/couchbase.js +4 -4
- 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 +9 -5
- package/packages/datadog-instrumentations/src/mocha.js +5 -3
- package/packages/datadog-plugin-cypress/src/plugin.js +4 -2
- package/packages/datadog-plugin-graphql/src/execute.js +6 -4
- 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-jest/src/index.js +8 -3
- package/packages/datadog-plugin-openai/src/index.js +39 -16
- package/packages/datadog-plugin-openai/src/services.js +13 -9
- package/packages/datadog-plugin-router/src/index.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +3 -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/hsts-header-missing-analyzer.js +45 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/index.js +3 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/ldap-injection-analyzer.js +3 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/missing-header-analyzer.js +66 -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 +27 -8
- 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/analyzers/xcontenttype-header-missing-analyzer.js +19 -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 +11 -7
- 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 +7 -5
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +23 -4
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +49 -17
- 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/{origin-types.js → source-types.js} +1 -0
- 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/appsec/iast/vulnerabilities-formatter/index.js +5 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +3 -1
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
- package/packages/dd-trace/src/config.js +47 -12
- package/packages/dd-trace/src/constants.js +1 -0
- package/packages/dd-trace/src/external-logger/src/index.js +9 -1
- package/packages/dd-trace/src/external-logger/test/index.spec.js +1 -1
- package/packages/dd-trace/src/format.js +1 -1
- package/packages/dd-trace/src/lambda/handler.js +8 -1
- package/packages/dd-trace/src/opentelemetry/span.js +3 -1
- package/packages/dd-trace/src/opentracing/span_context.js +2 -1
- 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 +3 -2
- 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/plugins/util/web.js +1 -0
- package/packages/dd-trace/src/profiling/config.js +8 -8
- package/packages/dd-trace/src/profiling/exporters/agent.js +4 -1
- package/packages/dd-trace/src/profiling/index.js +0 -2
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/wall.js +162 -10
- package/packages/dd-trace/src/service-naming/index.js +2 -2
- package/packages/dd-trace/src/service-naming/schemas/v0/graphql.js +12 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/index.js +2 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/graphql.js +12 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/index.js +2 -1
- package/packages/dd-trace/src/span_processor.js +0 -4
- package/packages/dd-trace/src/span_sampler.js +1 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +24 -12
- package/packages/dd-trace/src/telemetry/metrics.js +11 -1
- package/packages/diagnostics_channel/src/index.js +64 -0
- package/scripts/install_plugin_modules.js +1 -0
- package/packages/dd-trace/src/profiling/profilers/cpu.js +0 -126
- package/scripts/version.js +0 -66
|
@@ -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) {
|
|
@@ -166,9 +166,12 @@ class JestPlugin extends CiPlugin {
|
|
|
166
166
|
this.enter(span, store)
|
|
167
167
|
})
|
|
168
168
|
|
|
169
|
-
this.addSub('ci:jest:test:finish', (status) => {
|
|
169
|
+
this.addSub('ci:jest:test:finish', ({ status, testStartLine }) => {
|
|
170
170
|
const span = storage.getStore().span
|
|
171
171
|
span.setTag(TEST_STATUS, status)
|
|
172
|
+
if (testStartLine) {
|
|
173
|
+
span.setTag(TEST_SOURCE_START, testStartLine)
|
|
174
|
+
}
|
|
172
175
|
span.finish()
|
|
173
176
|
finishAllTraceSpans(span)
|
|
174
177
|
})
|
|
@@ -197,8 +200,10 @@ class JestPlugin extends CiPlugin {
|
|
|
197
200
|
const extraTags = {
|
|
198
201
|
[JEST_TEST_RUNNER]: runner,
|
|
199
202
|
[TEST_PARAMETERS]: testParameters,
|
|
200
|
-
[TEST_FRAMEWORK_VERSION]: frameworkVersion
|
|
201
|
-
|
|
203
|
+
[TEST_FRAMEWORK_VERSION]: frameworkVersion
|
|
204
|
+
}
|
|
205
|
+
if (testStartLine) {
|
|
206
|
+
extraTags[TEST_SOURCE_START] = testStartLine
|
|
202
207
|
}
|
|
203
208
|
|
|
204
209
|
return super.startTestSpan(name, suite, this.testSuiteSpan, extraTags)
|
|
@@ -8,6 +8,10 @@ const services = require('./services')
|
|
|
8
8
|
const Sampler = require('../../dd-trace/src/sampler')
|
|
9
9
|
const { MEASURED } = require('../../../ext/tags')
|
|
10
10
|
|
|
11
|
+
// String#replaceAll unavailable on Node.js@v14 (dd-trace@<=v3)
|
|
12
|
+
const RE_NEWLINE = /\n/g
|
|
13
|
+
const RE_TAB = /\t/g
|
|
14
|
+
|
|
11
15
|
// TODO: In the future we should refactor config.js to make it requirable
|
|
12
16
|
let MAX_TEXT_LEN = 128
|
|
13
17
|
|
|
@@ -26,7 +30,9 @@ class OpenApiPlugin extends TracingPlugin {
|
|
|
26
30
|
this.sampler = new Sampler(0.1) // default 10% log sampling
|
|
27
31
|
|
|
28
32
|
// hoist the max length env var to avoid making all of these functions a class method
|
|
29
|
-
|
|
33
|
+
if (this._tracerConfig) {
|
|
34
|
+
MAX_TEXT_LEN = this._tracerConfig.openaiSpanCharLimit
|
|
35
|
+
}
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
configure (config) {
|
|
@@ -83,11 +89,11 @@ class OpenApiPlugin extends TracingPlugin {
|
|
|
83
89
|
store.prompt = prompt
|
|
84
90
|
if (typeof prompt === 'string' || (Array.isArray(prompt) && typeof prompt[0] === 'number')) {
|
|
85
91
|
// This is a single prompt, either String or [Number]
|
|
86
|
-
tags[`openai.request.prompt`] = normalizeStringOrTokenArray(prompt)
|
|
92
|
+
tags[`openai.request.prompt`] = normalizeStringOrTokenArray(prompt, true)
|
|
87
93
|
} else if (Array.isArray(prompt)) {
|
|
88
94
|
// This is multiple prompts, either [String] or [[Number]]
|
|
89
95
|
for (let i = 0; i < prompt.length; i++) {
|
|
90
|
-
tags[`openai.request.prompt.${i}`] = normalizeStringOrTokenArray(prompt[i])
|
|
96
|
+
tags[`openai.request.prompt.${i}`] = normalizeStringOrTokenArray(prompt[i], true)
|
|
91
97
|
}
|
|
92
98
|
}
|
|
93
99
|
}
|
|
@@ -363,6 +369,8 @@ function retrieveModelResponseExtraction (tags, body) {
|
|
|
363
369
|
tags['openai.response.parent'] = body.parent
|
|
364
370
|
tags['openai.response.root'] = body.root
|
|
365
371
|
|
|
372
|
+
if (!body.permission) return
|
|
373
|
+
|
|
366
374
|
tags['openai.response.permission.id'] = body.permission[0].id
|
|
367
375
|
tags['openai.response.permission.created'] = body.permission[0].created
|
|
368
376
|
tags['openai.response.permission.allow_create_engine'] = body.permission[0].allow_create_engine
|
|
@@ -382,10 +390,14 @@ function commonLookupFineTuneRequestExtraction (tags, body) {
|
|
|
382
390
|
}
|
|
383
391
|
|
|
384
392
|
function listModelsResponseExtraction (tags, body) {
|
|
393
|
+
if (!body.data) return
|
|
394
|
+
|
|
385
395
|
tags['openai.response.count'] = body.data.length
|
|
386
396
|
}
|
|
387
397
|
|
|
388
398
|
function commonImageResponseExtraction (tags, body) {
|
|
399
|
+
if (!body.data) return
|
|
400
|
+
|
|
389
401
|
tags['openai.response.images_count'] = body.data.length
|
|
390
402
|
|
|
391
403
|
for (let i = 0; i < body.data.length; i++) {
|
|
@@ -400,7 +412,7 @@ function createAudioResponseExtraction (tags, body) {
|
|
|
400
412
|
tags['openai.response.text'] = body.text
|
|
401
413
|
tags['openai.response.language'] = body.language
|
|
402
414
|
tags['openai.response.duration'] = body.duration
|
|
403
|
-
tags['openai.response.segments_count'] = body.segments
|
|
415
|
+
tags['openai.response.segments_count'] = defensiveArrayLength(body.segments)
|
|
404
416
|
}
|
|
405
417
|
|
|
406
418
|
function createFineTuneRequestExtraction (tags, body) {
|
|
@@ -417,21 +429,24 @@ function createFineTuneRequestExtraction (tags, body) {
|
|
|
417
429
|
}
|
|
418
430
|
|
|
419
431
|
function commonFineTuneResponseExtraction (tags, body) {
|
|
420
|
-
tags['openai.response.events_count'] = body.events
|
|
432
|
+
tags['openai.response.events_count'] = defensiveArrayLength(body.events)
|
|
421
433
|
tags['openai.response.fine_tuned_model'] = body.fine_tuned_model
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
tags['openai.response.
|
|
434
|
+
if (body.hyperparams) {
|
|
435
|
+
tags['openai.response.hyperparams.n_epochs'] = body.hyperparams.n_epochs
|
|
436
|
+
tags['openai.response.hyperparams.batch_size'] = body.hyperparams.batch_size
|
|
437
|
+
tags['openai.response.hyperparams.prompt_loss_weight'] = body.hyperparams.prompt_loss_weight
|
|
438
|
+
tags['openai.response.hyperparams.learning_rate_multiplier'] = body.hyperparams.learning_rate_multiplier
|
|
439
|
+
}
|
|
440
|
+
tags['openai.response.training_files_count'] = defensiveArrayLength(body.training_files)
|
|
441
|
+
tags['openai.response.result_files_count'] = defensiveArrayLength(body.result_files)
|
|
442
|
+
tags['openai.response.validation_files_count'] = defensiveArrayLength(body.validation_files)
|
|
429
443
|
tags['openai.response.updated_at'] = body.updated_at
|
|
430
444
|
tags['openai.response.status'] = body.status
|
|
431
445
|
}
|
|
432
446
|
|
|
433
447
|
// the OpenAI package appears to stream the content download then provide it all as a singular string
|
|
434
448
|
function downloadFileResponseExtraction (tags, body) {
|
|
449
|
+
if (!body.file) return
|
|
435
450
|
tags['openai.response.total_bytes'] = body.file.length
|
|
436
451
|
}
|
|
437
452
|
|
|
@@ -472,6 +487,8 @@ function createRetrieveFileResponseExtraction (tags, body) {
|
|
|
472
487
|
function createEmbeddingResponseExtraction (tags, body) {
|
|
473
488
|
usageExtraction(tags, body)
|
|
474
489
|
|
|
490
|
+
if (!body.data) return
|
|
491
|
+
|
|
475
492
|
tags['openai.response.embeddings_count'] = body.data.length
|
|
476
493
|
for (let i = 0; i < body.data.length; i++) {
|
|
477
494
|
tags[`openai.response.embedding.${i}.embedding_length`] = body.data[i].embedding.length
|
|
@@ -479,6 +496,7 @@ function createEmbeddingResponseExtraction (tags, body) {
|
|
|
479
496
|
}
|
|
480
497
|
|
|
481
498
|
function commonListCountResponseExtraction (tags, body) {
|
|
499
|
+
if (!body.data) return
|
|
482
500
|
tags['openai.response.count'] = body.data.length
|
|
483
501
|
}
|
|
484
502
|
|
|
@@ -486,6 +504,9 @@ function commonListCountResponseExtraction (tags, body) {
|
|
|
486
504
|
function createModerationResponseExtraction (tags, body) {
|
|
487
505
|
tags['openai.response.id'] = body.id
|
|
488
506
|
// tags[`openai.response.model`] = body.model // redundant, already extracted globally
|
|
507
|
+
|
|
508
|
+
if (!body.results) return
|
|
509
|
+
|
|
489
510
|
tags['openai.response.flagged'] = body.results[0].flagged
|
|
490
511
|
|
|
491
512
|
for (const [category, match] of Object.entries(body.results[0].categories)) {
|
|
@@ -501,6 +522,8 @@ function createModerationResponseExtraction (tags, body) {
|
|
|
501
522
|
function commonCreateResponseExtraction (tags, body, store) {
|
|
502
523
|
usageExtraction(tags, body)
|
|
503
524
|
|
|
525
|
+
if (!body.choices) return
|
|
526
|
+
|
|
504
527
|
tags['openai.response.choices_count'] = body.choices.length
|
|
505
528
|
|
|
506
529
|
store.choices = body.choices
|
|
@@ -530,7 +553,7 @@ function usageExtraction (tags, body) {
|
|
|
530
553
|
}
|
|
531
554
|
|
|
532
555
|
function truncateApiKey (apiKey) {
|
|
533
|
-
return `sk-...${apiKey.substr(apiKey.length - 4)}`
|
|
556
|
+
return apiKey && `sk-...${apiKey.substr(apiKey.length - 4)}`
|
|
534
557
|
}
|
|
535
558
|
|
|
536
559
|
/**
|
|
@@ -540,8 +563,8 @@ function truncateText (text) {
|
|
|
540
563
|
if (!text) return
|
|
541
564
|
|
|
542
565
|
text = text
|
|
543
|
-
.
|
|
544
|
-
.
|
|
566
|
+
.replace(RE_NEWLINE, '\\n')
|
|
567
|
+
.replace(RE_TAB, '\\t')
|
|
545
568
|
|
|
546
569
|
if (text.length > MAX_TEXT_LEN) {
|
|
547
570
|
return text.substring(0, MAX_TEXT_LEN) + '...'
|
|
@@ -671,7 +694,7 @@ function normalizeRequestPayload (methodName, args) {
|
|
|
671
694
|
* "foo" -> "foo"
|
|
672
695
|
* [1,2,3] -> "[1, 2, 3]"
|
|
673
696
|
*/
|
|
674
|
-
function normalizeStringOrTokenArray (input, truncate
|
|
697
|
+
function normalizeStringOrTokenArray (input, truncate) {
|
|
675
698
|
const normalized = Array.isArray(input)
|
|
676
699
|
? `[${input.join(', ')}]` // "[1, 2, 999]"
|
|
677
700
|
: input // "foo"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { DogStatsDClient, NoopDogStatsDClient } = require('../../dd-trace/src/dogstatsd')
|
|
4
|
-
const ExternalLogger = require('../../dd-trace/src/external-logger/src')
|
|
4
|
+
const { ExternalLogger, NoopExternalLogger } = require('../../dd-trace/src/external-logger/src')
|
|
5
5
|
|
|
6
6
|
const FLUSH_INTERVAL = 10 * 1000
|
|
7
7
|
|
|
@@ -10,7 +10,7 @@ let logger = null
|
|
|
10
10
|
let interval = null
|
|
11
11
|
|
|
12
12
|
module.exports.init = function (tracerConfig) {
|
|
13
|
-
if (tracerConfig.dogstatsd) {
|
|
13
|
+
if (tracerConfig && tracerConfig.dogstatsd) {
|
|
14
14
|
metrics = new DogStatsDClient({
|
|
15
15
|
host: tracerConfig.dogstatsd.hostname,
|
|
16
16
|
port: tracerConfig.dogstatsd.port,
|
|
@@ -24,13 +24,17 @@ module.exports.init = function (tracerConfig) {
|
|
|
24
24
|
metrics = new NoopDogStatsDClient()
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
if (tracerConfig && tracerConfig.apiKey) {
|
|
28
|
+
logger = new ExternalLogger({
|
|
29
|
+
ddsource: 'openai',
|
|
30
|
+
hostname: tracerConfig.hostname,
|
|
31
|
+
service: tracerConfig.service,
|
|
32
|
+
apiKey: tracerConfig.apiKey,
|
|
33
|
+
interval: FLUSH_INTERVAL
|
|
34
|
+
})
|
|
35
|
+
} else {
|
|
36
|
+
logger = new NoopExternalLogger()
|
|
37
|
+
}
|
|
34
38
|
|
|
35
39
|
interval = setInterval(() => {
|
|
36
40
|
metrics.flush()
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module.exports = {
|
|
4
4
|
'COMMAND_INJECTION_ANALYZER': require('./command-injection-analyzer'),
|
|
5
|
+
'HSTS_HEADER_MISSING_ANALYZER': require('./hsts-header-missing-analyzer'),
|
|
5
6
|
'INSECURE_COOKIE_ANALYZER': require('./insecure-cookie-analyzer'),
|
|
6
7
|
'LDAP_ANALYZER': require('./ldap-injection-analyzer'),
|
|
7
8
|
'NO_HTTPONLY_COOKIE_ANALYZER': require('./no-httponly-cookie-analyzer'),
|
|
@@ -11,5 +12,6 @@ module.exports = {
|
|
|
11
12
|
'SSRF': require('./ssrf-analyzer'),
|
|
12
13
|
'UNVALIDATED_REDIRECT_ANALYZER': require('./unvalidated-redirect-analyzer'),
|
|
13
14
|
'WEAK_CIPHER_ANALYZER': require('./weak-cipher-analyzer'),
|
|
14
|
-
'WEAK_HASH_ANALYZER': require('./weak-hash-analyzer')
|
|
15
|
+
'WEAK_HASH_ANALYZER': require('./weak-hash-analyzer'),
|
|
16
|
+
'XCONTENTTYPE_HEADER_MISSING_ANALYZER': require('./xcontenttype-header-missing-analyzer')
|
|
15
17
|
}
|
|
@@ -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 }) {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { HSTS_HEADER_MISSING } = require('../vulnerabilities')
|
|
4
|
+
const { MissingHeaderAnalyzer } = require('./missing-header-analyzer')
|
|
5
|
+
|
|
6
|
+
const HSTS_HEADER_NAME = 'Strict-Transport-Security'
|
|
7
|
+
const HEADER_VALID_PREFIX = 'max-age'
|
|
8
|
+
class HstsHeaderMissingAnalyzer extends MissingHeaderAnalyzer {
|
|
9
|
+
constructor () {
|
|
10
|
+
super(HSTS_HEADER_MISSING, HSTS_HEADER_NAME)
|
|
11
|
+
}
|
|
12
|
+
_isVulnerableFromRequestAndResponse (req, res) {
|
|
13
|
+
const headerToCheck = res.getHeader(HSTS_HEADER_NAME)
|
|
14
|
+
return !this._isHeaderValid(headerToCheck) && this._isHttpsProtocol(req)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
_isHeaderValid (headerValue) {
|
|
18
|
+
if (!headerValue) {
|
|
19
|
+
return false
|
|
20
|
+
}
|
|
21
|
+
headerValue = headerValue.trim()
|
|
22
|
+
|
|
23
|
+
if (!headerValue.startsWith(HEADER_VALID_PREFIX)) {
|
|
24
|
+
return false
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const semicolonIndex = headerValue.indexOf(';')
|
|
28
|
+
let timestampString
|
|
29
|
+
if (semicolonIndex > -1) {
|
|
30
|
+
timestampString = headerValue.substring(HEADER_VALID_PREFIX.length + 1, semicolonIndex)
|
|
31
|
+
} else {
|
|
32
|
+
timestampString = headerValue.substring(HEADER_VALID_PREFIX.length + 1)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const timestamp = parseInt(timestampString)
|
|
36
|
+
// eslint-disable-next-line eqeqeq
|
|
37
|
+
return timestamp == timestampString && timestamp > 0
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
_isHttpsProtocol (req) {
|
|
41
|
+
return req.protocol === 'https' || req.headers['x-forwarded-proto'] === 'https'
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = new HstsHeaderMissingAnalyzer()
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
const analyzers = require('./analyzers')
|
|
4
4
|
const setCookiesHeaderInterceptor = require('./set-cookies-header-interceptor')
|
|
5
5
|
|
|
6
|
-
function enableAllAnalyzers () {
|
|
7
|
-
setCookiesHeaderInterceptor.configure(true)
|
|
6
|
+
function enableAllAnalyzers (tracerConfig) {
|
|
7
|
+
setCookiesHeaderInterceptor.configure({ enabled: true, tracerConfig })
|
|
8
8
|
for (const analyzer in analyzers) {
|
|
9
|
-
analyzers[analyzer].configure(true)
|
|
9
|
+
analyzers[analyzer].configure({ enabled: true, tracerConfig })
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -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
|
}
|