UNPKG

dd-trace

Version:

Datadog APM tracing client for JavaScript

130 lines (100 loc) 3.27 kB
'use strict' const { isAbsolute } = require('path') const { fsOperationStart, incomingHttpRequestStart, expressResponseRenderStart } = require('../channels') const { storage } = require('../../../../datadog-core') const { FS_OPERATION_PATH } = require('../addresses') const waf = require('../waf') const { enable: enableFsPlugin, disable: disableFsPlugin, RASP_MODULE } = require('./fs-plugin') const { RULE_TYPES, handleResult } = require('./utils') let config let enabled let analyzeSubscribed function enable (_config) { config = _config if (enabled) return enabled = true incomingHttpRequestStart.subscribe(onFirstReceivedRequest) } function disable () { if (fsOperationStart.hasSubscribers) fsOperationStart.unsubscribe(analyzeLfi) if (incomingHttpRequestStart.hasSubscribers) incomingHttpRequestStart.unsubscribe(onFirstReceivedRequest) if (expressResponseRenderStart.hasSubscribers) expressResponseRenderStart.unsubscribe(analyzeLfiInResponseRender) disableFsPlugin(RASP_MODULE) enabled = false analyzeSubscribed = false } function onFirstReceivedRequest () { // Node.js unsubscribe during publish bug: https://github.com/nodejs/node/pull/55116 process.nextTick(() => { incomingHttpRequestStart.unsubscribe(onFirstReceivedRequest) }) enableFsPlugin(RASP_MODULE) if (!analyzeSubscribed) { fsOperationStart.subscribe(analyzeLfi) expressResponseRenderStart.subscribe(analyzeLfiInResponseRender) analyzeSubscribed = true } } function analyzeLfiInResponseRender (ctx) { const store = storage('legacy').getStore() if (!store) return analyzeLfiPath(ctx.view, ctx.req, store.res, ctx.abortController) } function analyzeLfi (ctx) { const store = storage('legacy').getStore() if (!store) return const { req, fs, res } = store if (!req || !fs) return getPaths(ctx, fs).forEach(path => { analyzeLfiPath(path, req, res, ctx.abortController) }) } function analyzeLfiPath (path, req, res, abortController) { const ephemeral = { [FS_OPERATION_PATH]: path } const raspRule = { type: RULE_TYPES.LFI } const result = waf.run({ ephemeral }, req, raspRule) handleResult(result, req, res, abortController, config, raspRule) } function getPaths (ctx, fs) { // these properties could have String, Buffer, URL, Integer or FileHandle types const pathArguments = [ ctx.dest, ctx.existingPath, ctx.file, ctx.newPath, ctx.oldPath, ctx.path, ctx.prefix, ctx.src, ctx.target ] return pathArguments .map(path => pathToStr(path)) .filter(path => shouldAnalyze(path, fs)) } function pathToStr (path) { if (!path) return if (typeof path === 'string' || // eslint-disable-next-line unicorn/no-instanceof-builtins path instanceof String || path instanceof Buffer || path instanceof URL) { return path.toString() } } function shouldAnalyze (path, fs) { if (!path) return const notExcludedRootOp = !fs.opExcluded && fs.root return notExcludedRootOp && (isAbsolute(path) || path.includes('../') || shouldAnalyzeURLFile(path, fs)) } function shouldAnalyzeURLFile (path, fs) { if (path.startsWith('file://')) { return shouldAnalyze(path.slice(7), fs) } } module.exports = { enable, disable }