dd-trace
Version:
Datadog APM tracing client for JavaScript
106 lines (94 loc) • 3.92 kB
JavaScript
const vulnerabilityReporter = require('./vulnerability-reporter')
const { enableAllAnalyzers, disableAllAnalyzers } = require('./analyzers')
const web = require('../../plugins/util/web')
const { storage } = require('../../../../datadog-core')
const overheadController = require('./overhead-controller')
const dc = require('dc-polyfill')
const iastContextFunctions = require('./iast-context')
const {
enableTaintTracking,
disableTaintTracking,
createTransaction,
removeTransaction,
taintTrackingPlugin
} = require('./taint-tracking')
const { IAST_ENABLED_TAG_KEY } = require('./tags')
const iastTelemetry = require('./telemetry')
const { enable: enableFsPlugin, disable: disableFsPlugin, IAST_MODULE } = require('../rasp/fs-plugin')
const securityControls = require('./security-controls')
// TODO Change to `apm:http:server:request:[start|close]` when the subscription
// order of the callbacks can be enforce
const requestStart = dc.channel('dd-trace:incomingHttpRequestStart')
const requestClose = dc.channel('dd-trace:incomingHttpRequestEnd')
const iastResponseEnd = dc.channel('datadog:iast:response-end')
let isEnabled = false
function enable (config, _tracer) {
if (isEnabled) return
iastTelemetry.configure(config, config.iast?.telemetryVerbosity)
enableFsPlugin(IAST_MODULE)
enableAllAnalyzers(config)
enableTaintTracking(config.iast, iastTelemetry.verbosity)
requestStart.subscribe(onIncomingHttpRequestStart)
requestClose.subscribe(onIncomingHttpRequestEnd)
overheadController.configure(config.iast)
overheadController.startGlobalContext()
securityControls.configure(config.iast)
vulnerabilityReporter.start(config, _tracer)
isEnabled = true
}
function disable () {
if (!isEnabled) return
isEnabled = false
iastTelemetry.stop()
disableFsPlugin(IAST_MODULE)
disableAllAnalyzers()
disableTaintTracking()
overheadController.finishGlobalContext()
if (requestStart.hasSubscribers) requestStart.unsubscribe(onIncomingHttpRequestStart)
if (requestClose.hasSubscribers) requestClose.unsubscribe(onIncomingHttpRequestEnd)
vulnerabilityReporter.stop()
}
function onIncomingHttpRequestStart (data) {
if (data?.req) {
const store = storage('legacy').getStore()
if (store) {
const topContext = web.getContext(data.req)
if (topContext) {
const rootSpan = topContext.span
const isRequestAcquired = overheadController.acquireRequest(rootSpan)
if (isRequestAcquired) {
const iastContext = iastContextFunctions.saveIastContext(store, topContext, { rootSpan, req: data.req })
createTransaction(rootSpan.context().toSpanId(), iastContext)
overheadController.initializeRequestContext(iastContext)
iastTelemetry.onRequestStart(iastContext)
taintTrackingPlugin.taintRequest(data.req, iastContext)
}
if (rootSpan.addTags) {
rootSpan.addTags({
[IAST_ENABLED_TAG_KEY]: isRequestAcquired ? 1 : 0
})
}
}
}
}
}
function onIncomingHttpRequestEnd (data) {
if (data?.req) {
const store = storage('legacy').getStore()
const topContext = web.getContext(data.req)
const iastContext = iastContextFunctions.getIastContext(store, topContext)
if (iastContext?.rootSpan) {
iastResponseEnd.publish(data)
const vulnerabilities = iastContext.vulnerabilities
const rootSpan = iastContext.rootSpan
vulnerabilityReporter.sendVulnerabilities(vulnerabilities, rootSpan)
removeTransaction(iastContext)
iastTelemetry.onRequestEnd(iastContext, iastContext.rootSpan)
}
// TODO web.getContext(data.req) is required when the request is aborted
if (iastContextFunctions.cleanIastContext(store, topContext, iastContext)) {
overheadController.releaseRequest()
}
}
}
module.exports = { enable, disable, onIncomingHttpRequestEnd, onIncomingHttpRequestStart }