UNPKG

dd-trace

Version:

Datadog APM tracing client for JavaScript

118 lines (91 loc) 3.68 kB
'use strict' const fs = require('fs') const log = require('../log') const RuleManager = require('./rule_manager') const { incomingHttpRequestStart, incomingHttpRequestEnd } = require('./gateway/channels') const Gateway = require('./gateway/engine') const addresses = require('./addresses') const Reporter = require('./reporter') function enable (config) { try { // TODO: enable dc_blocking: config.appsec.blocking === true let rules = fs.readFileSync(config.appsec.rules) rules = JSON.parse(rules) RuleManager.applyRules(rules) } catch (err) { log.error('Unable to start AppSec') log.error(err) // abort AppSec start RuleManager.clearAllRules() return } Reporter.setRateLimit(config.appsec.rateLimit) incomingHttpRequestStart.subscribe(incomingHttpStartTranslator) incomingHttpRequestEnd.subscribe(incomingHttpEndTranslator) // add fields needed for HTTP context reporting Gateway.manager.addresses.add(addresses.HTTP_INCOMING_HEADERS) Gateway.manager.addresses.add(addresses.HTTP_INCOMING_ENDPOINT) Gateway.manager.addresses.add(addresses.HTTP_INCOMING_RESPONSE_HEADERS) Gateway.manager.addresses.add(addresses.HTTP_INCOMING_REMOTE_IP) } function incomingHttpStartTranslator (data) { // TODO: get span from datadog-core storage instead const topSpan = data.req._datadog && data.req._datadog.span if (topSpan) { topSpan.addTags({ '_dd.appsec.enabled': 1, '_dd.runtime_family': 'nodejs' }) } const store = Gateway.startContext() store.set('req', data.req) store.set('res', data.res) } function incomingHttpEndTranslator (data) { const context = Gateway.getContext() if (!context) return const requestHeaders = Object.assign({}, data.req.headers) delete requestHeaders.cookie // TODO: this doesn't support headers sent with res.writeHead() const responseHeaders = Object.assign({}, data.res.getHeaders()) delete responseHeaders['set-cookie'] const payload = { [addresses.HTTP_INCOMING_URL]: data.req.url, [addresses.HTTP_INCOMING_HEADERS]: requestHeaders, [addresses.HTTP_INCOMING_METHOD]: data.req.method, [addresses.HTTP_INCOMING_REMOTE_IP]: data.req.socket.remoteAddress, [addresses.HTTP_INCOMING_REMOTE_PORT]: data.req.socket.remotePort, [addresses.HTTP_INCOMING_RESPONSE_CODE]: data.res.statusCode, [addresses.HTTP_INCOMING_RESPONSE_HEADERS]: responseHeaders } // TODO: temporary express instrumentation, will use express plugin later if (data.req.body !== undefined && data.req.body !== null) { payload[addresses.HTTP_INCOMING_BODY] = data.req.body } if (data.req.query && typeof data.req.query === 'object') { payload[addresses.HTTP_INCOMING_QUERY] = data.req.query } if (data.req.route && typeof data.req.route.path === 'string') { payload[addresses.HTTP_INCOMING_ENDPOINT] = data.req.route.path } if (data.req.params && typeof data.req.params === 'object') { payload[addresses.HTTP_INCOMING_PARAMS] = data.req.params } if (data.req.cookies && typeof data.req.cookies === 'object') { payload[addresses.HTTP_INCOMING_COOKIES] = data.req.cookies } Gateway.propagate(payload, context) Reporter.finishAttacks(data.req, context) } function disable () { RuleManager.clearAllRules() // Channel#unsubscribe() is undefined for non active channels if (incomingHttpRequestStart.hasSubscribers) incomingHttpRequestStart.unsubscribe(incomingHttpStartTranslator) if (incomingHttpRequestEnd.hasSubscribers) incomingHttpRequestEnd.unsubscribe(incomingHttpEndTranslator) } module.exports = { enable, disable, incomingHttpStartTranslator, incomingHttpEndTranslator }