dd-trace
Version:
Datadog APM tracing client for JavaScript
183 lines (149 loc) • 5.04 kB
JavaScript
'use strict'
const { storage } = require('../../../../../datadog-core')
const { getNonDDCallSiteFrames } = require('../path-line')
const { getIastContext, getIastStackTraceId } = require('../iast-context')
const overheadController = require('../overhead-controller')
const { SinkIastPlugin } = require('../iast-plugin')
const {
addVulnerability,
getVulnerabilityCallSiteFrames,
replaceCallSiteFromSourceMap
} = require('../vulnerability-reporter')
const { getMarkFromVulnerabilityType } = require('../taint-tracking/secure-marks')
const { SUPPRESSED_VULNERABILITIES } = require('../telemetry/iast-metric')
class Analyzer extends SinkIastPlugin {
constructor (type) {
super()
this._type = type
this._secureMark = getMarkFromVulnerabilityType(type)
}
_isVulnerable (value, context) {
return false
}
_isExcluded (location) {
return false
}
_report (value, context, meta) {
const evidence = this._getEvidence(value, context, meta)
this._reportEvidence(value, context, evidence)
}
_reportEvidence (value, context, evidence) {
const callSiteFrames = getVulnerabilityCallSiteFrames()
const nonDDCallSiteFrames = getNonDDCallSiteFrames(callSiteFrames, this._getExcludedPaths())
const location = this._getLocation(value, nonDDCallSiteFrames)
if (!this._isExcluded(location)) {
const originalLocation = this._getOriginalLocation(location)
const spanId = context?.rootSpan?.context().toSpanId()
const stackId = getIastStackTraceId(context)
const vulnerability = this._createVulnerability(
this._type,
evidence,
spanId,
originalLocation,
stackId
)
addVulnerability(context, vulnerability, nonDDCallSiteFrames)
}
}
_reportIfVulnerable (value, context, meta) {
if (this._isVulnerable(value, context) && this._checkOCE(context, value)) {
this._report(value, context, meta)
return true
}
return false
}
_getEvidence (value) {
return { value }
}
_getLocation (value, callSiteFrames) {
return callSiteFrames[0]
}
_getOriginalLocation (location) {
const locationFromSourceMap = replaceCallSiteFromSourceMap(location)
const originalLocation = {}
if (locationFromSourceMap?.path) {
originalLocation.path = locationFromSourceMap.path
}
if (locationFromSourceMap?.line) {
originalLocation.line = locationFromSourceMap.line
}
if (location?.class_name) {
originalLocation.class = location.class_name
}
if (location?.function) {
originalLocation.method = location.function
}
return originalLocation
}
_getExcludedPaths () {}
_isInvalidContext (store, iastContext) {
return store && !iastContext
}
analyze (value, store = storage('legacy').getStore(), meta) {
const iastContext = getIastContext(store)
if (this._isInvalidContext(store, iastContext)) return
this._reportIfVulnerable(value, iastContext, meta)
}
analyzeAll (...values) {
const store = storage('legacy').getStore()
const iastContext = getIastContext(store)
if (this._isInvalidContext(store, iastContext)) return
for (const value of values) {
if (this._isVulnerable(value, iastContext)) {
// TODO(BridgeAR): Here are multiple cases that receive a different
// number of arguments than passed through. Fix those cases.
if (this._checkOCE(iastContext, value)) {
this._report(value, iastContext)
}
break
}
}
}
_checkOCE (context) {
return overheadController.hasQuota(overheadController.OPERATIONS.REPORT_VULNERABILITY, context, this._type)
}
_createVulnerability (type, evidence, spanId, location, stackId) {
if (type && evidence) {
const _spanId = spanId || 0
return {
type,
evidence,
location: {
spanId: _spanId,
stackId,
...location
},
hash: this._createHash(this._createHashSource(type, evidence, location))
}
}
return null
}
_createHashSource (type, evidence, location) {
return location ? `${type}:${location.path}:${location.line}` : type
}
_createHash (hashSource) {
let hash = 0
let offset = 0
const size = hashSource.length
for (let i = 0; i < size; i++) {
hash = ((hash << 5) - hash) + hashSource.charCodeAt(offset++)
}
return hash
}
_getSuppressedMetricTag () {
if (!this._suppressedMetricTag) {
this._suppressedMetricTag = SUPPRESSED_VULNERABILITIES.formatTags(this._type)[0]
}
return this._suppressedMetricTag
}
_incrementSuppressedMetric (iastContext) {
SUPPRESSED_VULNERABILITIES.inc(iastContext, this._getSuppressedMetricTag())
}
addSub (iastSubOrChannelName, handler) {
const iastSub = typeof iastSubOrChannelName === 'string'
? { channelName: iastSubOrChannelName }
: iastSubOrChannelName
super.addSub({ tag: this._type, ...iastSub }, handler)
}
}
module.exports = Analyzer