dd-trace 3.10.0 → 3.11.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 +1 -0
- package/index.d.ts +17 -1
- package/package.json +7 -6
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/http/server.js +7 -1
- package/packages/datadog-instrumentations/src/ldapjs.js +91 -0
- package/packages/datadog-instrumentations/src/pg.js +1 -2
- package/packages/datadog-plugin-http/src/server.js +7 -3
- package/packages/datadog-plugin-router/src/index.js +6 -3
- package/packages/dd-trace/src/appsec/addresses.js +3 -1
- package/packages/dd-trace/src/appsec/blocking.js +44 -0
- package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +1 -1
- package/packages/dd-trace/src/appsec/gateway/engine/engine.js +1 -1
- package/packages/dd-trace/src/appsec/gateway/engine/index.js +6 -1
- package/packages/dd-trace/src/appsec/gateway/engine/runner.js +0 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +2 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/ldap-injection-analyzer.js +11 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +40 -2
- package/packages/dd-trace/src/appsec/iast/iast-context.js +3 -1
- package/packages/dd-trace/src/appsec/iast/index.js +7 -0
- package/packages/dd-trace/src/appsec/iast/path-line.js +6 -5
- package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +12 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +2 -29
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +16 -15
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +85 -0
- package/packages/dd-trace/src/appsec/index.js +68 -27
- package/packages/dd-trace/src/{plugins/util → appsec}/ip_blocklist.js +0 -0
- package/packages/dd-trace/src/appsec/ip_extractor.js +98 -0
- package/packages/dd-trace/src/appsec/remote_config/index.js +2 -1
- package/packages/dd-trace/src/appsec/templates/blocked.html +99 -0
- package/packages/dd-trace/src/appsec/templates/blocked.json +8 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +4 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +18 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +12 -6
- package/packages/dd-trace/src/config.js +24 -2
- package/packages/dd-trace/src/plugin_manager.js +0 -10
- package/packages/dd-trace/src/plugins/util/web.js +1 -104
package/LICENSE-3rdparty.csv
CHANGED
|
@@ -20,6 +20,7 @@ require,lodash.uniq,MIT,Copyright JS Foundation and other contributors
|
|
|
20
20
|
require,lru-cache,ISC,Copyright (c) 2010-2022 Isaac Z. Schlueter and Contributors
|
|
21
21
|
require,methods,MIT,Copyright 2013-2014 TJ Holowaychuk
|
|
22
22
|
require,module-details-from-path,MIT,Copyright 2016 Thomas Watson Steen
|
|
23
|
+
require,node-abort-controller,MIT,Copyright (c) 2019 Steve Faulkner
|
|
23
24
|
require,opentracing,MIT,Copyright 2016 Resonance Labs Inc
|
|
24
25
|
require,path-to-regexp,MIT,Copyright 2014 Blake Embrey
|
|
25
26
|
require,protobufjs,BSD-3-Clause,Copyright 2016 Daniel Wirtz
|
package/index.d.ts
CHANGED
|
@@ -509,7 +509,17 @@ export declare interface TracerOptions {
|
|
|
509
509
|
/**
|
|
510
510
|
* Specifies a regex that will redact sensitive data by its value in attack reports.
|
|
511
511
|
*/
|
|
512
|
-
obfuscatorValueRegex?: string
|
|
512
|
+
obfuscatorValueRegex?: string,
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Specifies a path to a custom blocking template html file.
|
|
516
|
+
*/
|
|
517
|
+
blockedTemplateHtml?: string,
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Specifies a path to a custom blocking template json file.
|
|
521
|
+
*/
|
|
522
|
+
blockedTemplateJson?: string,
|
|
513
523
|
};
|
|
514
524
|
}
|
|
515
525
|
|
|
@@ -1208,6 +1218,12 @@ declare namespace plugins {
|
|
|
1208
1218
|
*/
|
|
1209
1219
|
interface kafkajs extends Instrumentation {}
|
|
1210
1220
|
|
|
1221
|
+
/**
|
|
1222
|
+
* This plugin automatically instruments the
|
|
1223
|
+
* [ldapjs](https://github.com/ldapjs/node-ldapjs/) module.
|
|
1224
|
+
*/
|
|
1225
|
+
interface ldapjs extends Instrumentation {}
|
|
1226
|
+
|
|
1211
1227
|
/**
|
|
1212
1228
|
* This plugin automatically instruments the
|
|
1213
1229
|
* [mariadb](https://github.com/mariadb-corporation/mariadb-connector-nodejs) module.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.11.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -16,14 +16,14 @@
|
|
|
16
16
|
"services": "node ./scripts/install_plugin_modules && node packages/dd-trace/test/setup/services",
|
|
17
17
|
"tdd": "node scripts/tdd.js",
|
|
18
18
|
"test": "SERVICES=* yarn services && mocha --colors --exit --expose-gc 'packages/dd-trace/test/setup/node.js' 'packages/*/test/**/*.spec.js'",
|
|
19
|
-
"test:trace:core": "mocha --colors --exit --expose-gc --file packages/dd-trace/test/setup/core.js --exclude \"packages/dd-trace/test/profiling/**/*.spec.js\" \"packages/dd-trace/test/**/*.spec.js\"",
|
|
20
|
-
"test:trace:core:ci": "nyc --no-clean --include \"packages/dd-trace/src/**/*.js\" --exclude \"packages/dd-trace/src/profiling/**/*.js\" -- npm run test:trace:core -- --reporter mocha-multi-reporters --reporter-options configFile=mocha-reporter.json",
|
|
19
|
+
"test:trace:core": "mocha --colors --exit --expose-gc --file packages/dd-trace/test/setup/core.js --exclude \"packages/dd-trace/test/profiling/**/*.spec.js\" --exclude \"packages/dd-trace/test/appsec/iast/**/*.plugin.spec.js\" \"packages/dd-trace/test/**/*.spec.js\"",
|
|
20
|
+
"test:trace:core:ci": "nyc --no-clean --include \"packages/dd-trace/src/**/*.js\" --exclude \"packages/dd-trace/src/profiling/**/*.js\" --exclude \"packages/dd-trace/test/appsec/iast/**/*.plugin.spec.js\" -- npm run test:trace:core -- --reporter mocha-multi-reporters --reporter-options configFile=mocha-reporter.json",
|
|
21
21
|
"test:instrumentations": "mocha --colors --file 'packages/dd-trace/test/setup/core.js' 'packages/datadog-instrumentations/test/**/*.spec.js'",
|
|
22
22
|
"test:instrumentations:ci": "nyc --no-clean --include 'packages/datadog-instrumentations/src/**/*.js' -- npm run test:instrumentations",
|
|
23
23
|
"test:core": "mocha --colors --file packages/datadog-core/test/setup.js 'packages/datadog-core/test/**/*.spec.js'",
|
|
24
24
|
"test:core:ci": "nyc --no-clean --include 'packages/datadog-core/src/**/*.js' -- npm run test:core",
|
|
25
|
-
"test:plugins": "mocha --colors --exit --file \"packages/dd-trace/test/setup/core.js\" \"packages/datadog-instrumentations/test/@($(echo $PLUGINS)).spec.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/**/*.spec.js\"",
|
|
26
|
-
"test:plugins:ci": "yarn services && nyc --no-clean --include \"packages/datadog-instrumentations/src/@($(echo $PLUGINS)).js\" --include \"packages/datadog-instrumentations/src/@($(echo $PLUGINS))/**/*.js\" --include \"packages/datadog-plugin-@($(echo $PLUGINS))/src/**/*.js\" -- npm run test:plugins",
|
|
25
|
+
"test:plugins": "mocha --colors --exit --file \"packages/dd-trace/test/setup/core.js\" \"packages/datadog-instrumentations/test/@($(echo $PLUGINS)).spec.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/**/*.spec.js\" \"packages/dd-trace/test/appsec/iast/**/*.@($(echo $PLUGINS)).plugin.spec.js\"",
|
|
26
|
+
"test:plugins:ci": "yarn services && nyc --no-clean --include \"packages/datadog-instrumentations/src/@($(echo $PLUGINS)).js\" --include \"packages/datadog-instrumentations/src/@($(echo $PLUGINS))/**/*.js\" --include \"packages/datadog-plugin-@($(echo $PLUGINS))/src/**/*.js\" --include \"packages/dd-trace/test/appsec/iast/**/*.@($(echo $PLUGINS)).plugin.spec.js\" -- npm run test:plugins",
|
|
27
27
|
"test:plugins:upstream": "node ./packages/dd-trace/test/plugins/suite.js",
|
|
28
28
|
"test:profiler": "mocha --colors --exit --file \"packages/dd-trace/test/setup/core.js\" \"packages/dd-trace/test/profiling/**/*.spec.js\"",
|
|
29
29
|
"test:profiler:ci": "nyc --no-clean --include \"packages/dd-trace/src/profiling/**/*.js\" -- npm run test:profiler",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@datadog/native-appsec": "2.0.0",
|
|
62
|
-
"@datadog/native-iast-rewriter": "1.
|
|
62
|
+
"@datadog/native-iast-rewriter": "1.1.2",
|
|
63
63
|
"@datadog/native-iast-taint-tracking": "1.0.0",
|
|
64
64
|
"@datadog/native-metrics": "^1.5.0",
|
|
65
65
|
"@datadog/pprof": "^1.1.1",
|
|
@@ -79,6 +79,7 @@
|
|
|
79
79
|
"lru-cache": "^7.14.0",
|
|
80
80
|
"methods": "^1.1.2",
|
|
81
81
|
"module-details-from-path": "^1.0.3",
|
|
82
|
+
"node-abort-controller": "^3.0.1",
|
|
82
83
|
"opentracing": ">=0.12.1",
|
|
83
84
|
"path-to-regexp": "^0.1.2",
|
|
84
85
|
"protobufjs": "^7.1.2",
|
|
@@ -46,6 +46,7 @@ module.exports = {
|
|
|
46
46
|
'koa': () => require('../koa'),
|
|
47
47
|
'koa-router': () => require('../koa'),
|
|
48
48
|
'kafkajs': () => require('../kafkajs'),
|
|
49
|
+
'ldapjs': () => require('../ldapjs'),
|
|
49
50
|
'limitd-client': () => require('../limitd-client'),
|
|
50
51
|
'mariadb': () => require('../mariadb'),
|
|
51
52
|
'memcached': () => require('../memcached'),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { AbortController } = require('node-abort-controller') // AbortController is not available in node <15
|
|
3
4
|
const {
|
|
4
5
|
channel,
|
|
5
6
|
addHook
|
|
@@ -48,9 +49,14 @@ function wrapEmit (emit) {
|
|
|
48
49
|
if (eventName === 'request') {
|
|
49
50
|
res.req = req
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
const abortController = new AbortController()
|
|
53
|
+
|
|
54
|
+
startServerCh.publish({ req, res, abortController })
|
|
52
55
|
|
|
53
56
|
try {
|
|
57
|
+
if (abortController.signal.aborted) {
|
|
58
|
+
return res.end()
|
|
59
|
+
}
|
|
54
60
|
return emit.apply(this, arguments)
|
|
55
61
|
} catch (err) {
|
|
56
62
|
errorServerCh.publish(err)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
channel,
|
|
5
|
+
addHook,
|
|
6
|
+
AsyncResource
|
|
7
|
+
} = require('./helpers/instrument')
|
|
8
|
+
const shimmer = require('../../datadog-shimmer')
|
|
9
|
+
|
|
10
|
+
function isString (value) {
|
|
11
|
+
return typeof value === 'string' || value instanceof String
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function getCallbackArgIndex (args) {
|
|
15
|
+
let callbackIndex = -1
|
|
16
|
+
for (let i = args.length - 1; i >= 0; i--) {
|
|
17
|
+
if (typeof args[i] === 'function') {
|
|
18
|
+
callbackIndex = i
|
|
19
|
+
break
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return callbackIndex
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function wrapEmitter (corkedEmitter) {
|
|
26
|
+
const callbackMap = new WeakMap()
|
|
27
|
+
|
|
28
|
+
const addListener = on => function (name, fn) {
|
|
29
|
+
if (typeof fn === 'function') {
|
|
30
|
+
let bindedFn = callbackMap.get(fn)
|
|
31
|
+
if (!bindedFn) {
|
|
32
|
+
const callbackResource = new AsyncResource('bound-anonymous-fn')
|
|
33
|
+
bindedFn = callbackResource.bind(fn)
|
|
34
|
+
callbackMap.set(fn, bindedFn)
|
|
35
|
+
}
|
|
36
|
+
arguments[1] = bindedFn
|
|
37
|
+
}
|
|
38
|
+
on.apply(this, arguments)
|
|
39
|
+
}
|
|
40
|
+
shimmer.wrap(corkedEmitter, 'on', addListener)
|
|
41
|
+
shimmer.wrap(corkedEmitter, 'addListener', addListener)
|
|
42
|
+
|
|
43
|
+
const removeListener = off => function (name, fn) {
|
|
44
|
+
if (typeof fn === 'function') {
|
|
45
|
+
const emitterOn = callbackMap.get(fn)
|
|
46
|
+
if (emitterOn) {
|
|
47
|
+
arguments[1] = emitterOn
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
off.apply(this, arguments)
|
|
51
|
+
}
|
|
52
|
+
shimmer.wrap(corkedEmitter, 'off', removeListener)
|
|
53
|
+
shimmer.wrap(corkedEmitter, 'removeListener', removeListener)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
addHook({ name: 'ldapjs', versions: ['>=2'] }, ldapjs => {
|
|
57
|
+
const ldapSearchCh = channel('datadog:ldapjs:client:search')
|
|
58
|
+
|
|
59
|
+
shimmer.wrap(ldapjs.Client.prototype, 'search', search => function (base, options) {
|
|
60
|
+
if (ldapSearchCh.hasSubscribers) {
|
|
61
|
+
let filter
|
|
62
|
+
if (isString(options)) {
|
|
63
|
+
filter = options
|
|
64
|
+
} else if (typeof options === 'object' && options.filter) {
|
|
65
|
+
if (isString(options.filter)) {
|
|
66
|
+
filter = options.filter
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
ldapSearchCh.publish({ base, filter })
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return search.apply(this, arguments)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
shimmer.wrap(ldapjs.Client.prototype, '_send', _send => function () {
|
|
76
|
+
const callbackIndex = getCallbackArgIndex(arguments)
|
|
77
|
+
if (callbackIndex > -1) {
|
|
78
|
+
const callback = arguments[callbackIndex]
|
|
79
|
+
arguments[callbackIndex] = shimmer.wrap(callback, function (err, corkedEmitter) {
|
|
80
|
+
if (typeof corkedEmitter === 'object' && typeof corkedEmitter['on'] === 'function') {
|
|
81
|
+
wrapEmitter(corkedEmitter)
|
|
82
|
+
}
|
|
83
|
+
callback.apply(this, arguments)
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return _send.apply(this, arguments)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
return ldapjs
|
|
91
|
+
})
|
|
@@ -40,9 +40,8 @@ function wrapQuery (query) {
|
|
|
40
40
|
const callbackResource = new AsyncResource('bound-anonymous-fn')
|
|
41
41
|
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
42
42
|
const processId = this.processID
|
|
43
|
-
|
|
44
43
|
return asyncResource.runInAsyncScope(() => {
|
|
45
|
-
startCh.publish({ params: this.connectionParameters, query: pgQuery, processId })
|
|
44
|
+
startCh.publish({ params: this.connectionParameters, originalQuery: pgQuery.text, query: pgQuery, processId })
|
|
46
45
|
|
|
47
46
|
const finish = asyncResource.bind(function (error) {
|
|
48
47
|
if (error) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const Plugin = require('../../dd-trace/src/plugins/plugin')
|
|
4
4
|
const { storage } = require('../../datadog-core')
|
|
5
5
|
const web = require('../../dd-trace/src/plugins/util/web')
|
|
6
|
-
const { incomingHttpRequestStart } = require('../../dd-trace/src/appsec/gateway/channels')
|
|
6
|
+
const { incomingHttpRequestStart, incomingHttpRequestEnd } = require('../../dd-trace/src/appsec/gateway/channels')
|
|
7
7
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
8
8
|
|
|
9
9
|
class HttpServerPlugin extends Plugin {
|
|
@@ -16,7 +16,7 @@ class HttpServerPlugin extends Plugin {
|
|
|
16
16
|
|
|
17
17
|
this._parentStore = undefined
|
|
18
18
|
|
|
19
|
-
this.addSub('apm:http:server:request:start', ({ req, res }) => {
|
|
19
|
+
this.addSub('apm:http:server:request:start', ({ req, res, abortController }) => {
|
|
20
20
|
const store = storage.getStore()
|
|
21
21
|
const span = web.startSpan(this.tracer, this.config, req, res, 'web.request')
|
|
22
22
|
|
|
@@ -33,7 +33,7 @@ class HttpServerPlugin extends Plugin {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
if (incomingHttpRequestStart.hasSubscribers) {
|
|
36
|
-
incomingHttpRequestStart.publish({ req, res })
|
|
36
|
+
incomingHttpRequestStart.publish({ req, res, abortController })
|
|
37
37
|
}
|
|
38
38
|
})
|
|
39
39
|
|
|
@@ -52,6 +52,10 @@ class HttpServerPlugin extends Plugin {
|
|
|
52
52
|
|
|
53
53
|
if (!context || !context.res) return // Not created by a http.Server instance.
|
|
54
54
|
|
|
55
|
+
if (incomingHttpRequestEnd.hasSubscribers) {
|
|
56
|
+
incomingHttpRequestEnd.publish({ req, res: context.res })
|
|
57
|
+
}
|
|
58
|
+
|
|
55
59
|
web.finishAll(context)
|
|
56
60
|
})
|
|
57
61
|
}
|
|
@@ -29,8 +29,9 @@ class RouterPlugin extends WebPlugin {
|
|
|
29
29
|
context.middleware.push(span)
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
this.
|
|
32
|
+
const store = storage.getStore()
|
|
33
|
+
this._storeStack.push(store)
|
|
34
|
+
this.enter(span, store)
|
|
34
35
|
|
|
35
36
|
web.patch(req)
|
|
36
37
|
web.setRoute(req, context.route)
|
|
@@ -53,7 +54,9 @@ class RouterPlugin extends WebPlugin {
|
|
|
53
54
|
})
|
|
54
55
|
|
|
55
56
|
this.addSub(`apm:${this.constructor.name}:middleware:exit`, ({ req }) => {
|
|
56
|
-
this.
|
|
57
|
+
const savedStore = this._storeStack.pop()
|
|
58
|
+
const span = savedStore && savedStore.span
|
|
59
|
+
this.enter(span, savedStore)
|
|
57
60
|
})
|
|
58
61
|
|
|
59
62
|
this.addSub(`apm:${this.constructor.name}:middleware:error`, ({ req, error }) => {
|
|
@@ -14,5 +14,7 @@ module.exports = {
|
|
|
14
14
|
HTTP_INCOMING_RESPONSE_HEADERS: 'server.response.headers.no_cookies',
|
|
15
15
|
// TODO: 'server.response.trailers',
|
|
16
16
|
HTTP_INCOMING_REMOTE_IP: 'server.request.client_ip',
|
|
17
|
-
HTTP_INCOMING_REMOTE_PORT: 'server.request.client_port'
|
|
17
|
+
HTTP_INCOMING_REMOTE_PORT: 'server.request.client_port',
|
|
18
|
+
|
|
19
|
+
HTTP_CLIENT_IP: 'http.client_ip'
|
|
18
20
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
let templateHtml, templateJson
|
|
5
|
+
function block (req, res, topSpan, abortController) {
|
|
6
|
+
let type
|
|
7
|
+
let body
|
|
8
|
+
|
|
9
|
+
// parse the Accept header, ex: Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8
|
|
10
|
+
const accept = req.headers.accept && req.headers.accept.split(',').map((str) => str.split(';', 1)[0].trim())
|
|
11
|
+
|
|
12
|
+
if (accept && accept.includes('text/html') && !accept.includes('application/json')) {
|
|
13
|
+
type = 'text/html'
|
|
14
|
+
body = templateHtml
|
|
15
|
+
} else {
|
|
16
|
+
type = 'application/json'
|
|
17
|
+
body = templateJson
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
topSpan.addTags({
|
|
21
|
+
'appsec.blocked': 'true'
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
res.statusCode = 403
|
|
25
|
+
res.setHeader('Content-Type', type)
|
|
26
|
+
res.setHeader('Content-Length', Buffer.byteLength(body))
|
|
27
|
+
res.end(body)
|
|
28
|
+
|
|
29
|
+
abortController.abort()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function loadTemplates (config) {
|
|
33
|
+
templateHtml = fs.readFileSync(config.appsec.blockedTemplateHtml)
|
|
34
|
+
templateJson = fs.readFileSync(config.appsec.blockedTemplateJson)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function loadTemplatesAsync (config) {
|
|
38
|
+
templateHtml = await fs.promises.readFile(config.appsec.blockedTemplateHtml)
|
|
39
|
+
templateJson = await fs.promises.readFile(config.appsec.blockedTemplateJson)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = {
|
|
43
|
+
block, loadTemplates, loadTemplatesAsync
|
|
44
|
+
}
|
|
@@ -28,7 +28,7 @@ class SubscriptionManager {
|
|
|
28
28
|
const list = this.addressToSubscriptions.get(address)
|
|
29
29
|
|
|
30
30
|
if (list === undefined) {
|
|
31
|
-
this.addressToSubscriptions.set(address, [
|
|
31
|
+
this.addressToSubscriptions.set(address, [subscription])
|
|
32
32
|
} else {
|
|
33
33
|
list.push(subscription)
|
|
34
34
|
}
|
|
@@ -22,6 +22,10 @@ function getContext () {
|
|
|
22
22
|
return store && store.get('context')
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
function needsAddress (address) {
|
|
26
|
+
return manager.addresses.has(address)
|
|
27
|
+
}
|
|
28
|
+
|
|
25
29
|
function propagate (data, context = getContext()) {
|
|
26
30
|
if (!context) return
|
|
27
31
|
|
|
@@ -30,7 +34,7 @@ function propagate (data, context = getContext()) {
|
|
|
30
34
|
for (let i = 0; i < keys.length; ++i) {
|
|
31
35
|
const key = keys[i]
|
|
32
36
|
|
|
33
|
-
if (
|
|
37
|
+
if (needsAddress(key)) {
|
|
34
38
|
context.setValue(key, data[key])
|
|
35
39
|
}
|
|
36
40
|
}
|
|
@@ -42,5 +46,6 @@ module.exports = {
|
|
|
42
46
|
manager,
|
|
43
47
|
startContext,
|
|
44
48
|
getContext,
|
|
49
|
+
needsAddress,
|
|
45
50
|
propagate
|
|
46
51
|
}
|
|
@@ -2,5 +2,6 @@ module.exports = {
|
|
|
2
2
|
'WEAK_CIPHER_ANALYZER': require('./weak-cipher-analyzer'),
|
|
3
3
|
'WEAK_HASH_ANALYZER': require('./weak-hash-analyzer'),
|
|
4
4
|
'SQL_INJECTION_ANALYZER': require('./sql-injection-analyzer'),
|
|
5
|
-
'COMMAND_INJECTION_ANALYZER': require('./command-injection-analyzer')
|
|
5
|
+
'COMMAND_INJECTION_ANALYZER': require('./command-injection-analyzer'),
|
|
6
|
+
'LDAP_ANALYZER': require('./ldap-injection-analyzer')
|
|
6
7
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const InjectionAnalyzer = require('./injection-analyzer')
|
|
3
|
+
|
|
4
|
+
class LdapInjectionAnalyzer extends InjectionAnalyzer {
|
|
5
|
+
constructor () {
|
|
6
|
+
super('LDAP_INJECTION')
|
|
7
|
+
this.addSub('datadog:ldapjs:client:search', ({ base, filter }) => this.analyzeAll(base, filter))
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
module.exports = new LdapInjectionAnalyzer()
|
|
@@ -6,7 +6,7 @@ class SqlInjectionAnalyzer extends InjectionAnalyzer {
|
|
|
6
6
|
super('SQL_INJECTION')
|
|
7
7
|
this.addSub('apm:mysql:query:start', ({ sql }) => this.analyze(sql))
|
|
8
8
|
this.addSub('apm:mysql2:query:start', ({ sql }) => this.analyze(sql))
|
|
9
|
-
this.addSub('apm:pg:query:start', ({
|
|
9
|
+
this.addSub('apm:pg:query:start', ({ originalQuery }) => this.analyze(originalQuery))
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const Plugin = require('../../../../src/plugins/plugin')
|
|
4
4
|
const { storage } = require('../../../../../datadog-core')
|
|
5
|
+
const log = require('../../../log')
|
|
5
6
|
const { getFirstNonDDPathAndLine } = require('../path-line')
|
|
6
7
|
const { createVulnerability, addVulnerability } = require('../vulnerability-reporter')
|
|
7
8
|
const { getIastContext } = require('../iast-context')
|
|
@@ -13,6 +14,20 @@ class Analyzer extends Plugin {
|
|
|
13
14
|
this._type = type
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
_wrapHandler (handler) {
|
|
18
|
+
return (message, name) => {
|
|
19
|
+
try {
|
|
20
|
+
handler(message, name)
|
|
21
|
+
} catch (e) {
|
|
22
|
+
log.debug(e)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
addSub (channelName, handler) {
|
|
28
|
+
super.addSub(channelName, this._wrapHandler(handler))
|
|
29
|
+
}
|
|
30
|
+
|
|
16
31
|
_isVulnerable (value, context) {
|
|
17
32
|
return false
|
|
18
33
|
}
|
|
@@ -25,6 +40,14 @@ class Analyzer extends Plugin {
|
|
|
25
40
|
addVulnerability(context, vulnerability)
|
|
26
41
|
}
|
|
27
42
|
|
|
43
|
+
_reportIfVulnerable (value, context) {
|
|
44
|
+
if (this._isVulnerable(value, context) && this._checkOCE(context)) {
|
|
45
|
+
this._report(value, context)
|
|
46
|
+
return true
|
|
47
|
+
}
|
|
48
|
+
return false
|
|
49
|
+
}
|
|
50
|
+
|
|
28
51
|
_getEvidence (value) {
|
|
29
52
|
return { value }
|
|
30
53
|
}
|
|
@@ -35,8 +58,23 @@ class Analyzer extends Plugin {
|
|
|
35
58
|
|
|
36
59
|
analyze (value) {
|
|
37
60
|
const iastContext = getIastContext(storage.getStore())
|
|
38
|
-
if (iastContext
|
|
39
|
-
|
|
61
|
+
if (!iastContext) return
|
|
62
|
+
|
|
63
|
+
this._reportIfVulnerable(value, iastContext)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
analyzeAll (...values) {
|
|
67
|
+
const iastContext = getIastContext(storage.getStore())
|
|
68
|
+
if (!iastContext) return
|
|
69
|
+
|
|
70
|
+
for (let i = 0; i < values.length; i++) {
|
|
71
|
+
const value = values[i]
|
|
72
|
+
if (this._isVulnerable(value, iastContext)) {
|
|
73
|
+
if (this._checkOCE(iastContext)) {
|
|
74
|
+
this._report(value, iastContext)
|
|
75
|
+
}
|
|
76
|
+
break
|
|
77
|
+
}
|
|
40
78
|
}
|
|
41
79
|
}
|
|
42
80
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const IAST_CONTEXT_KEY = Symbol('_dd.iast.context')
|
|
2
|
+
const IAST_TRANSACTION_ID = Symbol('_dd.iast.transactionId')
|
|
2
3
|
|
|
3
4
|
function getIastContext (store) {
|
|
4
5
|
return store && store[IAST_CONTEXT_KEY]
|
|
@@ -46,5 +47,6 @@ module.exports = {
|
|
|
46
47
|
getIastContext,
|
|
47
48
|
saveIastContext,
|
|
48
49
|
cleanIastContext,
|
|
49
|
-
IAST_CONTEXT_KEY
|
|
50
|
+
IAST_CONTEXT_KEY,
|
|
51
|
+
IAST_TRANSACTION_ID
|
|
50
52
|
}
|
|
@@ -7,6 +7,8 @@ const dc = require('diagnostics_channel')
|
|
|
7
7
|
const iastContextFunctions = require('./iast-context')
|
|
8
8
|
const { enableTaintTracking, disableTaintTracking, createTransaction, removeTransaction } = require('./taint-tracking')
|
|
9
9
|
|
|
10
|
+
const IAST_ENABLED_TAG_KEY = '_dd.iast.enabled'
|
|
11
|
+
|
|
10
12
|
// TODO Change to `apm:http:server:request:[start|close]` when the subscription
|
|
11
13
|
// order of the callbacks can be enforce
|
|
12
14
|
const requestStart = dc.channel('dd-trace:incomingHttpRequestStart')
|
|
@@ -40,6 +42,11 @@ function onIncomingHttpRequestStart (data) {
|
|
|
40
42
|
createTransaction(rootSpan.context().toSpanId(), iastContext)
|
|
41
43
|
overheadController.initializeRequestContext(iastContext)
|
|
42
44
|
}
|
|
45
|
+
if (rootSpan.addTags) {
|
|
46
|
+
rootSpan.addTags({
|
|
47
|
+
[IAST_ENABLED_TAG_KEY]: isRequestAcquired ? '1' : '0'
|
|
48
|
+
})
|
|
49
|
+
}
|
|
43
50
|
}
|
|
44
51
|
}
|
|
45
52
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
|
+
const process = require('process')
|
|
2
3
|
const pathLine = {
|
|
3
4
|
getFirstNonDDPathAndLine,
|
|
4
5
|
getFirstNonDDPathAndLineFromCallsites, // Exported only for test purposes
|
|
@@ -7,7 +8,7 @@ const pathLine = {
|
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
const EXCLUDED_PATHS = [
|
|
10
|
-
'
|
|
11
|
+
path.join(path.sep, 'node_modules', 'diagnostics_channel')
|
|
11
12
|
]
|
|
12
13
|
const EXCLUDED_PATH_PREFIXES = [
|
|
13
14
|
'node:diagnostics_channel',
|
|
@@ -20,7 +21,7 @@ const EXCLUDED_PATH_PREFIXES = [
|
|
|
20
21
|
|
|
21
22
|
function calculateDDBasePath (dirname) {
|
|
22
23
|
const dirSteps = dirname.split(path.sep)
|
|
23
|
-
const packagesIndex = dirSteps.
|
|
24
|
+
const packagesIndex = dirSteps.lastIndexOf('packages')
|
|
24
25
|
return dirSteps.slice(0, packagesIndex).join(path.sep) + path.sep
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -43,10 +44,10 @@ function getFirstNonDDPathAndLineFromCallsites (callsites) {
|
|
|
43
44
|
if (callsites) {
|
|
44
45
|
for (let i = 0; i < callsites.length; i++) {
|
|
45
46
|
const callsite = callsites[i]
|
|
46
|
-
const
|
|
47
|
-
if (!isExcluded(callsite) &&
|
|
47
|
+
const filepath = callsite.getFileName()
|
|
48
|
+
if (!isExcluded(callsite) && filepath.indexOf(pathLine.ddBasePath) === -1) {
|
|
48
49
|
return {
|
|
49
|
-
path,
|
|
50
|
+
path: path.relative(process.cwd(), filepath),
|
|
50
51
|
line: callsite.getLineNumber()
|
|
51
52
|
}
|
|
52
53
|
}
|
|
@@ -1,33 +1,6 @@
|
|
|
1
|
-
const { storage } = require('../../../../../datadog-core')
|
|
2
|
-
const iastContextFunctions = require('../iast-context')
|
|
3
|
-
const log = require('../../../log')
|
|
4
1
|
const TaintedUtils = require('@datadog/native-iast-taint-tracking')
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
function noop (res) { return res }
|
|
9
|
-
const TaintTrackingDummy = {
|
|
10
|
-
plusOperator: noop
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const TaintTracking = {
|
|
14
|
-
plusOperator: function (res, op1, op2) {
|
|
15
|
-
try {
|
|
16
|
-
if (typeof res !== 'string' ||
|
|
17
|
-
(typeof op1 !== 'string' && typeof op2 !== 'string')) { return res }
|
|
18
|
-
|
|
19
|
-
const store = storage.getStore()
|
|
20
|
-
const iastContext = iastContextFunctions.getIastContext(store)
|
|
21
|
-
const transactionId = iastContext && iastContext[IAST_TRANSACTION_ID]
|
|
22
|
-
if (transactionId) {
|
|
23
|
-
return TaintedUtils.concat(transactionId, res, op1, op2)
|
|
24
|
-
}
|
|
25
|
-
} catch (e) {
|
|
26
|
-
log.debug(e)
|
|
27
|
-
}
|
|
28
|
-
return res
|
|
29
|
-
}
|
|
30
|
-
}
|
|
2
|
+
const { IAST_TRANSACTION_ID } = require('../iast-context')
|
|
3
|
+
const { TaintTracking, TaintTrackingDummy } = require('./taint-tracking-impl')
|
|
31
4
|
|
|
32
5
|
function createTransaction (id, iastContext) {
|
|
33
6
|
if (id && iastContext) {
|