dd-trace 2.12.2 → 2.15.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 +2 -0
- package/ext/tags.d.ts +2 -1
- package/ext/tags.js +2 -1
- package/index.d.ts +43 -20
- package/package.json +5 -3
- package/packages/datadog-core/src/storage/async_resource.js +19 -1
- package/packages/datadog-instrumentations/index.js +1 -52
- package/packages/datadog-instrumentations/src/crypto.js +30 -0
- package/packages/datadog-instrumentations/src/cucumber.js +15 -0
- package/packages/datadog-instrumentations/src/fs.js +11 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +70 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +5 -34
- package/packages/datadog-instrumentations/src/helpers/instrumentations.js +7 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +59 -0
- package/packages/datadog-instrumentations/src/http/server.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +33 -11
- package/packages/datadog-instrumentations/src/net.js +13 -0
- package/packages/datadog-plugin-cucumber/src/index.js +4 -0
- package/packages/datadog-plugin-fs/src/index.js +72 -38
- package/packages/datadog-plugin-jest/src/index.js +25 -4
- package/packages/datadog-plugin-mocha/src/index.js +2 -2
- package/packages/datadog-plugin-mongodb-core/src/index.js +32 -8
- package/packages/datadog-plugin-oracledb/src/index.js +12 -4
- package/packages/dd-trace/index.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +3 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/index.js +20 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +48 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +24 -0
- package/packages/dd-trace/src/appsec/iast/iast-context.js +50 -0
- package/packages/dd-trace/src/appsec/iast/index.js +59 -0
- package/packages/dd-trace/src/appsec/iast/overhead-controller.js +94 -0
- package/packages/dd-trace/src/appsec/iast/path-line.js +70 -0
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +113 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +50 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +53 -8
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +23 -24
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +220 -0
- package/packages/dd-trace/src/config.js +89 -10
- package/packages/dd-trace/src/constants.js +9 -1
- package/packages/dd-trace/src/encode/0.4.js +51 -58
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +13 -34
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +84 -0
- package/packages/dd-trace/src/encode/span-stats.js +155 -0
- package/packages/dd-trace/src/exporters/agent/index.js +25 -7
- package/packages/dd-trace/src/exporters/agent/writer.js +7 -4
- package/packages/dd-trace/src/{profiling/exporters → exporters/common}/form-data.js +0 -0
- package/packages/dd-trace/src/exporters/common/request.js +25 -10
- package/packages/dd-trace/src/exporters/common/writer.js +9 -6
- package/packages/dd-trace/src/exporters/span-stats/index.js +20 -0
- package/packages/dd-trace/src/exporters/span-stats/writer.js +54 -0
- package/packages/dd-trace/src/format.js +2 -0
- package/packages/dd-trace/src/id.js +16 -13
- package/packages/dd-trace/src/iitm.js +11 -0
- package/packages/dd-trace/src/index.js +10 -0
- package/packages/dd-trace/src/noop/proxy.js +87 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +77 -6
- package/packages/dd-trace/src/opentracing/tracer.js +1 -1
- package/packages/dd-trace/src/plugin_manager.js +107 -65
- package/packages/dd-trace/src/plugins/index.js +58 -45
- package/packages/dd-trace/src/plugins/log_plugin.js +16 -9
- package/packages/dd-trace/src/plugins/util/ci.js +34 -9
- package/packages/dd-trace/src/plugins/util/git.js +52 -2
- package/packages/dd-trace/src/plugins/util/ip_blocklist.js +25 -0
- package/packages/dd-trace/src/plugins/util/tags.js +4 -1
- package/packages/dd-trace/src/plugins/util/web.js +99 -2
- package/packages/dd-trace/src/priority_sampler.js +36 -1
- package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
- package/packages/dd-trace/src/proxy.js +23 -89
- package/packages/dd-trace/src/ritm.js +10 -1
- package/packages/dd-trace/src/span_processor.js +7 -1
- package/packages/dd-trace/src/span_stats.js +210 -0
- package/packages/dd-trace/src/startup-log.js +8 -19
- package/packages/dd-trace/src/telemetry/dependencies.js +83 -0
- package/packages/dd-trace/src/{telemetry.js → telemetry/index.js} +11 -79
- package/packages/dd-trace/src/telemetry/send-data.js +35 -0
- package/scripts/install_plugin_modules.js +17 -26
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
// The `fs` plugin is an old style plugin that has not been updated for the new
|
|
4
|
+
// plugin system and was hacked in for backward compatibility with 2.x.
|
|
5
|
+
|
|
3
6
|
const { storage } = require('../../datadog-core')
|
|
7
|
+
const { channel } = require('../../datadog-instrumentations/src/helpers/instrument')
|
|
8
|
+
const shimmer = require('../../datadog-shimmer')
|
|
9
|
+
const Plugin = require('../../dd-trace/src/plugins/plugin')
|
|
4
10
|
|
|
5
11
|
let kDirReadPromisified
|
|
6
12
|
let kDirClosePromisified
|
|
7
13
|
let kHandle
|
|
14
|
+
let fsConfig
|
|
15
|
+
let fsInstance
|
|
8
16
|
|
|
9
17
|
const ddFhSym = Symbol('ddFileHandle')
|
|
10
18
|
|
|
@@ -144,8 +152,8 @@ function createWrapDirAsyncIterator (config, tracer, instrumenter) {
|
|
|
144
152
|
}
|
|
145
153
|
}
|
|
146
154
|
}
|
|
147
|
-
|
|
148
|
-
|
|
155
|
+
shimmer.wrap(this, kDirReadPromisified, createWrapDirRead(config, tracer))
|
|
156
|
+
shimmer.wrap(this, kDirClosePromisified, createWrapKDirClose(config, tracer, instrumenter))
|
|
149
157
|
return asyncIterator.apply(this, arguments)
|
|
150
158
|
}
|
|
151
159
|
}
|
|
@@ -158,8 +166,8 @@ function createWrapKDirClose (config, tracer, instrumenter) {
|
|
|
158
166
|
return tracer.trace('fs.operation', { tags, orphanable }, (span) => {
|
|
159
167
|
const p = kDirClose.apply(this, arguments)
|
|
160
168
|
const unwrapBoth = () => {
|
|
161
|
-
|
|
162
|
-
|
|
169
|
+
shimmer.unwrap(this, kDirReadPromisified)
|
|
170
|
+
shimmer.unwrap(this, kDirClosePromisified)
|
|
163
171
|
}
|
|
164
172
|
p.then(unwrapBoth, unwrapBoth)
|
|
165
173
|
return p
|
|
@@ -339,7 +347,7 @@ function makeFSTags (resourceName, path, options, config, tracer) {
|
|
|
339
347
|
'component': 'fs',
|
|
340
348
|
'span.kind': 'internal',
|
|
341
349
|
'resource.name': resourceName,
|
|
342
|
-
'service.name':
|
|
350
|
+
'service.name': fsConfig.service || tracer._service
|
|
343
351
|
}
|
|
344
352
|
|
|
345
353
|
switch (typeof path) {
|
|
@@ -399,9 +407,9 @@ function patchClassicFunctions (fs, tracer, config) {
|
|
|
399
407
|
if (tagMakerName in tagMakers) {
|
|
400
408
|
const tagMaker = tagMakers[tagMakerName]
|
|
401
409
|
if (name.endsWith('Sync')) {
|
|
402
|
-
|
|
410
|
+
shimmer.wrap(fs, name, createWrap(tracer, config, name, tagMaker))
|
|
403
411
|
} else {
|
|
404
|
-
|
|
412
|
+
shimmer.wrap(fs, name, createWrapCb(tracer, config, name, tagMaker))
|
|
405
413
|
}
|
|
406
414
|
if (name in promisifiable) {
|
|
407
415
|
copySymbols(original, fs[name])
|
|
@@ -424,8 +432,6 @@ function patchFileHandle (fs, tracer, config) {
|
|
|
424
432
|
tagMaker = createFDTags
|
|
425
433
|
}
|
|
426
434
|
|
|
427
|
-
const instrumenter = this
|
|
428
|
-
|
|
429
435
|
const desc = Reflect.getOwnPropertyDescriptor(fileHandlePrototype, kHandle)
|
|
430
436
|
if (!desc || !desc.get) {
|
|
431
437
|
Reflect.defineProperty(fileHandlePrototype, kHandle, {
|
|
@@ -434,13 +440,13 @@ function patchFileHandle (fs, tracer, config) {
|
|
|
434
440
|
},
|
|
435
441
|
set (h) {
|
|
436
442
|
this[ddFhSym] = h
|
|
437
|
-
|
|
443
|
+
shimmer.wrap(this, 'close', createWrap(tracer, config, 'filehandle.close', tagMakers.close))
|
|
438
444
|
},
|
|
439
445
|
configurable: true
|
|
440
446
|
})
|
|
441
447
|
}
|
|
442
448
|
|
|
443
|
-
|
|
449
|
+
shimmer.wrap(fileHandlePrototype, name, createWrap(tracer, config, 'filehandle.' + name, tagMaker))
|
|
444
450
|
}
|
|
445
451
|
})
|
|
446
452
|
}
|
|
@@ -449,17 +455,17 @@ function patchPromiseFunctions (fs, tracer, config) {
|
|
|
449
455
|
for (const name in fs.promises) {
|
|
450
456
|
if (name in tagMakers) {
|
|
451
457
|
const tagMaker = tagMakers[name]
|
|
452
|
-
|
|
458
|
+
shimmer.wrap(fs.promises, name, createWrap(tracer, config, 'promises.' + name, tagMaker))
|
|
453
459
|
}
|
|
454
460
|
}
|
|
455
461
|
}
|
|
456
462
|
|
|
457
463
|
function patchDirFunctions (fs, tracer, config) {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
464
|
+
shimmer.wrap(fs.Dir.prototype, 'close', createWrapDirClose(config, tracer))
|
|
465
|
+
shimmer.wrap(fs.Dir.prototype, 'closeSync', createWrapDirClose(config, tracer, true))
|
|
466
|
+
shimmer.wrap(fs.Dir.prototype, 'read', createWrapDirRead(config, tracer))
|
|
467
|
+
shimmer.wrap(fs.Dir.prototype, 'readSync', createWrapDirRead(config, tracer, true))
|
|
468
|
+
shimmer.wrap(fs.Dir.prototype, Symbol.asyncIterator, createWrapDirAsyncIterator(config, tracer, this))
|
|
463
469
|
}
|
|
464
470
|
|
|
465
471
|
function unpatchClassicFunctions (fs) {
|
|
@@ -467,7 +473,7 @@ function unpatchClassicFunctions (fs) {
|
|
|
467
473
|
if (!fs[name]) continue
|
|
468
474
|
const tagMakerName = name.endsWith('Sync') ? name.substr(0, name.length - 4) : name
|
|
469
475
|
if (tagMakerName in tagMakers) {
|
|
470
|
-
|
|
476
|
+
shimmer.unwrap(fs, name)
|
|
471
477
|
}
|
|
472
478
|
}
|
|
473
479
|
}
|
|
@@ -478,7 +484,7 @@ function unpatchFileHandle (fs) {
|
|
|
478
484
|
if (typeof name !== 'string' || name === 'constructor' || name === 'fd' || name === 'getAsyncId') {
|
|
479
485
|
continue
|
|
480
486
|
}
|
|
481
|
-
|
|
487
|
+
shimmer.unwrap(fileHandlePrototype, name)
|
|
482
488
|
}
|
|
483
489
|
delete fileHandlePrototype[kHandle]
|
|
484
490
|
})
|
|
@@ -487,22 +493,46 @@ function unpatchFileHandle (fs) {
|
|
|
487
493
|
function unpatchPromiseFunctions (fs) {
|
|
488
494
|
for (const name in fs.promises) {
|
|
489
495
|
if (name in tagMakers) {
|
|
490
|
-
|
|
496
|
+
shimmer.unwrap(fs.promises, name)
|
|
491
497
|
}
|
|
492
498
|
}
|
|
493
499
|
}
|
|
494
500
|
|
|
495
501
|
function unpatchDirFunctions (fs) {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
502
|
+
shimmer.unwrap(fs.Dir.prototype, 'close')
|
|
503
|
+
shimmer.unwrap(fs.Dir.prototype, 'closeSync')
|
|
504
|
+
shimmer.unwrap(fs.Dir.prototype, 'read')
|
|
505
|
+
shimmer.unwrap(fs.Dir.prototype, 'readSync')
|
|
506
|
+
shimmer.unwrap(fs.Dir.prototype, Symbol.asyncIterator)
|
|
501
507
|
}
|
|
502
508
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
509
|
+
const hookChannel = channel('apm:fs:hook')
|
|
510
|
+
|
|
511
|
+
hookChannel.subscribe(fs => {
|
|
512
|
+
fsInstance = fs
|
|
513
|
+
})
|
|
514
|
+
|
|
515
|
+
class FsPlugin extends Plugin {
|
|
516
|
+
static get name () {
|
|
517
|
+
return 'fs'
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
configure (config) {
|
|
521
|
+
fsConfig = config
|
|
522
|
+
|
|
523
|
+
super.configure(config)
|
|
524
|
+
|
|
525
|
+
this._unpatch()
|
|
526
|
+
|
|
527
|
+
if (this._enabled) {
|
|
528
|
+
this._patch()
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
_patch () {
|
|
533
|
+
const fs = fsInstance
|
|
534
|
+
const tracer = this.tracer
|
|
535
|
+
const config = this.config
|
|
506
536
|
const realpathNative = fs.realpath.native
|
|
507
537
|
const realpathSyncNative = fs.realpathSync.native
|
|
508
538
|
patchClassicFunctions.call(this, fs, tracer, config)
|
|
@@ -513,18 +543,20 @@ module.exports = {
|
|
|
513
543
|
if (fs.Dir) {
|
|
514
544
|
patchDirFunctions.call(this, fs, tracer, config)
|
|
515
545
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
546
|
+
shimmer.wrap(fs, 'createReadStream', createWrapCreateReadStream(config, tracer))
|
|
547
|
+
shimmer.wrap(fs, 'createWriteStream', createWrapCreateWriteStream(config, tracer))
|
|
548
|
+
shimmer.wrap(fs, 'existsSync', createWrap(tracer, config, 'existsSync', createPathTags))
|
|
549
|
+
shimmer.wrap(fs, 'exists', createWrapExists(config, tracer))
|
|
520
550
|
if (realpathNative) {
|
|
521
551
|
fs.realpath.native = createWrapCb(tracer, config, 'realpath.native', createPathTags)(realpathNative)
|
|
522
552
|
}
|
|
523
553
|
if (realpathSyncNative) {
|
|
524
554
|
fs.realpathSync.native = createWrap(tracer, config, 'realpath.native', createPathTags)(realpathSyncNative)
|
|
525
555
|
}
|
|
526
|
-
}
|
|
527
|
-
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
_unpatch () {
|
|
559
|
+
const fs = fsInstance
|
|
528
560
|
unpatchClassicFunctions.call(this, fs)
|
|
529
561
|
if (fs.promises) {
|
|
530
562
|
unpatchFileHandle.call(this, fs)
|
|
@@ -533,13 +565,15 @@ module.exports = {
|
|
|
533
565
|
if (fs.Dir) {
|
|
534
566
|
unpatchDirFunctions.call(this, fs)
|
|
535
567
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
568
|
+
shimmer.unwrap(fs, 'createReadStream')
|
|
569
|
+
shimmer.unwrap(fs, 'createWriteStream')
|
|
570
|
+
shimmer.unwrap(fs, 'existsSync')
|
|
571
|
+
shimmer.unwrap(fs, 'exists')
|
|
540
572
|
}
|
|
541
573
|
}
|
|
542
574
|
|
|
575
|
+
module.exports = FsPlugin
|
|
576
|
+
|
|
543
577
|
/** TODO fs functions:
|
|
544
578
|
|
|
545
579
|
unwatchFile
|
|
@@ -15,6 +15,9 @@ const {
|
|
|
15
15
|
TEST_CODE_OWNERS
|
|
16
16
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
17
17
|
|
|
18
|
+
// https://github.com/facebook/jest/blob/d6ad15b0f88a05816c2fe034dd6900d28315d570/packages/jest-worker/src/types.ts#L38
|
|
19
|
+
const CHILD_MESSAGE_END = 2
|
|
20
|
+
|
|
18
21
|
function getTestSpanMetadata (tracer, test) {
|
|
19
22
|
const childOf = getTestParentSpan(tracer)
|
|
20
23
|
|
|
@@ -38,9 +41,31 @@ class JestPlugin extends Plugin {
|
|
|
38
41
|
constructor (...args) {
|
|
39
42
|
super(...args)
|
|
40
43
|
|
|
44
|
+
// Used to handle the end of a jest worker to be able to flush
|
|
45
|
+
const handler = ([message]) => {
|
|
46
|
+
if (message === CHILD_MESSAGE_END) {
|
|
47
|
+
this.tracer._exporter._writer.flush(() => {
|
|
48
|
+
// eslint-disable-next-line
|
|
49
|
+
// https://github.com/facebook/jest/blob/24ed3b5ecb419c023ee6fdbc838f07cc028fc007/packages/jest-worker/src/workers/processChild.ts#L118-L133
|
|
50
|
+
// Only after the flush is done we clean up open handles
|
|
51
|
+
// so the worker process can hopefully exit gracefully
|
|
52
|
+
process.removeListener('message', handler)
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
process.on('message', handler)
|
|
57
|
+
|
|
41
58
|
this.testEnvironmentMetadata = getTestEnvironmentMetadata('jest', this.config)
|
|
42
59
|
this.codeOwnersEntries = getCodeOwnersFileEntries()
|
|
43
60
|
|
|
61
|
+
this.addSub('ci:jest:test:code-coverage', (coverageFiles) => {
|
|
62
|
+
if (!this.config.isAgentlessEnabled || !this.config.isIntelligentTestRunnerEnabled) {
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
const testSpan = storage.getStore().span
|
|
66
|
+
this.tracer._exporter.exportCoverage({ testSpan, coverageFiles })
|
|
67
|
+
})
|
|
68
|
+
|
|
44
69
|
this.addSub('ci:jest:test:start', (test) => {
|
|
45
70
|
const store = storage.getStore()
|
|
46
71
|
const span = this.startTestSpan(test)
|
|
@@ -55,10 +80,6 @@ class JestPlugin extends Plugin {
|
|
|
55
80
|
finishAllTraceSpans(span)
|
|
56
81
|
})
|
|
57
82
|
|
|
58
|
-
this.addSub('ci:jest:test-suite:finish', () => {
|
|
59
|
-
this.tracer._exporter._writer.flush()
|
|
60
|
-
})
|
|
61
|
-
|
|
62
83
|
this.addSub('ci:jest:test:err', (error) => {
|
|
63
84
|
if (error) {
|
|
64
85
|
const span = storage.getStore().span
|
|
@@ -161,12 +161,12 @@ class MochaPlugin extends Plugin {
|
|
|
161
161
|
const testSuiteSpan = this._testSuites.get(test.parent)
|
|
162
162
|
|
|
163
163
|
if (testSuiteSpan) {
|
|
164
|
-
const testSuiteId = testSuiteSpan.context()._spanId.toString(
|
|
164
|
+
const testSuiteId = testSuiteSpan.context()._spanId.toString(16)
|
|
165
165
|
testSuiteTags[TEST_SUITE_ID] = testSuiteId
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
if (this.testSessionSpan) {
|
|
169
|
-
const testSessionId = this.testSessionSpan.context()._traceId.toString(
|
|
169
|
+
const testSessionId = this.testSessionSpan.context()._traceId.toString(16)
|
|
170
170
|
testSuiteTags[TEST_SESSION_ID] = testSessionId
|
|
171
171
|
testSuiteTags[TEST_COMMAND] = this.command
|
|
172
172
|
}
|
|
@@ -14,7 +14,7 @@ class MongodbCorePlugin extends Plugin {
|
|
|
14
14
|
|
|
15
15
|
this.addSub(`apm:mongodb:query:start`, ({ ns, ops, options, name }) => {
|
|
16
16
|
const query = getQuery(ops)
|
|
17
|
-
const resource = getResource(ns, query, name)
|
|
17
|
+
const resource = truncate(getResource(ns, query, name))
|
|
18
18
|
const store = storage.getStore()
|
|
19
19
|
const childOf = store ? store.span : store
|
|
20
20
|
const span = this.tracer.startSpan('mongodb.query', {
|
|
@@ -55,8 +55,8 @@ class MongodbCorePlugin extends Plugin {
|
|
|
55
55
|
|
|
56
56
|
function getQuery (cmd) {
|
|
57
57
|
if (!cmd || typeof cmd !== 'object' || Array.isArray(cmd)) return
|
|
58
|
-
if (cmd.query) return JSON.stringify(
|
|
59
|
-
if (cmd.filter) return JSON.stringify(
|
|
58
|
+
if (cmd.query) return JSON.stringify(limitDepth(cmd.query))
|
|
59
|
+
if (cmd.filter) return JSON.stringify(limitDepth(cmd.filter))
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function getResource (ns, query, operationName) {
|
|
@@ -69,12 +69,25 @@ function getResource (ns, query, operationName) {
|
|
|
69
69
|
return parts.join(' ')
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
function truncate (input) {
|
|
73
|
+
return input.slice(0, Math.min(input.length, 10000))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function shouldSimplify (input) {
|
|
77
|
+
return !isObject(input)
|
|
78
|
+
}
|
|
79
|
+
|
|
72
80
|
function shouldHide (input) {
|
|
73
|
-
return
|
|
81
|
+
return Buffer.isBuffer(input) || typeof input === 'function' || isBinary(input)
|
|
74
82
|
}
|
|
75
83
|
|
|
76
|
-
function
|
|
84
|
+
function limitDepth (input) {
|
|
85
|
+
if (isBSON(input)) {
|
|
86
|
+
input = input.toJSON()
|
|
87
|
+
}
|
|
88
|
+
|
|
77
89
|
if (shouldHide(input)) return '?'
|
|
90
|
+
if (shouldSimplify(input)) return input
|
|
78
91
|
|
|
79
92
|
const output = {}
|
|
80
93
|
const queue = [{
|
|
@@ -91,9 +104,16 @@ function sanitize (input) {
|
|
|
91
104
|
for (const key in input) {
|
|
92
105
|
if (typeof input[key] === 'function') continue
|
|
93
106
|
|
|
94
|
-
|
|
95
|
-
|
|
107
|
+
let child = input[key]
|
|
108
|
+
|
|
109
|
+
if (isBSON(child)) {
|
|
110
|
+
child = child.toJSON()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (depth >= 10 || shouldHide(child)) {
|
|
96
114
|
output[key] = '?'
|
|
115
|
+
} else if (shouldSimplify(child)) {
|
|
116
|
+
output[key] = child
|
|
97
117
|
} else {
|
|
98
118
|
queue.push({
|
|
99
119
|
input: child,
|
|
@@ -112,7 +132,11 @@ function isObject (val) {
|
|
|
112
132
|
}
|
|
113
133
|
|
|
114
134
|
function isBSON (val) {
|
|
115
|
-
return val && val._bsontype
|
|
135
|
+
return val && val._bsontype && !isBinary(val)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function isBinary (val) {
|
|
139
|
+
return val && val._bsontype === 'Binary'
|
|
116
140
|
}
|
|
117
141
|
|
|
118
142
|
module.exports = MongodbCorePlugin
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const Plugin = require('../../dd-trace/src/plugins/plugin')
|
|
4
4
|
const { storage } = require('../../datadog-core')
|
|
5
5
|
const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
|
|
6
|
+
const log = require('../../dd-trace/src/log')
|
|
6
7
|
|
|
7
8
|
class OracledbPlugin extends Plugin {
|
|
8
9
|
static get name () {
|
|
@@ -14,18 +15,25 @@ class OracledbPlugin extends Plugin {
|
|
|
14
15
|
|
|
15
16
|
this.addSub('apm:oracledb:execute:start', ({ query, connAttrs }) => {
|
|
16
17
|
const service = getServiceName(this.tracer, this.config, connAttrs)
|
|
17
|
-
|
|
18
|
+
let connectStringObj
|
|
19
|
+
try {
|
|
20
|
+
connectStringObj = new URL(`http://${connAttrs.connectString}`)
|
|
21
|
+
} catch (e) {
|
|
22
|
+
log.error(e)
|
|
23
|
+
}
|
|
18
24
|
const tags = {
|
|
19
25
|
'span.kind': 'client',
|
|
20
26
|
'span.type': 'sql',
|
|
21
27
|
'sql.query': query,
|
|
22
|
-
'db.instance': connectStringObj.pathname.substring(1),
|
|
23
|
-
'db.hostname': connectStringObj.hostname,
|
|
24
28
|
'db.user': this.config.user,
|
|
25
|
-
'db.port': connectStringObj.port,
|
|
26
29
|
'resource.name': query,
|
|
27
30
|
'service.name': service
|
|
28
31
|
}
|
|
32
|
+
if (typeof connectStringObj !== 'undefined') {
|
|
33
|
+
tags['db.instance'] = connectStringObj.pathname.substring(1)
|
|
34
|
+
tags['db.hostname'] = connectStringObj.hostname
|
|
35
|
+
tags['db.port'] = connectStringObj.port
|
|
36
|
+
}
|
|
29
37
|
const store = storage.getStore()
|
|
30
38
|
const childOf = store ? store.span : store
|
|
31
39
|
const span = this.tracer.startSpan('oracle.query', {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const analyzers = require('./analyzers')
|
|
4
|
+
|
|
5
|
+
function enableAllAnalyzers () {
|
|
6
|
+
for (const analyzer in analyzers) {
|
|
7
|
+
analyzers[analyzer].configure(true)
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function disableAllAnalyzers () {
|
|
12
|
+
for (const analyzer in analyzers) {
|
|
13
|
+
analyzers[analyzer].configure(false)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = {
|
|
18
|
+
enableAllAnalyzers,
|
|
19
|
+
disableAllAnalyzers
|
|
20
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const Plugin = require('../../../../src/plugins/plugin')
|
|
4
|
+
const { storage } = require('../../../../../datadog-core')
|
|
5
|
+
const { getFirstNonDDPathAndLine } = require('./../path-line')
|
|
6
|
+
const { createVulnerability, addVulnerability } = require('../vulnerability-reporter')
|
|
7
|
+
const { getIastContext } = require('../iast-context')
|
|
8
|
+
const overheadController = require('../overhead-controller')
|
|
9
|
+
|
|
10
|
+
class Analyzer extends Plugin {
|
|
11
|
+
constructor (type) {
|
|
12
|
+
super()
|
|
13
|
+
this._type = type
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
_isVulnerable (value, context) {
|
|
17
|
+
return false
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
_report (value, context) {
|
|
21
|
+
const evidence = this._getEvidence(value)
|
|
22
|
+
const location = this._getLocation()
|
|
23
|
+
const spanId = context && context.rootSpan && context.rootSpan.context().toSpanId()
|
|
24
|
+
const vulnerability = createVulnerability(this._type, evidence, spanId, location)
|
|
25
|
+
addVulnerability(context, vulnerability)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
_getEvidence (value) {
|
|
29
|
+
return { value }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
_getLocation () {
|
|
33
|
+
return getFirstNonDDPathAndLine()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
analyze (value) {
|
|
37
|
+
const iastContext = getIastContext(storage.getStore())
|
|
38
|
+
if (iastContext && this._isVulnerable(value, iastContext) && this._checkOCE(iastContext)) {
|
|
39
|
+
this._report(value, iastContext)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
_checkOCE (context) {
|
|
44
|
+
return overheadController.hasQuota(overheadController.OPERATIONS.REPORT_VULNERABILITY, context)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = Analyzer
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const Analyzer = require('./vulnerability-analyzer')
|
|
3
|
+
|
|
4
|
+
const INSECURE_HASH_ALGORITHMS = new Set([
|
|
5
|
+
'md4', 'md4WithRSAEncryption', 'RSA-MD4',
|
|
6
|
+
'RSA-MD5', 'md5', 'md5-sha1', 'ssl3-md5', 'md5WithRSAEncryption',
|
|
7
|
+
'RSA-SHA1', 'RSA-SHA1-2', 'sha1', 'md5-sha1', 'sha1WithRSAEncryption', 'ssl3-sha1'
|
|
8
|
+
].map(algorithm => algorithm.toLowerCase()))
|
|
9
|
+
|
|
10
|
+
class WeakHashAnalyzer extends Analyzer {
|
|
11
|
+
constructor () {
|
|
12
|
+
super('WEAK_HASH')
|
|
13
|
+
this.addSub('datadog:crypto:hashing:start', ({ algorithm }) => this.analyze(algorithm))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
_isVulnerable (algorithm) {
|
|
17
|
+
if (typeof algorithm === 'string') {
|
|
18
|
+
return INSECURE_HASH_ALGORITHMS.has(algorithm.toLowerCase())
|
|
19
|
+
}
|
|
20
|
+
return false
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = new WeakHashAnalyzer()
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const IAST_CONTEXT_KEY = Symbol('_dd.iast.context')
|
|
2
|
+
|
|
3
|
+
function getIastContext (store) {
|
|
4
|
+
return store && store[IAST_CONTEXT_KEY]
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/* TODO Fix storage problem when the close event is called without
|
|
8
|
+
finish event to remove `topContext` references
|
|
9
|
+
We have to save the context in two places, because
|
|
10
|
+
clean can be called when the storage store is not available
|
|
11
|
+
*/
|
|
12
|
+
function saveIastContext (store, topContext, context) {
|
|
13
|
+
if (store && topContext) {
|
|
14
|
+
store[IAST_CONTEXT_KEY] = context
|
|
15
|
+
topContext[IAST_CONTEXT_KEY] = context
|
|
16
|
+
return store[IAST_CONTEXT_KEY]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* TODO Fix storage problem when the close event is called without
|
|
21
|
+
finish event to remove `topContext` references
|
|
22
|
+
iastContext is currently saved in store and request rootContext
|
|
23
|
+
to fix problems with `close` without `finish` events
|
|
24
|
+
*/
|
|
25
|
+
function cleanIastContext (store, context, iastContext) {
|
|
26
|
+
if (store) {
|
|
27
|
+
if (!iastContext) {
|
|
28
|
+
iastContext = store[IAST_CONTEXT_KEY]
|
|
29
|
+
}
|
|
30
|
+
store[IAST_CONTEXT_KEY] = null
|
|
31
|
+
}
|
|
32
|
+
if (context) {
|
|
33
|
+
if (!iastContext) {
|
|
34
|
+
iastContext = context[IAST_CONTEXT_KEY]
|
|
35
|
+
}
|
|
36
|
+
context[IAST_CONTEXT_KEY] = null
|
|
37
|
+
}
|
|
38
|
+
if (iastContext) {
|
|
39
|
+
Object.keys(iastContext).forEach(key => delete iastContext[key])
|
|
40
|
+
return true
|
|
41
|
+
}
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = {
|
|
46
|
+
getIastContext,
|
|
47
|
+
saveIastContext,
|
|
48
|
+
cleanIastContext,
|
|
49
|
+
IAST_CONTEXT_KEY
|
|
50
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const { sendVulnerabilities } = require('./vulnerability-reporter')
|
|
2
|
+
const { enableAllAnalyzers, disableAllAnalyzers } = require('./analyzers')
|
|
3
|
+
const web = require('../../plugins/util/web')
|
|
4
|
+
const { storage } = require('../../../../datadog-core')
|
|
5
|
+
const overheadController = require('./overhead-controller')
|
|
6
|
+
const dc = require('diagnostics_channel')
|
|
7
|
+
const { saveIastContext, getIastContext, cleanIastContext } = require('./iast-context')
|
|
8
|
+
|
|
9
|
+
// TODO Change to `apm:http:server:request:[start|close]` when the subscription
|
|
10
|
+
// order of the callbacks can be enforce
|
|
11
|
+
const requestStart = dc.channel('dd-trace:incomingHttpRequestStart')
|
|
12
|
+
const requestClose = dc.channel('dd-trace:incomingHttpRequestEnd')
|
|
13
|
+
|
|
14
|
+
function enable (config) {
|
|
15
|
+
enableAllAnalyzers()
|
|
16
|
+
requestStart.subscribe(onIncomingHttpRequestStart)
|
|
17
|
+
requestClose.subscribe(onIncomingHttpRequestEnd)
|
|
18
|
+
overheadController.configure(config.iast)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function disable () {
|
|
22
|
+
disableAllAnalyzers()
|
|
23
|
+
if (requestStart.hasSubscribers) requestStart.unsubscribe(onIncomingHttpRequestStart)
|
|
24
|
+
if (requestClose.hasSubscribers) requestClose.unsubscribe(onIncomingHttpRequestEnd)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function onIncomingHttpRequestStart (data) {
|
|
28
|
+
if (data && data.req) {
|
|
29
|
+
const store = storage.getStore()
|
|
30
|
+
if (store) {
|
|
31
|
+
const topContext = web.getContext(data.req)
|
|
32
|
+
if (topContext) {
|
|
33
|
+
const rootSpan = topContext.span
|
|
34
|
+
const isRequestAcquired = overheadController.acquireRequest(rootSpan)
|
|
35
|
+
if (isRequestAcquired) {
|
|
36
|
+
const iastContext = saveIastContext(store, topContext, { rootSpan, req: data.req })
|
|
37
|
+
overheadController.initializeRequestContext(iastContext)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function onIncomingHttpRequestEnd (data) {
|
|
45
|
+
if (data && data.req) {
|
|
46
|
+
const store = storage.getStore()
|
|
47
|
+
const iastContext = getIastContext(storage.getStore())
|
|
48
|
+
if (iastContext && iastContext.rootSpan) {
|
|
49
|
+
overheadController.releaseRequest()
|
|
50
|
+
sendVulnerabilities(iastContext, iastContext.rootSpan)
|
|
51
|
+
}
|
|
52
|
+
// TODO web.getContext(data.req) is required when the request is aborted
|
|
53
|
+
if (cleanIastContext(store, web.getContext(data.req), iastContext)) {
|
|
54
|
+
overheadController.releaseRequest()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = { enable, disable, onIncomingHttpRequestEnd, onIncomingHttpRequestStart }
|