dd-trace 1.6.0-beta.0 → 2.0.0-appsec-beta.2

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.
Files changed (50) hide show
  1. package/LICENSE-3rdparty.csv +2 -0
  2. package/index.d.ts +0 -6
  3. package/package.json +7 -3
  4. package/packages/datadog-core/index.js +7 -0
  5. package/packages/datadog-core/src/storage/async_hooks.js +49 -0
  6. package/packages/datadog-core/src/storage/async_resource.js +76 -0
  7. package/packages/datadog-core/src/storage/index.js +14 -0
  8. package/packages/datadog-plugin-cucumber/src/index.js +2 -0
  9. package/packages/datadog-plugin-cypress/src/plugin.js +5 -3
  10. package/packages/datadog-plugin-http/src/server.js +1 -1
  11. package/packages/datadog-plugin-jest/src/jest-environment.js +5 -1
  12. package/packages/datadog-plugin-jest/src/jest-jasmine2.js +11 -2
  13. package/packages/datadog-plugin-mocha/src/index.js +3 -1
  14. package/packages/dd-trace/lib/version.js +1 -1
  15. package/packages/dd-trace/src/appsec/addresses.js +11 -0
  16. package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +115 -0
  17. package/packages/dd-trace/src/appsec/callbacks/index.js +7 -0
  18. package/packages/dd-trace/src/appsec/index.js +91 -2
  19. package/packages/dd-trace/src/appsec/recommended.json +1 -0
  20. package/packages/dd-trace/src/appsec/reporter.js +141 -0
  21. package/packages/dd-trace/src/appsec/rule_manager.js +27 -0
  22. package/packages/dd-trace/src/config.js +12 -7
  23. package/packages/dd-trace/src/gateway/als.js +5 -0
  24. package/packages/dd-trace/src/gateway/channels.js +8 -0
  25. package/packages/dd-trace/src/gateway/dc_block.js +68 -0
  26. package/packages/dd-trace/src/gateway/engine/engine.js +150 -0
  27. package/packages/dd-trace/src/gateway/engine/index.js +46 -0
  28. package/packages/dd-trace/src/gateway/engine/runner.js +40 -0
  29. package/packages/dd-trace/src/noop/scope.js +23 -0
  30. package/packages/dd-trace/src/noop/span.js +2 -0
  31. package/packages/dd-trace/src/noop/tracer.js +3 -10
  32. package/packages/dd-trace/src/opentracing/span.js +2 -32
  33. package/packages/dd-trace/src/plugins/util/ci-app-spec.json +1 -0
  34. package/packages/dd-trace/src/plugins/util/ci.js +36 -7
  35. package/packages/dd-trace/src/plugins/util/git.js +11 -25
  36. package/packages/dd-trace/src/plugins/util/test.js +9 -0
  37. package/packages/dd-trace/src/plugins/util/user-provided-git.js +57 -0
  38. package/packages/dd-trace/src/plugins/util/web.js +14 -1
  39. package/packages/dd-trace/src/scope.js +212 -18
  40. package/packages/dd-trace/src/startup-log.js +2 -1
  41. package/packages/dd-trace/src/tracer.js +4 -19
  42. package/scripts/version.js +8 -3
  43. package/packages/dd-trace/src/.DS_Store +0 -0
  44. package/packages/dd-trace/src/scope/async_hooks.js +0 -139
  45. package/packages/dd-trace/src/scope/async_local_storage.js +0 -29
  46. package/packages/dd-trace/src/scope/async_resource.js +0 -101
  47. package/packages/dd-trace/src/scope/base.js +0 -218
  48. package/packages/dd-trace/src/scope/scope_manager.js +0 -5
  49. package/packages/dd-trace/src/scope/sync.js +0 -29
  50. package/packages/dd-trace/src/scope/zone.js +0 -38
@@ -1,9 +1,11 @@
1
1
  Component,Origin,License,Copyright
2
+ require,@datadog/native-appsec,Apache license 2.0,Copyright 2018 Datadog Inc.
2
3
  require,@datadog/native-metrics,Apache license 2.0,Copyright 2018 Datadog Inc.
3
4
  require,@datadog/pprof,Apache license 2.0,Copyright 2019 Google Inc.
4
5
  require,@datadog/sketches-js,Apache license 2.0,Copyright 2020 Datadog Inc.
5
6
  require,@types/node,MIT,Copyright Authors
6
7
  require,crypto-randomuuid,MIT,Copyright 2021 Node.js Foundation and contributors
8
+ require,diagnostics_channel,MIT,Copyright 2021 Simon D.
7
9
  require,form-data,MIT,Copyright 2012 Felix Geisendörfer and contributors
8
10
  require,import-in-the-middle,Apache license 2.0,Copyright 2021 Datadog Inc.
9
11
  require,koalas,MIT,Copyright 2013-2017 Brian Woodward
package/index.d.ts CHANGED
@@ -285,12 +285,6 @@ export declare interface TracerOptions {
285
285
  */
286
286
  runtimeMetrics?: boolean
287
287
 
288
- /**
289
- * Whether to track the scope of async functions. This is needed for async/await to work with non-native promises (thenables). Only disable this if you are sure only native promises are used with async/await, or if you are using Node >=14.5 since the issue has been fixed in that version.
290
- * @default true
291
- */
292
- trackAsyncScope?: boolean
293
-
294
288
  /**
295
289
  * Custom function for DNS lookups when sending requests to the agent.
296
290
  * @default dns.lookup()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "1.6.0-beta.0",
3
+ "version": "2.0.0-appsec-beta.2",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -16,8 +16,10 @@
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 --exit --expose-gc 'packages/dd-trace/test/setup/node.js' 'packages/*/test/**/*.spec.js'",
19
- "test:core": "mocha --exit --expose-gc --file packages/dd-trace/test/setup/core.js \"packages/dd-trace/test/**/*.spec.js\"",
20
- "test:core:ci": "nyc --include \"packages/dd-trace/src/**/*.js\" -- npm run test:core -- --reporter mocha-multi-reporters --reporter-options configFile=mocha-reporter-config.json",
19
+ "test:trace:core": "mocha --exit --expose-gc --file packages/dd-trace/test/setup/core.js \"packages/dd-trace/test/**/*.spec.js\"",
20
+ "test:trace:core:ci": "nyc --include \"packages/dd-trace/src/**/*.js\" -- npm run test:trace:core -- --reporter mocha-multi-reporters --reporter-options configFile=mocha-reporter-config.json",
21
+ "test:core": "mocha --file packages/datadog-core/test/setup.js 'packages/datadog-core/test/**/*.spec.js'",
22
+ "test:core:ci": "nyc --include 'packages/datadog-core/src/**/*.js' -- npm run test:core -- --reporter mocha-multi-reporters --reporter-options configFile=mocha-reporter-config.json",
21
23
  "test:plugins": "mocha --exit --file \"packages/dd-trace/test/setup/core.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/**/*.spec.js\"",
22
24
  "test:plugins:ci": "yarn services && nyc --include \"packages/datadog-plugin-@($(echo $PLUGINS))/src/**/*.js\" -- npm run test:plugins -- --reporter mocha-multi-reporters --reporter-options configFile=mocha-reporter-config.json",
23
25
  "test:plugins:upstream": "node ./packages/dd-trace/test/plugins/suite.js",
@@ -56,11 +58,13 @@
56
58
  "node": ">=12"
57
59
  },
58
60
  "dependencies": {
61
+ "@datadog/native-appsec": "^0.6.1",
59
62
  "@datadog/native-metrics": "^1.0.1",
60
63
  "@datadog/pprof": "^0.3.0",
61
64
  "@datadog/sketches-js": "^1.0.4",
62
65
  "@types/node": "^10.12.18",
63
66
  "crypto-randomuuid": "^1.0.0",
67
+ "diagnostics_channel": "^1.1.0",
64
68
  "form-data": "^3.0.0",
65
69
  "import-in-the-middle": "^1.1.2",
66
70
  "koalas": "^1.0.2",
@@ -0,0 +1,7 @@
1
+ 'use strict'
2
+
3
+ const LocalStorage = require('./src/storage')
4
+
5
+ const storage = new LocalStorage()
6
+
7
+ module.exports = { storage }
@@ -0,0 +1,49 @@
1
+ 'use strict'
2
+
3
+ const { createHook, executionAsyncId } = require('async_hooks')
4
+ const AsyncResourceStorage = require('./async_resource')
5
+
6
+ class AsyncHooksStorage extends AsyncResourceStorage {
7
+ constructor () {
8
+ super()
9
+
10
+ this._resources = new Map()
11
+ }
12
+
13
+ disable () {
14
+ super.disable()
15
+
16
+ this._resources.clear()
17
+ }
18
+
19
+ _createHook () {
20
+ return createHook({
21
+ init: this._init.bind(this),
22
+ destroy: this._destroy.bind(this)
23
+ })
24
+ }
25
+
26
+ _init (asyncId, type, triggerAsyncId, resource) {
27
+ super._init.apply(this, arguments)
28
+
29
+ this._resources.set(asyncId, resource)
30
+ }
31
+
32
+ _destroy (asyncId) {
33
+ this._resources.delete(asyncId)
34
+ }
35
+
36
+ _executionAsyncResource () {
37
+ const asyncId = executionAsyncId()
38
+
39
+ let resource = this._resources.get(asyncId)
40
+
41
+ if (!resource) {
42
+ this._resources.set(asyncId, resource = {})
43
+ }
44
+
45
+ return resource
46
+ }
47
+ }
48
+
49
+ module.exports = AsyncHooksStorage
@@ -0,0 +1,76 @@
1
+ 'use strict'
2
+
3
+ const { createHook, executionAsyncResource } = require('async_hooks')
4
+
5
+ class AsyncResourceStorage {
6
+ constructor () {
7
+ this._ddResourceStore = Symbol('ddResourceStore')
8
+ this._enabled = false
9
+ this._hook = this._createHook()
10
+ }
11
+
12
+ disable () {
13
+ if (!this._enabled) return
14
+
15
+ this._hook.disable()
16
+ this._enabled = false
17
+ }
18
+
19
+ getStore () {
20
+ if (!this._enabled) return
21
+
22
+ const resource = this._executionAsyncResource()
23
+
24
+ return resource[this._ddResourceStore]
25
+ }
26
+
27
+ enterWith (store) {
28
+ this._enable()
29
+
30
+ const resource = this._executionAsyncResource()
31
+
32
+ resource[this._ddResourceStore] = store
33
+ }
34
+
35
+ run (store, callback, ...args) {
36
+ this._enable()
37
+
38
+ const resource = this._executionAsyncResource()
39
+ const oldStore = resource[this._ddResourceStore]
40
+
41
+ resource[this._ddResourceStore] = store
42
+
43
+ try {
44
+ return callback(...args)
45
+ } finally {
46
+ resource[this._ddResourceStore] = oldStore
47
+ }
48
+ }
49
+
50
+ _createHook () {
51
+ return createHook({
52
+ init: this._init.bind(this)
53
+ })
54
+ }
55
+
56
+ _enable () {
57
+ if (this._enabled) return
58
+
59
+ this._enabled = true
60
+ this._hook.enable()
61
+ }
62
+
63
+ _init (asyncId, type, triggerAsyncId, resource) {
64
+ const currentResource = this._executionAsyncResource()
65
+
66
+ if (Object.prototype.hasOwnProperty.call(currentResource, this._ddResourceStore)) {
67
+ resource[this._ddResourceStore] = currentResource[this._ddResourceStore]
68
+ }
69
+ }
70
+
71
+ _executionAsyncResource () {
72
+ return executionAsyncResource()
73
+ }
74
+ }
75
+
76
+ module.exports = AsyncResourceStorage
@@ -0,0 +1,14 @@
1
+ 'use strict'
2
+
3
+ // TODO: default to AsyncLocalStorage when it supports triggerAsyncResource
4
+
5
+ const semver = require('semver')
6
+
7
+ // https://github.com/nodejs/node/pull/33801
8
+ const hasJavaScriptAsyncHooks = semver.satisfies(process.versions.node, '>=14.5 || ^12.19.0')
9
+
10
+ if (hasJavaScriptAsyncHooks) {
11
+ module.exports = require('./async_resource')
12
+ } else {
13
+ module.exports = require('./async_hooks')
14
+ }
@@ -5,6 +5,7 @@ const {
5
5
  TEST_NAME,
6
6
  TEST_SUITE,
7
7
  TEST_STATUS,
8
+ TEST_FRAMEWORK_VERSION,
8
9
  TEST_SKIP_REASON,
9
10
  CI_APP_ORIGIN,
10
11
  ERROR_MESSAGE,
@@ -52,6 +53,7 @@ function createWrapRun (tracer, testEnvironmentMetadata, sourceRoot, setStatus)
52
53
  [TEST_NAME]: testName,
53
54
  [TEST_SUITE]: testSuite,
54
55
  [SAMPLING_RULE_DECISION]: 1,
56
+ [TEST_FRAMEWORK_VERSION]: tracer._version,
55
57
  ...testEnvironmentMetadata
56
58
  }
57
59
 
@@ -3,6 +3,7 @@ const {
3
3
  TEST_NAME,
4
4
  TEST_SUITE,
5
5
  TEST_STATUS,
6
+ TEST_FRAMEWORK_VERSION,
6
7
  getTestEnvironmentMetadata,
7
8
  CI_APP_ORIGIN,
8
9
  getTestParentSpan
@@ -19,7 +20,7 @@ const CYPRESS_STATUS_TO_TEST_STATUS = {
19
20
  skipped: 'skip'
20
21
  }
21
22
 
22
- function getTestSpanMetadata (tracer, testName, testSuite) {
23
+ function getTestSpanMetadata (tracer, testName, testSuite, cypressConfig) {
23
24
  const childOf = getTestParentSpan(tracer)
24
25
 
25
26
  return {
@@ -29,7 +30,8 @@ function getTestSpanMetadata (tracer, testName, testSuite) {
29
30
  [TEST_NAME]: testName,
30
31
  [TEST_SUITE]: testSuite,
31
32
  [SAMPLING_RULE_DECISION]: 1,
32
- [SAMPLING_PRIORITY]: AUTO_KEEP
33
+ [SAMPLING_PRIORITY]: AUTO_KEEP,
34
+ [TEST_FRAMEWORK_VERSION]: cypressConfig.version
33
35
  }
34
36
  }
35
37
 
@@ -50,7 +52,7 @@ module.exports = (on, config) => {
50
52
  childOf,
51
53
  resource,
52
54
  ...testSpanMetadata
53
- } = getTestSpanMetadata(tracer, testName, testSuite)
55
+ } = getTestSpanMetadata(tracer, testName, testSuite, config)
54
56
 
55
57
  if (!activeSpan) {
56
58
  activeSpan = tracer.startSpan('cypress.test', {
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const web = require('../../dd-trace/src/plugins/util/web')
4
- const Scope = require('../../dd-trace/src/scope/base')
4
+ const Scope = require('../../dd-trace/src/scope')
5
5
 
6
6
  function createWrapEmit (tracer, config) {
7
7
  config = web.normalizeConfig(config)
@@ -5,6 +5,8 @@ const {
5
5
  TEST_NAME,
6
6
  TEST_SUITE,
7
7
  TEST_STATUS,
8
+ TEST_FRAMEWORK_VERSION,
9
+ JEST_TEST_RUNNER,
8
10
  ERROR_MESSAGE,
9
11
  ERROR_TYPE,
10
12
  TEST_PARAMETERS,
@@ -138,7 +140,9 @@ function createHandleTestEvent (tracer, testEnvironmentMetadata, instrumenter) {
138
140
  const spanTags = {
139
141
  ...commonSpanTags,
140
142
  [TEST_NAME]: testName,
141
- [TEST_SUITE]: this.testSuite
143
+ [TEST_SUITE]: this.testSuite,
144
+ [TEST_FRAMEWORK_VERSION]: tracer._version,
145
+ [JEST_TEST_RUNNER]: 'jest-circus'
142
146
  }
143
147
 
144
148
  const testParametersString = getTestParametersString(nameToParams, event.test.name)
@@ -5,6 +5,8 @@ const {
5
5
  TEST_NAME,
6
6
  TEST_SUITE,
7
7
  TEST_STATUS,
8
+ TEST_FRAMEWORK_VERSION,
9
+ JEST_TEST_RUNNER,
8
10
  CI_APP_ORIGIN,
9
11
  getTestEnvironmentMetadata,
10
12
  finishAllTraceSpans,
@@ -29,7 +31,12 @@ function createWrapIt (tracer, globalConfig, globalInput, testEnvironmentMetadat
29
31
  {
30
32
  type: 'test',
31
33
  childOf,
32
- tags: { ...commonSpanTags, [TEST_SUITE]: testSuite }
34
+ tags: {
35
+ ...commonSpanTags,
36
+ [TEST_SUITE]: testSuite,
37
+ [TEST_FRAMEWORK_VERSION]: tracer._version,
38
+ [JEST_TEST_RUNNER]: 'jest-jasmine2'
39
+ }
33
40
  },
34
41
  async (done) => {
35
42
  const testSpan = tracer.scope().active()
@@ -124,7 +131,9 @@ function createWrapItSkip (tracer, globalConfig, globalInput, testEnvironmentMet
124
131
  [RESOURCE_NAME]: resource,
125
132
  [TEST_NAME]: testName,
126
133
  [TEST_SUITE]: testSuite,
127
- [TEST_STATUS]: 'skip'
134
+ [TEST_STATUS]: 'skip',
135
+ [TEST_FRAMEWORK_VERSION]: tracer._version,
136
+ [JEST_TEST_RUNNER]: 'jest-jasmine2'
128
137
  }
129
138
  }
130
139
  )
@@ -9,6 +9,7 @@ const {
9
9
  TEST_SUITE,
10
10
  TEST_STATUS,
11
11
  TEST_PARAMETERS,
12
+ TEST_FRAMEWORK_VERSION,
12
13
  CI_APP_ORIGIN,
13
14
  getTestEnvironmentMetadata,
14
15
  getTestParametersString,
@@ -31,7 +32,8 @@ function getTestSpanMetadata (tracer, test, sourceRoot) {
31
32
  [TEST_NAME]: fullTestName,
32
33
  [TEST_SUITE]: testSuite,
33
34
  [SAMPLING_RULE_DECISION]: 1,
34
- [SAMPLING_PRIORITY]: AUTO_KEEP
35
+ [SAMPLING_PRIORITY]: AUTO_KEEP,
36
+ [TEST_FRAMEWORK_VERSION]: tracer._version
35
37
  }
36
38
  }
37
39
 
@@ -1 +1 @@
1
- module.exports = '1.6.0-beta.0'
1
+ module.exports = '2.0.0-pre'
@@ -0,0 +1,11 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ HTTP_INCOMING_URL: 'server.request.uri.raw',
5
+ HTTP_INCOMING_HEADERS: 'server.request.headers.no_cookies',
6
+ HTTP_INCOMING_METHOD: 'server.request.method',
7
+ HTTP_INCOMING_REMOTE_IP: 'server.request.client_ip',
8
+ HTTP_INCOMING_REMOTE_PORT: 'server.request.client_port',
9
+ HTTP_INCOMING_RESPONSE_CODE: 'server.response.status',
10
+ HTTP_INCOMING_RESPONSE_HEADERS: 'server.response.headers.no_cookies'
11
+ }
@@ -0,0 +1,115 @@
1
+ 'use strict'
2
+
3
+ const log = require('../../log')
4
+ const addresses = require('../addresses')
5
+ const Gateway = require('../../gateway/engine')
6
+ const Reporter = require('../reporter')
7
+
8
+ let warned = false
9
+
10
+ const validAddressSet = new Set(Object.values(addresses))
11
+
12
+ const DEFAULT_MAX_BUDGET = 5e3 // µs
13
+
14
+ // TODO: put reusable code in a base class
15
+ class WAFCallback {
16
+ static loadDDWAF (rules) {
17
+ try {
18
+ // require in `try/catch` because this can throw at require time
19
+ const { DDWAF } = require('@datadog/native-appsec')
20
+
21
+ return new DDWAF(rules)
22
+ } catch (err) {
23
+ if (!warned) {
24
+ log.warn('AppSec could not load native package. In-app WAF features will not be available.')
25
+ warned = true
26
+ }
27
+
28
+ throw err
29
+ }
30
+ }
31
+
32
+ constructor (rules) {
33
+ this.ddwaf = WAFCallback.loadDDWAF(rules)
34
+ this.wafContextCache = new WeakMap()
35
+
36
+ // closures are faster than binds
37
+ const self = this
38
+ const method = (params, store) => {
39
+ return self.action(params, store)
40
+ }
41
+
42
+ // might be its own class with more info later
43
+ const callback = { method }
44
+
45
+ const subscribedAddresses = new Set()
46
+
47
+ for (const rule of rules.rules) {
48
+ for (const condition of rule.conditions) {
49
+ for (const input of condition.parameters.inputs) {
50
+ const address = input.address.split(':', 2)[0]
51
+
52
+ if (!validAddressSet.has(address) || subscribedAddresses.has(address)) continue
53
+
54
+ subscribedAddresses.add(address)
55
+
56
+ Gateway.manager.addSubscription({ addresses: [ address ], callback })
57
+ }
58
+ }
59
+ }
60
+ }
61
+
62
+ action (params, store) {
63
+ let wafContext
64
+
65
+ if (store) {
66
+ const key = store.get('context')
67
+
68
+ if (key) {
69
+ if (this.wafContextCache.has(key)) {
70
+ wafContext = this.wafContextCache.get(key)
71
+ } else {
72
+ wafContext = this.ddwaf.createContext()
73
+ this.wafContextCache.set(key, wafContext)
74
+ }
75
+ }
76
+ }
77
+
78
+ if (!wafContext) {
79
+ wafContext = this.ddwaf.createContext()
80
+ }
81
+
82
+ // cast status code to string
83
+ if (params[addresses.HTTP_INCOMING_RESPONSE_CODE]) {
84
+ params[addresses.HTTP_INCOMING_RESPONSE_CODE] = params[addresses.HTTP_INCOMING_RESPONSE_CODE] + ''
85
+ }
86
+
87
+ try {
88
+ // TODO: possible optimizaion: only send params that haven't already been sent to this wafContext
89
+ const result = wafContext.run(params, DEFAULT_MAX_BUDGET)
90
+
91
+ return this.applyResult(result, store)
92
+ } catch (err) {
93
+ log.warn('Error while running the AppSec WAF')
94
+ }
95
+ }
96
+
97
+ applyResult (result, store) {
98
+ if (result.data && result.data !== '[]') {
99
+ Reporter.reportAttack(result.data, store)
100
+ }
101
+
102
+ // result.perfData
103
+ // result.perfTotalRuntime
104
+ }
105
+
106
+ clear () {
107
+ this.ddwaf.dispose()
108
+
109
+ this.wafContextCache = new WeakMap()
110
+
111
+ Gateway.manager.clear()
112
+ }
113
+ }
114
+
115
+ module.exports = WAFCallback
@@ -0,0 +1,7 @@
1
+ 'use strict'
2
+
3
+ // lazy loading
4
+ // TODO: cache the returned value
5
+ module.exports = {
6
+ get DDWAF () { return require('./ddwaf') }
7
+ }
@@ -1,8 +1,97 @@
1
1
  'use strict'
2
2
 
3
- function enable () {
3
+ const fs = require('fs')
4
+ const log = require('../log')
5
+ const RuleManager = require('./rule_manager')
6
+ const { INCOMING_HTTP_REQUEST_START, INCOMING_HTTP_REQUEST_END } = require('../gateway/channels')
7
+ const Gateway = require('../gateway/engine/index')
8
+ const addresses = require('./addresses')
9
+ const Reporter = require('./reporter')
10
+
11
+ function enable (config) {
12
+ try {
13
+ // TODO: enable dc_blocking: config.appsec.blocking === true
14
+
15
+ let rules = fs.readFileSync(config.appsec.rules)
16
+ rules = JSON.parse(rules)
17
+
18
+ RuleManager.applyRules(rules)
19
+ } catch (err) {
20
+ log.error(`Unable to apply AppSec rules: ${err}`)
21
+
22
+ // abort AppSec start
23
+ RuleManager.clearAllRules()
24
+ return
25
+ }
26
+
27
+ INCOMING_HTTP_REQUEST_START.subscribe(incomingHttpStartTranslator)
28
+ INCOMING_HTTP_REQUEST_END.subscribe(incomingHttpEndTranslator)
29
+
30
+ config.tags['_dd.appsec.enabled'] = 1
31
+ config.tags['_dd.runtime_family'] = 'nodejs'
32
+
33
+ // add needed fields for HTTP context reporting
34
+ Gateway.manager.addresses.add(addresses.HTTP_INCOMING_URL)
35
+ Gateway.manager.addresses.add(addresses.HTTP_INCOMING_HEADERS)
36
+ Gateway.manager.addresses.add(addresses.HTTP_INCOMING_METHOD)
37
+ Gateway.manager.addresses.add(addresses.HTTP_INCOMING_REMOTE_IP)
38
+ Gateway.manager.addresses.add(addresses.HTTP_INCOMING_REMOTE_PORT)
39
+ Gateway.manager.addresses.add(addresses.HTTP_INCOMING_RESPONSE_CODE)
40
+ Gateway.manager.addresses.add(addresses.HTTP_INCOMING_RESPONSE_HEADERS)
41
+ }
42
+
43
+ function incomingHttpStartTranslator (data) {
44
+ const store = Gateway.startContext()
45
+
46
+ store.set('req', data.req)
47
+ store.set('res', data.res)
48
+
49
+ const headers = Object.assign({}, data.req.headers)
50
+ delete headers.cookie
51
+
52
+ const context = store.get('context')
53
+
54
+ Gateway.propagate({
55
+ [addresses.HTTP_INCOMING_URL]: data.req.url,
56
+ [addresses.HTTP_INCOMING_HEADERS]: headers,
57
+ [addresses.HTTP_INCOMING_METHOD]: data.req.method,
58
+ [addresses.HTTP_INCOMING_REMOTE_IP]: data.req.socket.remoteAddress,
59
+ [addresses.HTTP_INCOMING_REMOTE_PORT]: data.req.socket.remotePort
60
+ }, context)
61
+ }
62
+
63
+ function incomingHttpEndTranslator (data) {
64
+ const context = Gateway.getContext()
65
+
66
+ if (!context) return
67
+
68
+ // TODO: this doesn't support headers sent with res.writeHead()
69
+ const headers = Object.assign({}, data.res.getHeaders())
70
+ delete headers['set-cookie']
71
+
72
+ Gateway.propagate({
73
+ [addresses.HTTP_INCOMING_RESPONSE_CODE]: data.res.statusCode,
74
+ [addresses.HTTP_INCOMING_RESPONSE_HEADERS]: headers
75
+ }, context)
76
+
77
+ Reporter.finishAttacks(data.req, context)
78
+ }
79
+
80
+ function disable () {
81
+ RuleManager.clearAllRules()
82
+
83
+ if (INCOMING_HTTP_REQUEST_START.hasSubscribers) INCOMING_HTTP_REQUEST_START.unsubscribe(incomingHttpStartTranslator)
84
+ if (INCOMING_HTTP_REQUEST_END.hasSubscribers) INCOMING_HTTP_REQUEST_END.unsubscribe(incomingHttpEndTranslator)
85
+
86
+ const tags = global._ddtrace._tracer._tags
87
+
88
+ delete tags['_dd.appsec.enabled']
89
+ delete tags['_dd.runtime_family']
4
90
  }
5
91
 
6
92
  module.exports = {
7
- enable
93
+ enable,
94
+ disable,
95
+ incomingHttpStartTranslator,
96
+ incomingHttpEndTranslator
8
97
  }