dd-trace 2.37.0 → 2.39.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/LICENSE-3rdparty.csv +4 -3
- package/index.d.ts +27 -0
- package/package.json +4 -4
- package/packages/datadog-instrumentations/src/aws-sdk.js +5 -0
- package/packages/datadog-instrumentations/src/cassandra-driver.js +6 -3
- package/packages/datadog-instrumentations/src/elasticsearch.js +39 -1
- package/packages/datadog-instrumentations/src/express.js +23 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
- package/packages/datadog-instrumentations/src/kafkajs.js +2 -2
- package/packages/datadog-instrumentations/src/openai.js +50 -0
- package/packages/datadog-instrumentations/src/opensearch.js +2 -1
- package/packages/datadog-instrumentations/src/passport-http.js +22 -0
- package/packages/datadog-instrumentations/src/passport-local.js +22 -0
- package/packages/datadog-instrumentations/src/passport-utils.js +36 -0
- package/packages/datadog-instrumentations/src/pg.js +17 -4
- package/packages/datadog-plugin-aws-sdk/src/base.js +3 -3
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -0
- package/packages/datadog-plugin-cassandra-driver/src/index.js +6 -6
- package/packages/datadog-plugin-dns/src/lookup.js +1 -1
- package/packages/datadog-plugin-elasticsearch/src/index.js +2 -2
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +1 -1
- package/packages/datadog-plugin-graphql/src/parse.js +1 -1
- package/packages/datadog-plugin-graphql/src/resolve.js +0 -5
- package/packages/datadog-plugin-graphql/src/validate.js +1 -1
- package/packages/datadog-plugin-grpc/src/client.js +9 -3
- package/packages/datadog-plugin-grpc/src/server.js +3 -3
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-http/src/server.js +38 -34
- package/packages/datadog-plugin-http2/src/client.js +0 -5
- package/packages/datadog-plugin-http2/src/server.js +23 -23
- package/packages/datadog-plugin-kafkajs/src/consumer.js +6 -1
- package/packages/datadog-plugin-kafkajs/src/producer.js +8 -1
- package/packages/datadog-plugin-mocha/src/index.js +3 -3
- package/packages/datadog-plugin-moleculer/src/client.js +3 -3
- package/packages/datadog-plugin-moleculer/src/server.js +2 -2
- package/packages/datadog-plugin-mongodb-core/src/index.js +15 -4
- package/packages/datadog-plugin-next/src/index.js +50 -52
- package/packages/datadog-plugin-openai/src/index.js +685 -0
- package/packages/datadog-plugin-openai/src/services.js +43 -0
- package/packages/datadog-plugin-oracledb/src/index.js +3 -10
- package/packages/datadog-plugin-pg/src/index.js +3 -11
- package/packages/datadog-plugin-sharedb/src/index.js +1 -1
- package/packages/dd-trace/src/appsec/channels.js +1 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/origin-types.js +3 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +12 -2
- package/packages/dd-trace/src/appsec/index.js +20 -0
- package/packages/dd-trace/src/appsec/passport.js +110 -0
- package/packages/dd-trace/src/appsec/sdk/track_event.js +14 -5
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +17 -4
- package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +45 -0
- package/packages/dd-trace/src/config.js +38 -1
- package/packages/dd-trace/src/constants.js +2 -0
- package/packages/dd-trace/src/data_streams_context.js +15 -0
- package/packages/dd-trace/src/datastreams/pathway.js +58 -0
- package/packages/dd-trace/src/datastreams/processor.js +194 -0
- package/packages/dd-trace/src/datastreams/writer.js +66 -0
- package/packages/dd-trace/src/dogstatsd.js +12 -4
- package/packages/dd-trace/src/external-logger/src/index.js +4 -0
- package/packages/dd-trace/src/opentelemetry/span.js +1 -0
- package/packages/dd-trace/src/opentracing/span.js +32 -0
- package/packages/dd-trace/src/opentracing/tracer.js +3 -1
- package/packages/dd-trace/src/plugin_manager.js +7 -2
- package/packages/dd-trace/src/plugins/client.js +1 -0
- package/packages/dd-trace/src/plugins/database.js +2 -1
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/outbound.js +59 -1
- package/packages/dd-trace/src/plugins/server.js +2 -0
- package/packages/dd-trace/src/plugins/tracing.js +5 -1
- package/packages/dd-trace/src/plugins/util/exec.js +2 -0
- package/packages/dd-trace/src/plugins/util/git.js +38 -10
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +36 -2
- package/packages/dd-trace/src/profiling/config.js +34 -7
- package/packages/dd-trace/src/proxy.js +6 -0
- package/packages/dd-trace/src/service-naming/index.js +13 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/index.js +2 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +34 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/web.js +27 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/index.js +2 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +31 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +26 -0
- package/packages/dd-trace/src/telemetry/index.js +3 -0
- package/packages/dd-trace/src/telemetry/metrics.js +281 -0
- package/packages/dd-trace/src/tracer.js +19 -1
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const DogStatsDClient = require('../../dd-trace/src/dogstatsd')
|
|
4
|
+
const ExternalLogger = require('../../dd-trace/src/external-logger/src')
|
|
5
|
+
|
|
6
|
+
const FLUSH_INTERVAL = 10 * 1000
|
|
7
|
+
|
|
8
|
+
let metrics = null
|
|
9
|
+
let logger = null
|
|
10
|
+
let interval = null
|
|
11
|
+
|
|
12
|
+
module.exports.init = function (tracerConfig) {
|
|
13
|
+
metrics = new DogStatsDClient({
|
|
14
|
+
host: tracerConfig.dogstatsd.hostname,
|
|
15
|
+
port: tracerConfig.dogstatsd.port,
|
|
16
|
+
tags: [
|
|
17
|
+
`service:${tracerConfig.tags.service}`,
|
|
18
|
+
`env:${tracerConfig.tags.env}`,
|
|
19
|
+
`version:${tracerConfig.tags.version}`
|
|
20
|
+
]
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
logger = new ExternalLogger({
|
|
24
|
+
ddsource: 'openai',
|
|
25
|
+
hostname: tracerConfig.hostname,
|
|
26
|
+
service: tracerConfig.service,
|
|
27
|
+
apiKey: tracerConfig.apiKey,
|
|
28
|
+
interval: FLUSH_INTERVAL
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
interval = setInterval(() => {
|
|
32
|
+
metrics.flush()
|
|
33
|
+
}, FLUSH_INTERVAL).unref()
|
|
34
|
+
|
|
35
|
+
return { metrics, logger }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports.shutdown = function () {
|
|
39
|
+
clearInterval(interval)
|
|
40
|
+
metrics = null
|
|
41
|
+
logger = null
|
|
42
|
+
interval = null
|
|
43
|
+
}
|
|
@@ -7,12 +7,13 @@ const log = require('../../dd-trace/src/log')
|
|
|
7
7
|
class OracledbPlugin extends DatabasePlugin {
|
|
8
8
|
static get id () { return 'oracledb' }
|
|
9
9
|
static get system () { return 'oracle' }
|
|
10
|
+
static get peerServicePrecursors () { return ['db.instance', 'db.hostname'] }
|
|
10
11
|
|
|
11
12
|
start ({ query, connAttrs }) {
|
|
12
|
-
const service =
|
|
13
|
+
const service = this.serviceName(this.config, connAttrs)
|
|
13
14
|
const url = getUrl(connAttrs.connectString)
|
|
14
15
|
|
|
15
|
-
this.startSpan(
|
|
16
|
+
this.startSpan(this.operationName(), {
|
|
16
17
|
service,
|
|
17
18
|
resource: query,
|
|
18
19
|
type: 'sql',
|
|
@@ -27,14 +28,6 @@ class OracledbPlugin extends DatabasePlugin {
|
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
function getServiceName (config, connAttrs) {
|
|
31
|
-
if (typeof config.service === 'function') {
|
|
32
|
-
return config.service(connAttrs)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return config.service
|
|
36
|
-
}
|
|
37
|
-
|
|
38
31
|
// TODO: Avoid creating an error since it's a heavy operation.
|
|
39
32
|
function getUrl (connectString) {
|
|
40
33
|
try {
|
|
@@ -9,10 +9,10 @@ class PGPlugin extends DatabasePlugin {
|
|
|
9
9
|
static get system () { return 'postgres' }
|
|
10
10
|
|
|
11
11
|
start ({ params = {}, query, processId }) {
|
|
12
|
-
const service =
|
|
12
|
+
const service = this.serviceName(this.config, params)
|
|
13
13
|
const originalStatement = query.text
|
|
14
14
|
|
|
15
|
-
this.startSpan(
|
|
15
|
+
this.startSpan(this.operationName(), {
|
|
16
16
|
service,
|
|
17
17
|
resource: originalStatement,
|
|
18
18
|
type: 'sql',
|
|
@@ -27,16 +27,8 @@ class PGPlugin extends DatabasePlugin {
|
|
|
27
27
|
}
|
|
28
28
|
})
|
|
29
29
|
|
|
30
|
-
query.
|
|
30
|
+
query.__ddInjectableQuery = this.injectDbmQuery(query.text, service, !!query.name)
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
function getServiceName (tracer, params) {
|
|
35
|
-
if (typeof tracer.config.service === 'function') {
|
|
36
|
-
return tracer.config.service(params)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return tracer.config.service || `${tracer._tracer._tracer._service}-postgres`
|
|
40
|
-
}
|
|
41
|
-
|
|
42
34
|
module.exports = PGPlugin
|
|
@@ -7,6 +7,7 @@ module.exports = {
|
|
|
7
7
|
bodyParser: dc.channel('datadog:body-parser:read:finish'),
|
|
8
8
|
incomingHttpRequestStart: dc.channel('dd-trace:incomingHttpRequestStart'),
|
|
9
9
|
incomingHttpRequestEnd: dc.channel('dd-trace:incomingHttpRequestEnd'),
|
|
10
|
+
passportVerify: dc.channel('datadog:passport:verify:finish'),
|
|
10
11
|
queryParser: dc.channel('datadog:query:read:finish'),
|
|
11
12
|
setCookieChannel: dc.channel('datadog:iast:set-cookie')
|
|
12
13
|
}
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
module.exports = {
|
|
4
4
|
HTTP_REQUEST_BODY: 'http.request.body',
|
|
5
|
-
HTTP_REQUEST_PARAMETER: 'http.request.parameter',
|
|
6
5
|
HTTP_REQUEST_COOKIE_VALUE: 'http.request.cookie.value',
|
|
7
6
|
HTTP_REQUEST_COOKIE_NAME: 'http.request.cookie.name',
|
|
8
7
|
HTTP_REQUEST_HEADER_NAME: 'http.request.header.name',
|
|
9
|
-
HTTP_REQUEST_HEADER_VALUE: 'http.request.header'
|
|
8
|
+
HTTP_REQUEST_HEADER_VALUE: 'http.request.header',
|
|
9
|
+
HTTP_REQUEST_PARAMETER: 'http.request.parameter',
|
|
10
|
+
HTTP_REQUEST_PATH_PARAM: 'http.request.path.parameter'
|
|
10
11
|
}
|
|
@@ -5,12 +5,14 @@ const { getIastContext } = require('../iast-context')
|
|
|
5
5
|
const { storage } = require('../../../../../datadog-core')
|
|
6
6
|
const { taintObject } = require('./operations')
|
|
7
7
|
const {
|
|
8
|
-
HTTP_REQUEST_PARAMETER,
|
|
9
8
|
HTTP_REQUEST_BODY,
|
|
10
9
|
HTTP_REQUEST_COOKIE_VALUE,
|
|
11
10
|
HTTP_REQUEST_COOKIE_NAME,
|
|
12
11
|
HTTP_REQUEST_HEADER_VALUE,
|
|
13
|
-
HTTP_REQUEST_HEADER_NAME
|
|
12
|
+
HTTP_REQUEST_HEADER_NAME,
|
|
13
|
+
HTTP_REQUEST_PARAMETER,
|
|
14
|
+
HTTP_REQUEST_PATH_PARAM
|
|
15
|
+
|
|
14
16
|
} = require('./origin-types')
|
|
15
17
|
|
|
16
18
|
class TaintTrackingPlugin extends Plugin {
|
|
@@ -44,6 +46,14 @@ class TaintTrackingPlugin extends Plugin {
|
|
|
44
46
|
'datadog:cookie:parse:finish',
|
|
45
47
|
({ cookies }) => this._cookiesTaintTrackingHandler(cookies)
|
|
46
48
|
)
|
|
49
|
+
this.addSub(
|
|
50
|
+
'datadog:express:process_params:start',
|
|
51
|
+
({ req }) => {
|
|
52
|
+
if (req && req.params && typeof req.params === 'object') {
|
|
53
|
+
this._taintTrackingHandler(HTTP_REQUEST_PATH_PARAM, req, 'params')
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
)
|
|
47
57
|
}
|
|
48
58
|
|
|
49
59
|
_taintTrackingHandler (type, target, property, iastContext = getIastContext(storage.getStore())) {
|
|
@@ -7,6 +7,7 @@ const {
|
|
|
7
7
|
incomingHttpRequestStart,
|
|
8
8
|
incomingHttpRequestEnd,
|
|
9
9
|
bodyParser,
|
|
10
|
+
passportVerify,
|
|
10
11
|
queryParser
|
|
11
12
|
} = require('./channels')
|
|
12
13
|
const waf = require('./waf')
|
|
@@ -16,6 +17,8 @@ const web = require('../plugins/util/web')
|
|
|
16
17
|
const { extractIp } = require('../plugins/util/ip_extractor')
|
|
17
18
|
const { HTTP_CLIENT_IP } = require('../../../../ext/tags')
|
|
18
19
|
const { block, setTemplates } = require('./blocking')
|
|
20
|
+
const { passportTrackEvent } = require('./passport')
|
|
21
|
+
const { storage } = require('../../../datadog-core')
|
|
19
22
|
|
|
20
23
|
let isEnabled = false
|
|
21
24
|
let config
|
|
@@ -37,6 +40,10 @@ function enable (_config) {
|
|
|
37
40
|
bodyParser.subscribe(onRequestBodyParsed)
|
|
38
41
|
queryParser.subscribe(onRequestQueryParsed)
|
|
39
42
|
|
|
43
|
+
if (_config.appsec.eventTracking.enabled) {
|
|
44
|
+
passportVerify.subscribe(onPassportVerify)
|
|
45
|
+
}
|
|
46
|
+
|
|
40
47
|
isEnabled = true
|
|
41
48
|
config = _config
|
|
42
49
|
} catch (err) {
|
|
@@ -139,6 +146,18 @@ function onRequestQueryParsed ({ req, res, abortController }) {
|
|
|
139
146
|
handleResults(results, req, res, rootSpan, abortController)
|
|
140
147
|
}
|
|
141
148
|
|
|
149
|
+
function onPassportVerify ({ credentials, user }) {
|
|
150
|
+
const store = storage.getStore()
|
|
151
|
+
const rootSpan = store && store.req && web.root(store.req)
|
|
152
|
+
|
|
153
|
+
if (!rootSpan) {
|
|
154
|
+
log.warn('No rootSpan found in onPassportVerify')
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
passportTrackEvent(credentials, user, rootSpan, config.appsec.eventTracking.mode)
|
|
159
|
+
}
|
|
160
|
+
|
|
142
161
|
function handleResults (actions, req, res, rootSpan, abortController) {
|
|
143
162
|
if (!actions || !req || !res || !rootSpan || !abortController) return
|
|
144
163
|
|
|
@@ -160,6 +179,7 @@ function disable () {
|
|
|
160
179
|
if (incomingHttpRequestEnd.hasSubscribers) incomingHttpRequestEnd.unsubscribe(incomingHttpEndTranslator)
|
|
161
180
|
if (bodyParser.hasSubscribers) bodyParser.unsubscribe(onRequestBodyParsed)
|
|
162
181
|
if (queryParser.hasSubscribers) queryParser.unsubscribe(onRequestQueryParsed)
|
|
182
|
+
if (passportVerify.hasSubscribers) passportVerify.unsubscribe(onPassportVerify)
|
|
163
183
|
}
|
|
164
184
|
|
|
165
185
|
module.exports = {
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const log = require('../log')
|
|
4
|
+
const { trackEvent } = require('./sdk/track_event')
|
|
5
|
+
const { setUserTags } = require('./sdk/set_user')
|
|
6
|
+
|
|
7
|
+
const UUID_PATTERN = '^[0-9A-F]{8}-[0-9A-F]{4}-[1-5][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$'
|
|
8
|
+
const regexUsername = new RegExp(UUID_PATTERN, 'i')
|
|
9
|
+
|
|
10
|
+
const SDK_USER_EVENT_PATTERN = '^_dd\\.appsec\\.events\\.users\\.[\\W\\w+]+\\.sdk$'
|
|
11
|
+
const regexSdkEvent = new RegExp(SDK_USER_EVENT_PATTERN, 'i')
|
|
12
|
+
|
|
13
|
+
function isSdkCalled (tags) {
|
|
14
|
+
let called = false
|
|
15
|
+
|
|
16
|
+
if (tags && typeof tags === 'object') {
|
|
17
|
+
called = Object.entries(tags).some(([key, value]) => regexSdkEvent.test(key) && value === 'true')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return called
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// delete this function later if we know it's always credential.username
|
|
24
|
+
function getLogin (credentials) {
|
|
25
|
+
const type = credentials && credentials.type
|
|
26
|
+
let login
|
|
27
|
+
if (type === 'local' || type === 'http') {
|
|
28
|
+
login = credentials.username
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return login
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function parseUser (login, passportUser, mode) {
|
|
35
|
+
const user = {
|
|
36
|
+
'usr.id': login
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!user['usr.id']) {
|
|
40
|
+
return user
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (passportUser) {
|
|
44
|
+
// Guess id
|
|
45
|
+
if (passportUser.id) {
|
|
46
|
+
user['usr.id'] = passportUser.id
|
|
47
|
+
} else if (passportUser._id) {
|
|
48
|
+
user['usr.id'] = passportUser._id
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (mode === 'extended') {
|
|
52
|
+
if (login) {
|
|
53
|
+
user['usr.login'] = login
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (passportUser.email) {
|
|
57
|
+
user['usr.email'] = passportUser.email
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Guess username
|
|
61
|
+
if (passportUser.username) {
|
|
62
|
+
user['usr.username'] = passportUser.username
|
|
63
|
+
} else if (passportUser.name) {
|
|
64
|
+
user['usr.username'] = passportUser.name
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (mode === 'safe') {
|
|
70
|
+
// Remove PII in safe mode
|
|
71
|
+
if (!regexUsername.test(user['usr.id'])) {
|
|
72
|
+
user['usr.id'] = ' '
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return user
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function passportTrackEvent (credentials, passportUser, rootSpan, mode) {
|
|
80
|
+
const tags = rootSpan && rootSpan.context() && rootSpan.context()._tags
|
|
81
|
+
|
|
82
|
+
if (isSdkCalled(tags)) {
|
|
83
|
+
// Don't overwrite tags set by SDK callings
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
const user = parseUser(getLogin(credentials), passportUser, mode)
|
|
87
|
+
|
|
88
|
+
if (user['usr.id'] === undefined) {
|
|
89
|
+
log.warn('No user ID found in authentication instrumentation')
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (passportUser) {
|
|
94
|
+
// If a passportUser object is published then the login succeded
|
|
95
|
+
const userTags = {}
|
|
96
|
+
Object.entries(user).forEach(([k, v]) => {
|
|
97
|
+
const attr = k.split('.', 2)[1]
|
|
98
|
+
userTags[attr] = v
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
setUserTags(userTags, rootSpan)
|
|
102
|
+
trackEvent('users.login.success', null, 'passportTrackEvent', rootSpan, mode)
|
|
103
|
+
} else {
|
|
104
|
+
trackEvent('users.login.failure', user, 'passportTrackEvent', rootSpan, mode)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = {
|
|
109
|
+
passportTrackEvent
|
|
110
|
+
}
|
|
@@ -20,7 +20,7 @@ function trackUserLoginSuccessEvent (tracer, user, metadata) {
|
|
|
20
20
|
|
|
21
21
|
setUserTags(user, rootSpan)
|
|
22
22
|
|
|
23
|
-
trackEvent(
|
|
23
|
+
trackEvent('users.login.success', metadata, 'trackUserLoginSuccessEvent', rootSpan, 'sdk')
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
function trackUserLoginFailureEvent (tracer, userId, exists, metadata) {
|
|
@@ -35,7 +35,7 @@ function trackUserLoginFailureEvent (tracer, userId, exists, metadata) {
|
|
|
35
35
|
...metadata
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
trackEvent(
|
|
38
|
+
trackEvent('users.login.failure', fields, 'trackUserLoginFailureEvent', getRootSpan(tracer), 'sdk')
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
function trackCustomEvent (tracer, eventName, metadata) {
|
|
@@ -44,10 +44,10 @@ function trackCustomEvent (tracer, eventName, metadata) {
|
|
|
44
44
|
return
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
trackEvent(
|
|
47
|
+
trackEvent(eventName, metadata, 'trackCustomEvent', getRootSpan(tracer), 'sdk')
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
function trackEvent (
|
|
50
|
+
function trackEvent (eventName, fields, sdkMethodName, rootSpan, mode) {
|
|
51
51
|
if (!rootSpan) {
|
|
52
52
|
log.warn(`Root span not available in ${sdkMethodName}`)
|
|
53
53
|
return
|
|
@@ -58,6 +58,14 @@ function trackEvent (tracer, eventName, fields, sdkMethodName, rootSpan = getRoo
|
|
|
58
58
|
[MANUAL_KEEP]: 'true'
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
if (mode === 'sdk') {
|
|
62
|
+
tags[`_dd.appsec.events.${eventName}.sdk`] = 'true'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (mode === 'safe' || mode === 'extended') {
|
|
66
|
+
tags[`_dd.appsec.events.${eventName}.auto.mode`] = mode
|
|
67
|
+
}
|
|
68
|
+
|
|
61
69
|
if (fields) {
|
|
62
70
|
for (const metadataKey of Object.keys(fields)) {
|
|
63
71
|
tags[`appsec.events.${eventName}.${metadataKey}`] = '' + fields[metadataKey]
|
|
@@ -70,5 +78,6 @@ function trackEvent (tracer, eventName, fields, sdkMethodName, rootSpan = getRoo
|
|
|
70
78
|
module.exports = {
|
|
71
79
|
trackUserLoginSuccessEvent,
|
|
72
80
|
trackUserLoginFailureEvent,
|
|
73
|
-
trackCustomEvent
|
|
81
|
+
trackCustomEvent,
|
|
82
|
+
trackEvent
|
|
74
83
|
}
|
|
@@ -48,7 +48,8 @@ function getCommonRequestOptions (url) {
|
|
|
48
48
|
*/
|
|
49
49
|
function getCommitsToExclude ({ url, isEvpProxy, repositoryUrl }, callback) {
|
|
50
50
|
const latestCommits = getLatestCommits()
|
|
51
|
-
|
|
51
|
+
|
|
52
|
+
log.debug(`There were ${latestCommits.length} commits since last month.`)
|
|
52
53
|
|
|
53
54
|
const commonOptions = getCommonRequestOptions(url)
|
|
54
55
|
|
|
@@ -88,7 +89,7 @@ function getCommitsToExclude ({ url, isEvpProxy, repositoryUrl }, callback) {
|
|
|
88
89
|
} catch (e) {
|
|
89
90
|
return callback(new Error(`Can't parse commits to exclude response: ${e.message}`))
|
|
90
91
|
}
|
|
91
|
-
callback(null, commitsToExclude,
|
|
92
|
+
callback(null, commitsToExclude, latestCommits)
|
|
92
93
|
})
|
|
93
94
|
}
|
|
94
95
|
|
|
@@ -158,26 +159,38 @@ function sendGitMetadata (url, isEvpProxy, configRepositoryUrl, callback) {
|
|
|
158
159
|
repositoryUrl = getRepositoryUrl()
|
|
159
160
|
}
|
|
160
161
|
|
|
162
|
+
log.debug(`Uploading git history for repository ${repositoryUrl}`)
|
|
163
|
+
|
|
161
164
|
if (!repositoryUrl) {
|
|
162
165
|
return callback(new Error('Repository URL is empty'))
|
|
163
166
|
}
|
|
164
167
|
|
|
165
168
|
if (isShallowRepository()) {
|
|
169
|
+
log.debug('It is shallow clone, unshallowing...')
|
|
166
170
|
unshallowRepository()
|
|
167
171
|
}
|
|
168
172
|
|
|
169
|
-
getCommitsToExclude({ url, repositoryUrl, isEvpProxy }, (err, commitsToExclude,
|
|
173
|
+
getCommitsToExclude({ url, repositoryUrl, isEvpProxy }, (err, commitsToExclude, latestCommits) => {
|
|
170
174
|
if (err) {
|
|
171
175
|
return callback(err)
|
|
172
176
|
}
|
|
173
|
-
|
|
177
|
+
log.debug(`There are ${commitsToExclude.length} commits to exclude.`)
|
|
178
|
+
const [headCommit] = latestCommits
|
|
179
|
+
const commitsToInclude = latestCommits.filter((commit) => !commitsToExclude.includes(commit))
|
|
180
|
+
log.debug(`There are ${commitsToInclude.length} commits to include.`)
|
|
181
|
+
|
|
182
|
+
const commitsToUpload = getCommitsToUpload(commitsToExclude, commitsToInclude)
|
|
174
183
|
|
|
175
184
|
if (!commitsToUpload.length) {
|
|
176
185
|
log.debug('No commits to upload')
|
|
177
186
|
return callback(null)
|
|
178
187
|
}
|
|
188
|
+
log.debug(`There are ${commitsToUpload.length} commits to upload`)
|
|
189
|
+
|
|
179
190
|
const packFilesToUpload = generatePackFilesForCommits(commitsToUpload)
|
|
180
191
|
|
|
192
|
+
log.debug(`Uploading ${packFilesToUpload.length} packfiles.`)
|
|
193
|
+
|
|
181
194
|
if (!packFilesToUpload.length) {
|
|
182
195
|
return callback(new Error('Failed to generate packfiles'))
|
|
183
196
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const CiPlugin = require('../../plugins/ci_plugin')
|
|
2
|
+
const {
|
|
3
|
+
TEST_STATUS,
|
|
4
|
+
finishAllTraceSpans,
|
|
5
|
+
getTestSuitePath
|
|
6
|
+
} = require('../../plugins/util/test')
|
|
7
|
+
const { storage } = require('../../../../datadog-core')
|
|
8
|
+
|
|
9
|
+
class TestApiManualPlugin extends CiPlugin {
|
|
10
|
+
static get id () {
|
|
11
|
+
return 'test-api-manual'
|
|
12
|
+
}
|
|
13
|
+
constructor (...args) {
|
|
14
|
+
super(...args)
|
|
15
|
+
this.sourceRoot = process.cwd()
|
|
16
|
+
|
|
17
|
+
this.addSub('dd-trace:ci:manual:test:start', ({ testName, testSuite }) => {
|
|
18
|
+
const store = storage.getStore()
|
|
19
|
+
const testSuiteRelative = getTestSuitePath(testSuite, this.sourceRoot)
|
|
20
|
+
const testSpan = this.startTestSpan(testName, testSuiteRelative)
|
|
21
|
+
this.enter(testSpan, store)
|
|
22
|
+
})
|
|
23
|
+
this.addSub('dd-trace:ci:manual:test:finish', ({ status, error }) => {
|
|
24
|
+
const store = storage.getStore()
|
|
25
|
+
const testSpan = store && store.span
|
|
26
|
+
if (testSpan) {
|
|
27
|
+
testSpan.setTag(TEST_STATUS, status)
|
|
28
|
+
if (error) {
|
|
29
|
+
testSpan.setTag('error', error)
|
|
30
|
+
}
|
|
31
|
+
testSpan.finish()
|
|
32
|
+
finishAllTraceSpans(testSpan)
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
this.addSub('dd-trace:ci:manual:test:addTags', (tags) => {
|
|
36
|
+
const store = storage.getStore()
|
|
37
|
+
const testSpan = store && store.span
|
|
38
|
+
if (testSpan) {
|
|
39
|
+
testSpan.addTags(tags)
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = TestApiManualPlugin
|
|
@@ -141,6 +141,11 @@ class Config {
|
|
|
141
141
|
process.env.DD_DBM_PROPAGATION_MODE,
|
|
142
142
|
'disabled'
|
|
143
143
|
)
|
|
144
|
+
const DD_DATA_STREAMS_ENABLED = coalesce(
|
|
145
|
+
options.dsmEnabled,
|
|
146
|
+
process.env.DD_DATA_STREAMS_ENABLED,
|
|
147
|
+
false
|
|
148
|
+
)
|
|
144
149
|
const DD_AGENT_HOST = coalesce(
|
|
145
150
|
options.hostname,
|
|
146
151
|
process.env.DD_AGENT_HOST,
|
|
@@ -169,6 +174,11 @@ class Config {
|
|
|
169
174
|
true
|
|
170
175
|
)
|
|
171
176
|
|
|
177
|
+
const DD_CIVISIBILITY_MANUAL_API_ENABLED = coalesce(
|
|
178
|
+
process.env.DD_CIVISIBILITY_MANUAL_API_ENABLED,
|
|
179
|
+
false
|
|
180
|
+
)
|
|
181
|
+
|
|
172
182
|
const DD_SERVICE = options.service ||
|
|
173
183
|
process.env.DD_SERVICE ||
|
|
174
184
|
process.env.DD_SERVICE_NAME ||
|
|
@@ -227,6 +237,9 @@ class Config {
|
|
|
227
237
|
const DD_TELEMETRY_HEARTBEAT_INTERVAL = process.env.DD_TELEMETRY_HEARTBEAT_INTERVAL
|
|
228
238
|
? parseInt(process.env.DD_TELEMETRY_HEARTBEAT_INTERVAL) * 1000
|
|
229
239
|
: 60000
|
|
240
|
+
const DD_OPENAI_SPAN_CHAR_LIMIT = process.env.DD_OPENAI_SPAN_CHAR_LIMIT
|
|
241
|
+
? parseInt(process.env.DD_OPENAI_SPAN_CHAR_LIMIT)
|
|
242
|
+
: 128
|
|
230
243
|
const DD_TELEMETRY_DEBUG = coalesce(
|
|
231
244
|
process.env.DD_TELEMETRY_DEBUG,
|
|
232
245
|
false
|
|
@@ -303,6 +316,12 @@ class Config {
|
|
|
303
316
|
const DD_TRACE_SPAN_ATTRIBUTE_SCHEMA = validateNamingVersion(
|
|
304
317
|
process.env.DD_TRACE_SPAN_ATTRIBUTE_SCHEMA
|
|
305
318
|
)
|
|
319
|
+
const DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED = process.env.DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED
|
|
320
|
+
|
|
321
|
+
const DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED = coalesce(
|
|
322
|
+
isTrue(process.env.DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED),
|
|
323
|
+
false
|
|
324
|
+
)
|
|
306
325
|
const DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH = coalesce(
|
|
307
326
|
process.env.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH,
|
|
308
327
|
'512'
|
|
@@ -377,6 +396,11 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
377
396
|
maybeFile(appsec.blockedTemplateJson),
|
|
378
397
|
maybeFile(process.env.DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON)
|
|
379
398
|
)
|
|
399
|
+
const DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING = coalesce(
|
|
400
|
+
appsec.eventTracking && appsec.eventTracking.mode,
|
|
401
|
+
process.env.DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING,
|
|
402
|
+
'safe'
|
|
403
|
+
).toLowerCase()
|
|
380
404
|
|
|
381
405
|
const remoteConfigOptions = options.remoteConfig || {}
|
|
382
406
|
const DD_REMOTE_CONFIGURATION_ENABLED = coalesce(
|
|
@@ -479,6 +503,7 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
479
503
|
|
|
480
504
|
this.tracing = !isFalse(DD_TRACING_ENABLED)
|
|
481
505
|
this.dbmPropagationMode = DD_DBM_PROPAGATION_MODE
|
|
506
|
+
this.dsmEnabled = isTrue(DD_DATA_STREAMS_ENABLED)
|
|
482
507
|
this.openAiLogsEnabled = DD_OPENAI_LOGS_ENABLED
|
|
483
508
|
this.apiKey = DD_API_KEY
|
|
484
509
|
this.logInjection = isTrue(DD_LOGS_INJECTION)
|
|
@@ -521,6 +546,11 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
521
546
|
exporters: DD_PROFILING_EXPORTERS
|
|
522
547
|
}
|
|
523
548
|
this.spanAttributeSchema = DD_TRACE_SPAN_ATTRIBUTE_SCHEMA
|
|
549
|
+
this.spanComputePeerService = (this.spanAttributeSchema === 'v0'
|
|
550
|
+
? isTrue(DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED)
|
|
551
|
+
: true
|
|
552
|
+
)
|
|
553
|
+
this.traceRemoveIntegrationServiceNamesEnabled = DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED
|
|
524
554
|
this.lookup = options.lookup
|
|
525
555
|
this.startupLogs = isTrue(DD_TRACE_STARTUP_LOGS)
|
|
526
556
|
// Disabled for CI Visibility's agentless
|
|
@@ -541,7 +571,11 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
541
571
|
obfuscatorKeyRegex: DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP,
|
|
542
572
|
obfuscatorValueRegex: DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP,
|
|
543
573
|
blockedTemplateHtml: DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML,
|
|
544
|
-
blockedTemplateJson: DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON
|
|
574
|
+
blockedTemplateJson: DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON,
|
|
575
|
+
eventTracking: {
|
|
576
|
+
enabled: ['extended', 'safe'].includes(DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING),
|
|
577
|
+
mode: DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING
|
|
578
|
+
}
|
|
545
579
|
}
|
|
546
580
|
this.remoteConfig = {
|
|
547
581
|
enabled: DD_REMOTE_CONFIGURATION_ENABLED,
|
|
@@ -563,6 +597,9 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
563
597
|
(this.isIntelligentTestRunnerEnabled && !isFalse(DD_CIVISIBILITY_GIT_UPLOAD_ENABLED))
|
|
564
598
|
|
|
565
599
|
this.gitMetadataEnabled = isTrue(DD_TRACE_GIT_METADATA_ENABLED)
|
|
600
|
+
this.isManualApiEnabled = this.isCiVisibility && isTrue(DD_CIVISIBILITY_MANUAL_API_ENABLED)
|
|
601
|
+
|
|
602
|
+
this.openaiSpanCharLimit = DD_OPENAI_SPAN_CHAR_LIMIT
|
|
566
603
|
|
|
567
604
|
if (this.gitMetadataEnabled) {
|
|
568
605
|
this.repositoryUrl = coalesce(
|
|
@@ -26,6 +26,8 @@ module.exports = {
|
|
|
26
26
|
ERROR_STACK: 'error.stack',
|
|
27
27
|
COMPONENT: 'component',
|
|
28
28
|
CLIENT_PORT_KEY: 'network.destination.port',
|
|
29
|
+
PEER_SERVICE_KEY: 'peer.service',
|
|
30
|
+
PEER_SERVICE_SOURCE_KEY: '_dd.peer.service.source',
|
|
29
31
|
SCI_REPOSITORY_URL: '_dd.git.repository_url',
|
|
30
32
|
SCI_COMMIT_SHA: '_dd.git.commit.sha'
|
|
31
33
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const { storage } = require('../../datadog-core')
|
|
2
|
+
|
|
3
|
+
function getDataStreamsContext () {
|
|
4
|
+
const store = storage.getStore()
|
|
5
|
+
return (store && store.dataStreamsContext) || null
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function setDataStreamsContext (dataStreamsContext) {
|
|
9
|
+
storage.enterWith({ ...(storage.getStore()), dataStreamsContext })
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
getDataStreamsContext,
|
|
14
|
+
setDataStreamsContext
|
|
15
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// encoding used here is sha256
|
|
2
|
+
// other languages use FNV1
|
|
3
|
+
// this inconsistency is ok because hashes do not need to be consistent across services
|
|
4
|
+
const crypto = require('crypto')
|
|
5
|
+
const { encodeVarint, decodeVarint } = require('./encoding')
|
|
6
|
+
const LRUCache = require('lru-cache')
|
|
7
|
+
|
|
8
|
+
const options = { max: 500 }
|
|
9
|
+
const cache = new LRUCache(options)
|
|
10
|
+
|
|
11
|
+
function shaHash (checkpointString) {
|
|
12
|
+
const hash = crypto.createHash('md5').update(checkpointString).digest('hex').slice(0, 16)
|
|
13
|
+
return Buffer.from(hash, 'hex')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function computeHash (service, env, edgeTags, parentHash) {
|
|
17
|
+
const key = `${service}${env}` + edgeTags.join('') + parentHash.toString()
|
|
18
|
+
if (cache.get(key)) {
|
|
19
|
+
return cache.get(key)
|
|
20
|
+
}
|
|
21
|
+
const currentHash = shaHash(`${service}${env}` + edgeTags.join(''))
|
|
22
|
+
const buf = Buffer.concat([ currentHash, parentHash ], 16)
|
|
23
|
+
const val = shaHash(buf.toString())
|
|
24
|
+
cache.set(key, val)
|
|
25
|
+
return val
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function encodePathwayContext (dataStreamsContext) {
|
|
29
|
+
return Buffer.concat([
|
|
30
|
+
dataStreamsContext.hash,
|
|
31
|
+
Buffer.from(encodeVarint(Math.round(dataStreamsContext.pathwayStartNs / 1e6))),
|
|
32
|
+
Buffer.from(encodeVarint(Math.round(dataStreamsContext.edgeStartNs / 1e6)))
|
|
33
|
+
], 20)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function decodePathwayContext (pathwayContext) {
|
|
37
|
+
if (pathwayContext == null || pathwayContext.length < 8) {
|
|
38
|
+
return null
|
|
39
|
+
}
|
|
40
|
+
// hash and parent hash are in LE
|
|
41
|
+
const pathwayHash = pathwayContext.subarray(0, 8)
|
|
42
|
+
const encodedTimestamps = pathwayContext.subarray(8)
|
|
43
|
+
const [pathwayStartMs, encodedTimeSincePrev] = decodeVarint(encodedTimestamps)
|
|
44
|
+
if (pathwayStartMs === undefined) {
|
|
45
|
+
return null
|
|
46
|
+
}
|
|
47
|
+
const [edgeStartMs] = decodeVarint(encodedTimeSincePrev)
|
|
48
|
+
if (edgeStartMs === undefined) {
|
|
49
|
+
return null
|
|
50
|
+
}
|
|
51
|
+
return { hash: pathwayHash, pathwayStartNs: pathwayStartMs * 1e6, edgeStartNs: edgeStartMs * 1e6 }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = {
|
|
55
|
+
computePathwayHash: computeHash,
|
|
56
|
+
encodePathwayContext,
|
|
57
|
+
decodePathwayContext
|
|
58
|
+
}
|