dd-trace 4.3.0 → 4.5.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
|
|
@@ -144,6 +144,11 @@ class Config {
|
|
|
144
144
|
process.env.DD_DBM_PROPAGATION_MODE,
|
|
145
145
|
'disabled'
|
|
146
146
|
)
|
|
147
|
+
const DD_DATA_STREAMS_ENABLED = coalesce(
|
|
148
|
+
options.dsmEnabled,
|
|
149
|
+
process.env.DD_DATA_STREAMS_ENABLED,
|
|
150
|
+
false
|
|
151
|
+
)
|
|
147
152
|
const DD_AGENT_HOST = coalesce(
|
|
148
153
|
options.hostname,
|
|
149
154
|
process.env.DD_AGENT_HOST,
|
|
@@ -172,6 +177,11 @@ class Config {
|
|
|
172
177
|
true
|
|
173
178
|
)
|
|
174
179
|
|
|
180
|
+
const DD_CIVISIBILITY_MANUAL_API_ENABLED = coalesce(
|
|
181
|
+
process.env.DD_CIVISIBILITY_MANUAL_API_ENABLED,
|
|
182
|
+
false
|
|
183
|
+
)
|
|
184
|
+
|
|
175
185
|
const DD_SERVICE = options.service ||
|
|
176
186
|
process.env.DD_SERVICE ||
|
|
177
187
|
process.env.DD_SERVICE_NAME ||
|
|
@@ -230,6 +240,9 @@ class Config {
|
|
|
230
240
|
const DD_TELEMETRY_HEARTBEAT_INTERVAL = process.env.DD_TELEMETRY_HEARTBEAT_INTERVAL
|
|
231
241
|
? parseInt(process.env.DD_TELEMETRY_HEARTBEAT_INTERVAL) * 1000
|
|
232
242
|
: 60000
|
|
243
|
+
const DD_OPENAI_SPAN_CHAR_LIMIT = process.env.DD_OPENAI_SPAN_CHAR_LIMIT
|
|
244
|
+
? parseInt(process.env.DD_OPENAI_SPAN_CHAR_LIMIT)
|
|
245
|
+
: 128
|
|
233
246
|
const DD_TELEMETRY_DEBUG = coalesce(
|
|
234
247
|
process.env.DD_TELEMETRY_DEBUG,
|
|
235
248
|
false
|
|
@@ -306,6 +319,12 @@ class Config {
|
|
|
306
319
|
const DD_TRACE_SPAN_ATTRIBUTE_SCHEMA = validateNamingVersion(
|
|
307
320
|
process.env.DD_TRACE_SPAN_ATTRIBUTE_SCHEMA
|
|
308
321
|
)
|
|
322
|
+
const DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED = process.env.DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED
|
|
323
|
+
|
|
324
|
+
const DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED = coalesce(
|
|
325
|
+
isTrue(process.env.DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED),
|
|
326
|
+
false
|
|
327
|
+
)
|
|
309
328
|
const DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH = coalesce(
|
|
310
329
|
process.env.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH,
|
|
311
330
|
'512'
|
|
@@ -380,6 +399,11 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
380
399
|
maybeFile(appsec.blockedTemplateJson),
|
|
381
400
|
maybeFile(process.env.DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON)
|
|
382
401
|
)
|
|
402
|
+
const DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING = coalesce(
|
|
403
|
+
appsec.eventTracking && appsec.eventTracking.mode,
|
|
404
|
+
process.env.DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING,
|
|
405
|
+
'safe'
|
|
406
|
+
).toLowerCase()
|
|
383
407
|
|
|
384
408
|
const remoteConfigOptions = options.remoteConfig || {}
|
|
385
409
|
const DD_REMOTE_CONFIGURATION_ENABLED = coalesce(
|
|
@@ -482,6 +506,7 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
482
506
|
|
|
483
507
|
this.tracing = !isFalse(DD_TRACING_ENABLED)
|
|
484
508
|
this.dbmPropagationMode = DD_DBM_PROPAGATION_MODE
|
|
509
|
+
this.dsmEnabled = isTrue(DD_DATA_STREAMS_ENABLED)
|
|
485
510
|
this.openAiLogsEnabled = DD_OPENAI_LOGS_ENABLED
|
|
486
511
|
this.apiKey = DD_API_KEY
|
|
487
512
|
this.logInjection = isTrue(DD_LOGS_INJECTION)
|
|
@@ -524,6 +549,11 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
524
549
|
exporters: DD_PROFILING_EXPORTERS
|
|
525
550
|
}
|
|
526
551
|
this.spanAttributeSchema = DD_TRACE_SPAN_ATTRIBUTE_SCHEMA
|
|
552
|
+
this.spanComputePeerService = (this.spanAttributeSchema === 'v0'
|
|
553
|
+
? isTrue(DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED)
|
|
554
|
+
: true
|
|
555
|
+
)
|
|
556
|
+
this.traceRemoveIntegrationServiceNamesEnabled = DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED
|
|
527
557
|
this.lookup = options.lookup
|
|
528
558
|
this.startupLogs = isTrue(DD_TRACE_STARTUP_LOGS)
|
|
529
559
|
// Disabled for CI Visibility's agentless
|
|
@@ -544,7 +574,11 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
544
574
|
obfuscatorKeyRegex: DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP,
|
|
545
575
|
obfuscatorValueRegex: DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP,
|
|
546
576
|
blockedTemplateHtml: DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML,
|
|
547
|
-
blockedTemplateJson: DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON
|
|
577
|
+
blockedTemplateJson: DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON,
|
|
578
|
+
eventTracking: {
|
|
579
|
+
enabled: ['extended', 'safe'].includes(DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING),
|
|
580
|
+
mode: DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING
|
|
581
|
+
}
|
|
548
582
|
}
|
|
549
583
|
this.remoteConfig = {
|
|
550
584
|
enabled: DD_REMOTE_CONFIGURATION_ENABLED,
|
|
@@ -566,6 +600,9 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
566
600
|
(this.isIntelligentTestRunnerEnabled && !isFalse(DD_CIVISIBILITY_GIT_UPLOAD_ENABLED))
|
|
567
601
|
|
|
568
602
|
this.gitMetadataEnabled = isTrue(DD_TRACE_GIT_METADATA_ENABLED)
|
|
603
|
+
this.isManualApiEnabled = this.isCiVisibility && isTrue(DD_CIVISIBILITY_MANUAL_API_ENABLED)
|
|
604
|
+
|
|
605
|
+
this.openaiSpanCharLimit = DD_OPENAI_SPAN_CHAR_LIMIT
|
|
569
606
|
|
|
570
607
|
if (this.gitMetadataEnabled) {
|
|
571
608
|
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
|
+
}
|