dd-trace 3.1.0 → 3.2.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.
Files changed (43) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/ext/tags.d.ts +2 -1
  3. package/ext/tags.js +2 -1
  4. package/index.d.ts +43 -20
  5. package/package.json +4 -3
  6. package/packages/datadog-instrumentations/src/crypto.js +30 -0
  7. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  8. package/packages/datadog-instrumentations/src/http/server.js +1 -1
  9. package/packages/datadog-instrumentations/src/net.js +13 -0
  10. package/packages/datadog-plugin-mongodb-core/src/index.js +19 -10
  11. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +3 -0
  12. package/packages/dd-trace/src/appsec/iast/analyzers/index.js +20 -0
  13. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +48 -0
  14. package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +24 -0
  15. package/packages/dd-trace/src/appsec/iast/iast-context.js +50 -0
  16. package/packages/dd-trace/src/appsec/iast/index.js +59 -0
  17. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +94 -0
  18. package/packages/dd-trace/src/appsec/iast/path-line.js +70 -0
  19. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +113 -0
  20. package/packages/dd-trace/src/config.js +76 -10
  21. package/packages/dd-trace/src/constants.js +9 -1
  22. package/packages/dd-trace/src/encode/span-stats.js +155 -0
  23. package/packages/dd-trace/src/exporters/agent/index.js +14 -2
  24. package/packages/dd-trace/src/exporters/agent/writer.js +6 -3
  25. package/packages/dd-trace/src/exporters/common/request.js +6 -2
  26. package/packages/dd-trace/src/exporters/span-stats/index.js +20 -0
  27. package/packages/dd-trace/src/exporters/span-stats/writer.js +54 -0
  28. package/packages/dd-trace/src/format.js +2 -0
  29. package/packages/dd-trace/src/iitm.js +11 -0
  30. package/packages/dd-trace/src/opentracing/propagation/text_map.js +71 -0
  31. package/packages/dd-trace/src/opentracing/tracer.js +1 -1
  32. package/packages/dd-trace/src/plugin_manager.js +12 -2
  33. package/packages/dd-trace/src/plugins/log_plugin.js +16 -9
  34. package/packages/dd-trace/src/plugins/util/ip_blocklist.js +25 -0
  35. package/packages/dd-trace/src/plugins/util/web.js +100 -2
  36. package/packages/dd-trace/src/priority_sampler.js +36 -1
  37. package/packages/dd-trace/src/proxy.js +3 -0
  38. package/packages/dd-trace/src/ritm.js +10 -1
  39. package/packages/dd-trace/src/span_processor.js +7 -1
  40. package/packages/dd-trace/src/span_stats.js +210 -0
  41. package/packages/dd-trace/src/telemetry/dependencies.js +83 -0
  42. package/packages/dd-trace/src/{telemetry.js → telemetry/index.js} +10 -65
  43. package/packages/dd-trace/src/telemetry/send-data.js +35 -0
@@ -3,6 +3,7 @@ require,@datadog/native-appsec,Apache license 2.0,Copyright 2018 Datadog Inc.
3
3
  require,@datadog/native-metrics,Apache license 2.0,Copyright 2018 Datadog Inc.
4
4
  require,@datadog/pprof,Apache license 2.0,Copyright 2019 Google Inc.
5
5
  require,@datadog/sketches-js,Apache license 2.0,Copyright 2020 Datadog Inc.
6
+ require,cidr-matcher,MIT,Copyright 2015 Marco Pracucci
6
7
  require,crypto-randomuuid,MIT,Copyright 2021 Node.js Foundation and contributors
7
8
  require,diagnostics_channel,MIT,Copyright 2021 Simon D.
8
9
  require,ignore,MIT,Copyright 2013 Kael Zhang and contributors
package/ext/tags.d.ts CHANGED
@@ -15,7 +15,8 @@ declare const tags: {
15
15
  HTTP_ROUTE: 'http.route'
16
16
  HTTP_REQUEST_HEADERS: 'http.request.headers'
17
17
  HTTP_RESPONSE_HEADERS: 'http.response.headers'
18
- HTTP_USERAGENT: 'http.useragent'
18
+ HTTP_USERAGENT: 'http.useragent',
19
+ HTTP_CLIENT_IP: 'http.client_ip'
19
20
  }
20
21
 
21
22
  export = tags
package/ext/tags.js CHANGED
@@ -20,7 +20,8 @@ const tags = {
20
20
  HTTP_ROUTE: 'http.route',
21
21
  HTTP_REQUEST_HEADERS: 'http.request.headers',
22
22
  HTTP_RESPONSE_HEADERS: 'http.response.headers',
23
- HTTP_USERAGENT: 'http.useragent'
23
+ HTTP_USERAGENT: 'http.useragent',
24
+ HTTP_CLIENT_IP: 'http.client_ip'
24
25
  }
25
26
 
26
27
  // Deprecated
package/index.d.ts CHANGED
@@ -267,6 +267,22 @@ export declare interface TracerOptions {
267
267
  */
268
268
  sampleRate?: number;
269
269
 
270
+ /**
271
+ * Global rate limit that is applied on the global sample rate and all rules,
272
+ * and controls the ingestion rate limit between the agent and the backend.
273
+ * Defaults to deferring the decision to the agent.
274
+ */
275
+ rateLimit?: Number,
276
+
277
+ /**
278
+ * Sampling rules to apply to priority samplin. Each rule is a JSON,
279
+ * consisting of `service` and `name`, which are regexes to match against
280
+ * a trace's `service` and `name`, and a corresponding `sampleRate`. If not
281
+ * specified, will defer to global sampling rate for all spans.
282
+ * @default []
283
+ */
284
+ samplingRules?: SamplingRule[]
285
+
270
286
  /**
271
287
  * Interval in milliseconds at which the tracer will submit traces to the agent.
272
288
  * @default 2000
@@ -298,7 +314,10 @@ export declare interface TracerOptions {
298
314
  protocolVersion?: string
299
315
 
300
316
  /**
301
- * Configuration of the ingestion between the agent and the backend.
317
+ * Deprecated in favor of the global versions of the variables provided under this option
318
+ *
319
+ * @deprecated
320
+ * @hidden
302
321
  */
303
322
  ingestion?: {
304
323
  /**
@@ -307,7 +326,7 @@ export declare interface TracerOptions {
307
326
  sampleRate?: number
308
327
 
309
328
  /**
310
- * Controls the ingestion rate limit between the agent and the backend.
329
+ * Controls the ingestion rate limit between the agent and the backend. Defaults to deferring the decision to the agent.
311
330
  */
312
331
  rateLimit?: number
313
332
  };
@@ -333,32 +352,36 @@ export declare interface TracerOptions {
333
352
  exporter?: 'log' | 'agent'
334
353
 
335
354
  /**
336
- * Configuration of the priority sampler. Supports a global config and rules by span name or service name. The first matching rule is applied, and if no rule matches it falls back to the global config or on the rates provided by the agent if there is no global config.
355
+ * Whether to enable the experimental `getRumData` method.
356
+ * @default false
337
357
  */
338
- sampler?: {
358
+ enableGetRumData?: boolean
359
+
360
+ /**
361
+ * Configuration of the IAST. Can be a boolean as an alias to `iast.enabled`.
362
+ */
363
+ iast?: boolean | {
339
364
  /**
340
- * Sample rate to apply globally when no other rule is matched. Omit to fallback on the dynamic rates returned by the agent instead.
365
+ * Whether to enable IAST.
366
+ * @default false
341
367
  */
342
- sampleRate?: Number,
343
-
368
+ enabled?: boolean,
344
369
  /**
345
- * Global rate limit that is applied on the global sample rate and all rules.
346
- * @default 100
370
+ * Controls the percentage of requests that iast will analyze
371
+ * @default 30
347
372
  */
348
- rateLimit?: Number,
349
-
373
+ requestSampling?: number,
350
374
  /**
351
- * Sampling rules to apply to priority sampling.
352
- * @default []
375
+ * Controls how many request can be analyzing code vulnerabilities at the same time
376
+ * @default 2
353
377
  */
354
- rules?: SamplingRule[]
378
+ maxConcurrentRequests?: number,
379
+ /**
380
+ * Controls how many code vulnerabilities can be detected in the same request
381
+ * @default 2
382
+ */
383
+ maxContextOperations?: number
355
384
  }
356
-
357
- /**
358
- * Whether to enable the experimental `getRumData` method.
359
- * @default false
360
- */
361
- enableGetRumData?: boolean
362
385
  };
363
386
 
364
387
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -61,11 +61,12 @@
61
61
  "@datadog/native-appsec": "^1.2.1",
62
62
  "@datadog/native-metrics": "^1.4.2",
63
63
  "@datadog/pprof": "^1.0.2",
64
- "@datadog/sketches-js": "^2.0.0",
64
+ "@datadog/sketches-js": "^2.1.0",
65
+ "cidr-matcher": "^2.1.1",
65
66
  "crypto-randomuuid": "^1.0.0",
66
67
  "diagnostics_channel": "^1.1.0",
67
68
  "ignore": "^5.2.0",
68
- "import-in-the-middle": "^1.3.0",
69
+ "import-in-the-middle": "^1.3.1",
69
70
  "istanbul-lib-coverage": "3.2.0",
70
71
  "koalas": "^1.0.2",
71
72
  "limiter": "^1.1.4",
@@ -0,0 +1,30 @@
1
+ 'use strict'
2
+
3
+ const {
4
+ channel,
5
+ addHook
6
+ } = require('./helpers/instrument')
7
+ const shimmer = require('../../datadog-shimmer')
8
+
9
+ const cryptoCh = channel('datadog:crypto:hashing:start')
10
+
11
+ addHook({ name: 'crypto' }, crypto => {
12
+ shimmer.massWrap(
13
+ crypto,
14
+ ['createHash', 'createHmac', 'createSign', 'createVerify', 'sign', 'verify'],
15
+ wrapMethod
16
+ )
17
+ return crypto
18
+ })
19
+
20
+ function wrapMethod (cryptoMethod) {
21
+ return function () {
22
+ if (cryptoCh.hasSubscribers) {
23
+ if (arguments.length > 0) {
24
+ const algorithm = arguments[0]
25
+ cryptoCh.publish({ algorithm })
26
+ }
27
+ }
28
+ return cryptoMethod.apply(this, arguments)
29
+ }
30
+ }
@@ -17,6 +17,7 @@ module.exports = {
17
17
  'cassandra-driver': () => require('../cassandra-driver'),
18
18
  'connect': () => require('../connect'),
19
19
  'couchbase': () => require('../couchbase'),
20
+ 'crypto': () => require('../crypto'),
20
21
  'cypress': () => require('../cypress'),
21
22
  'dns': () => require('../dns'),
22
23
  'elasticsearch': () => require('../elasticsearch'),
@@ -29,7 +29,7 @@ function wrapResponseEmit (emit) {
29
29
  return emit.apply(this, arguments)
30
30
  }
31
31
 
32
- if (eventName === 'finish') {
32
+ if (eventName === 'close') {
33
33
  finishServerCh.publish({ req: this.req })
34
34
  }
35
35
 
@@ -49,6 +49,19 @@ addHook({ name: 'net' }, net => {
49
49
  setupListeners(this, 'tcp', asyncResource)
50
50
  }
51
51
 
52
+ const emit = this.emit
53
+ this.emit = function (eventName) {
54
+ switch (eventName) {
55
+ case 'ready':
56
+ case 'connect':
57
+ return callbackResource.runInAsyncScope(() => {
58
+ return emit.apply(this, arguments)
59
+ })
60
+ default:
61
+ return emit.apply(this, arguments)
62
+ }
63
+ }
64
+
52
65
  try {
53
66
  return connect.apply(this, arguments)
54
67
  } catch (err) {
@@ -73,21 +73,21 @@ function truncate (input) {
73
73
  return input.slice(0, Math.min(input.length, 10000))
74
74
  }
75
75
 
76
- function simplify (input) {
77
- return isBSON(input) ? input.toHexString() : input
78
- }
79
-
80
76
  function shouldSimplify (input) {
81
- return !isObject(input) || isBSON(input)
77
+ return !isObject(input)
82
78
  }
83
79
 
84
80
  function shouldHide (input) {
85
- return Buffer.isBuffer(input) || typeof input === 'function'
81
+ return Buffer.isBuffer(input) || typeof input === 'function' || isBinary(input)
86
82
  }
87
83
 
88
84
  function limitDepth (input) {
85
+ if (isBSON(input)) {
86
+ input = input.toJSON()
87
+ }
88
+
89
89
  if (shouldHide(input)) return '?'
90
- if (shouldSimplify(input)) return simplify(input)
90
+ if (shouldSimplify(input)) return input
91
91
 
92
92
  const output = {}
93
93
  const queue = [{
@@ -104,11 +104,16 @@ function limitDepth (input) {
104
104
  for (const key in input) {
105
105
  if (typeof input[key] === 'function') continue
106
106
 
107
- const child = input[key]
107
+ let child = input[key]
108
+
109
+ if (isBSON(child)) {
110
+ child = child.toJSON()
111
+ }
112
+
108
113
  if (depth >= 10 || shouldHide(child)) {
109
114
  output[key] = '?'
110
115
  } else if (shouldSimplify(child)) {
111
- output[key] = simplify(child)
116
+ output[key] = child
112
117
  } else {
113
118
  queue.push({
114
119
  input: child,
@@ -127,7 +132,11 @@ function isObject (val) {
127
132
  }
128
133
 
129
134
  function isBSON (val) {
130
- return val && val._bsontype
135
+ return val && val._bsontype && !isBinary(val)
136
+ }
137
+
138
+ function isBinary (val) {
139
+ return val && val._bsontype === 'Binary'
131
140
  }
132
141
 
133
142
  module.exports = MongodbCorePlugin
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ 'WEAK_HASH_ANALYZER': require('./weak-hash-analyzer')
3
+ }
@@ -0,0 +1,20 @@
1
+ 'use strict'
2
+
3
+ const analyzers = require('./analyzers')
4
+
5
+ function enableAllAnalyzers () {
6
+ for (const analyzer in analyzers) {
7
+ analyzers[analyzer].configure(true)
8
+ }
9
+ }
10
+
11
+ function disableAllAnalyzers () {
12
+ for (const analyzer in analyzers) {
13
+ analyzers[analyzer].configure(false)
14
+ }
15
+ }
16
+
17
+ module.exports = {
18
+ enableAllAnalyzers,
19
+ disableAllAnalyzers
20
+ }
@@ -0,0 +1,48 @@
1
+ 'use strict'
2
+
3
+ const Plugin = require('../../../../src/plugins/plugin')
4
+ const { storage } = require('../../../../../datadog-core')
5
+ const { getFirstNonDDPathAndLine } = require('./../path-line')
6
+ const { createVulnerability, addVulnerability } = require('../vulnerability-reporter')
7
+ const { getIastContext } = require('../iast-context')
8
+ const overheadController = require('../overhead-controller')
9
+
10
+ class Analyzer extends Plugin {
11
+ constructor (type) {
12
+ super()
13
+ this._type = type
14
+ }
15
+
16
+ _isVulnerable (value, context) {
17
+ return false
18
+ }
19
+
20
+ _report (value, context) {
21
+ const evidence = this._getEvidence(value)
22
+ const location = this._getLocation()
23
+ const spanId = context && context.rootSpan && context.rootSpan.context().toSpanId()
24
+ const vulnerability = createVulnerability(this._type, evidence, spanId, location)
25
+ addVulnerability(context, vulnerability)
26
+ }
27
+
28
+ _getEvidence (value) {
29
+ return { value }
30
+ }
31
+
32
+ _getLocation () {
33
+ return getFirstNonDDPathAndLine()
34
+ }
35
+
36
+ analyze (value) {
37
+ const iastContext = getIastContext(storage.getStore())
38
+ if (iastContext && this._isVulnerable(value, iastContext) && this._checkOCE(iastContext)) {
39
+ this._report(value, iastContext)
40
+ }
41
+ }
42
+
43
+ _checkOCE (context) {
44
+ return overheadController.hasQuota(overheadController.OPERATIONS.REPORT_VULNERABILITY, context)
45
+ }
46
+ }
47
+
48
+ module.exports = Analyzer
@@ -0,0 +1,24 @@
1
+ 'use strict'
2
+ const Analyzer = require('./vulnerability-analyzer')
3
+
4
+ const INSECURE_HASH_ALGORITHMS = new Set([
5
+ 'md4', 'md4WithRSAEncryption', 'RSA-MD4',
6
+ 'RSA-MD5', 'md5', 'md5-sha1', 'ssl3-md5', 'md5WithRSAEncryption',
7
+ 'RSA-SHA1', 'RSA-SHA1-2', 'sha1', 'md5-sha1', 'sha1WithRSAEncryption', 'ssl3-sha1'
8
+ ].map(algorithm => algorithm.toLowerCase()))
9
+
10
+ class WeakHashAnalyzer extends Analyzer {
11
+ constructor () {
12
+ super('WEAK_HASH')
13
+ this.addSub('datadog:crypto:hashing:start', ({ algorithm }) => this.analyze(algorithm))
14
+ }
15
+
16
+ _isVulnerable (algorithm) {
17
+ if (typeof algorithm === 'string') {
18
+ return INSECURE_HASH_ALGORITHMS.has(algorithm.toLowerCase())
19
+ }
20
+ return false
21
+ }
22
+ }
23
+
24
+ module.exports = new WeakHashAnalyzer()
@@ -0,0 +1,50 @@
1
+ const IAST_CONTEXT_KEY = Symbol('_dd.iast.context')
2
+
3
+ function getIastContext (store) {
4
+ return store && store[IAST_CONTEXT_KEY]
5
+ }
6
+
7
+ /* TODO Fix storage problem when the close event is called without
8
+ finish event to remove `topContext` references
9
+ We have to save the context in two places, because
10
+ clean can be called when the storage store is not available
11
+ */
12
+ function saveIastContext (store, topContext, context) {
13
+ if (store && topContext) {
14
+ store[IAST_CONTEXT_KEY] = context
15
+ topContext[IAST_CONTEXT_KEY] = context
16
+ return store[IAST_CONTEXT_KEY]
17
+ }
18
+ }
19
+
20
+ /* TODO Fix storage problem when the close event is called without
21
+ finish event to remove `topContext` references
22
+ iastContext is currently saved in store and request rootContext
23
+ to fix problems with `close` without `finish` events
24
+ */
25
+ function cleanIastContext (store, context, iastContext) {
26
+ if (store) {
27
+ if (!iastContext) {
28
+ iastContext = store[IAST_CONTEXT_KEY]
29
+ }
30
+ store[IAST_CONTEXT_KEY] = null
31
+ }
32
+ if (context) {
33
+ if (!iastContext) {
34
+ iastContext = context[IAST_CONTEXT_KEY]
35
+ }
36
+ context[IAST_CONTEXT_KEY] = null
37
+ }
38
+ if (iastContext) {
39
+ Object.keys(iastContext).forEach(key => delete iastContext[key])
40
+ return true
41
+ }
42
+ return false
43
+ }
44
+
45
+ module.exports = {
46
+ getIastContext,
47
+ saveIastContext,
48
+ cleanIastContext,
49
+ IAST_CONTEXT_KEY
50
+ }
@@ -0,0 +1,59 @@
1
+ const { sendVulnerabilities } = require('./vulnerability-reporter')
2
+ const { enableAllAnalyzers, disableAllAnalyzers } = require('./analyzers')
3
+ const web = require('../../plugins/util/web')
4
+ const { storage } = require('../../../../datadog-core')
5
+ const overheadController = require('./overhead-controller')
6
+ const dc = require('diagnostics_channel')
7
+ const { saveIastContext, getIastContext, cleanIastContext } = require('./iast-context')
8
+
9
+ // TODO Change to `apm:http:server:request:[start|close]` when the subscription
10
+ // order of the callbacks can be enforce
11
+ const requestStart = dc.channel('dd-trace:incomingHttpRequestStart')
12
+ const requestClose = dc.channel('dd-trace:incomingHttpRequestEnd')
13
+
14
+ function enable (config) {
15
+ enableAllAnalyzers()
16
+ requestStart.subscribe(onIncomingHttpRequestStart)
17
+ requestClose.subscribe(onIncomingHttpRequestEnd)
18
+ overheadController.configure(config.iast)
19
+ }
20
+
21
+ function disable () {
22
+ disableAllAnalyzers()
23
+ if (requestStart.hasSubscribers) requestStart.unsubscribe(onIncomingHttpRequestStart)
24
+ if (requestClose.hasSubscribers) requestClose.unsubscribe(onIncomingHttpRequestEnd)
25
+ }
26
+
27
+ function onIncomingHttpRequestStart (data) {
28
+ if (data && data.req) {
29
+ const store = storage.getStore()
30
+ if (store) {
31
+ const topContext = web.getContext(data.req)
32
+ if (topContext) {
33
+ const rootSpan = topContext.span
34
+ const isRequestAcquired = overheadController.acquireRequest(rootSpan)
35
+ if (isRequestAcquired) {
36
+ const iastContext = saveIastContext(store, topContext, { rootSpan, req: data.req })
37
+ overheadController.initializeRequestContext(iastContext)
38
+ }
39
+ }
40
+ }
41
+ }
42
+ }
43
+
44
+ function onIncomingHttpRequestEnd (data) {
45
+ if (data && data.req) {
46
+ const store = storage.getStore()
47
+ const iastContext = getIastContext(storage.getStore())
48
+ if (iastContext && iastContext.rootSpan) {
49
+ overheadController.releaseRequest()
50
+ sendVulnerabilities(iastContext, iastContext.rootSpan)
51
+ }
52
+ // TODO web.getContext(data.req) is required when the request is aborted
53
+ if (cleanIastContext(store, web.getContext(data.req), iastContext)) {
54
+ overheadController.releaseRequest()
55
+ }
56
+ }
57
+ }
58
+
59
+ module.exports = { enable, disable, onIncomingHttpRequestEnd, onIncomingHttpRequestStart }
@@ -0,0 +1,94 @@
1
+ 'use strict'
2
+
3
+ const OVERHEAD_CONTROLLER_CONTEXT_KEY = 'oce'
4
+ const REPORT_VULNERABILITY = 'REPORT_VULNERABILITY'
5
+
6
+ const GLOBAL_OCE_CONTEXT = {}
7
+ let config = {}
8
+ let availableRequest = 0
9
+ const OPERATIONS = {
10
+ REPORT_VULNERABILITY: {
11
+ hasQuota: (context) => {
12
+ const reserved = context && context.tokens && context.tokens[REPORT_VULNERABILITY] > 0
13
+ if (reserved) {
14
+ context.tokens[REPORT_VULNERABILITY]--
15
+ }
16
+ return reserved
17
+ },
18
+ name: REPORT_VULNERABILITY,
19
+ initialTokenBucketSize () {
20
+ return typeof config.maxContextOperations === 'number' ? config.maxContextOperations : 2
21
+ },
22
+ initContext: function (context) {
23
+ context.tokens[REPORT_VULNERABILITY] = this.initialTokenBucketSize()
24
+ }
25
+ }
26
+ }
27
+
28
+ function _getNewContext () {
29
+ const oceContext = {
30
+ tokens: {}
31
+ }
32
+
33
+ for (const operation in OPERATIONS) {
34
+ OPERATIONS[operation].initContext(oceContext)
35
+ }
36
+
37
+ return oceContext
38
+ }
39
+
40
+ function _getContext (iastContext) {
41
+ if (iastContext && iastContext[OVERHEAD_CONTROLLER_CONTEXT_KEY]) {
42
+ return iastContext[OVERHEAD_CONTROLLER_CONTEXT_KEY]
43
+ }
44
+ return GLOBAL_OCE_CONTEXT
45
+ }
46
+
47
+ function _resetGlobalContext () {
48
+ Object.assign(GLOBAL_OCE_CONTEXT, _getNewContext())
49
+ }
50
+
51
+ function acquireRequest (rootSpan) {
52
+ if (availableRequest > 0) {
53
+ const sampling = config && typeof config.requestSampling === 'number'
54
+ ? config.requestSampling : 30
55
+ if (rootSpan.context().toSpanId().slice(-2) <= sampling) {
56
+ availableRequest--
57
+ return true
58
+ }
59
+ }
60
+ return false
61
+ }
62
+
63
+ function releaseRequest () {
64
+ if (availableRequest < config.maxConcurrentRequests) {
65
+ availableRequest++
66
+ }
67
+ }
68
+
69
+ function hasQuota (operation, iastContext) {
70
+ const oceContext = _getContext(iastContext)
71
+ return operation.hasQuota(oceContext)
72
+ }
73
+
74
+ function initializeRequestContext (iastContext) {
75
+ if (iastContext) iastContext[OVERHEAD_CONTROLLER_CONTEXT_KEY] = _getNewContext()
76
+ }
77
+
78
+ function configure (cfg) {
79
+ config = cfg
80
+ availableRequest = config.maxConcurrentRequests
81
+ }
82
+
83
+ _resetGlobalContext()
84
+
85
+ module.exports = {
86
+ OVERHEAD_CONTROLLER_CONTEXT_KEY,
87
+ OPERATIONS,
88
+ _resetGlobalContext,
89
+ initializeRequestContext,
90
+ hasQuota,
91
+ acquireRequest,
92
+ releaseRequest,
93
+ configure
94
+ }
@@ -0,0 +1,70 @@
1
+ const path = require('path')
2
+ const pathLine = {
3
+ getFirstNonDDPathAndLine,
4
+ getFirstNonDDPathAndLineFromCallsites, // Exported only for test purposes
5
+ calculateDDBasePath, // Exported only for test purposes
6
+ ddBasePath: calculateDDBasePath(__dirname) // Only for test purposes
7
+ }
8
+
9
+ const EXCLUDED_PATHS = [
10
+ '/node_modules/diagnostics_channel'
11
+ ]
12
+ const EXCLUDED_PATH_PREFIXES = [
13
+ 'node:diagnostics_channel',
14
+ 'diagnostics_channel'
15
+ ]
16
+
17
+ function calculateDDBasePath (dirname) {
18
+ const dirSteps = dirname.split(path.sep)
19
+ const packagesIndex = dirSteps.indexOf('packages')
20
+ return dirSteps.slice(0, packagesIndex).join(path.sep) + path.sep
21
+ }
22
+
23
+ function getCallSiteInfo () {
24
+ const previousPrepareStackTrace = Error.prepareStackTrace
25
+ let callsiteList
26
+ Error.prepareStackTrace = function (_, callsites) {
27
+ callsiteList = callsites
28
+ }
29
+ const e = new Error()
30
+ e.stack
31
+ Error.prepareStackTrace = previousPrepareStackTrace
32
+ return callsiteList
33
+ }
34
+
35
+ function getFirstNonDDPathAndLineFromCallsites (callsites) {
36
+ if (callsites) {
37
+ for (let i = 0; i < callsites.length; i++) {
38
+ const callsite = callsites[i]
39
+ const path = callsite.getFileName()
40
+ if (!isExcluded(callsite) && path.indexOf(pathLine.ddBasePath) === -1) {
41
+ return {
42
+ path,
43
+ line: callsite.getLineNumber()
44
+ }
45
+ }
46
+ }
47
+ }
48
+ return null
49
+ }
50
+
51
+ function isExcluded (callsite) {
52
+ if (callsite.isNative()) return true
53
+ const filename = callsite.getFileName()
54
+ for (let i = 0; i < EXCLUDED_PATHS.length; i++) {
55
+ if (filename.indexOf(EXCLUDED_PATHS[i]) > -1) {
56
+ return true
57
+ }
58
+ }
59
+ for (let i = 0; i < EXCLUDED_PATH_PREFIXES.length; i++) {
60
+ if (filename.indexOf(EXCLUDED_PATH_PREFIXES[i]) === 0) {
61
+ return true
62
+ }
63
+ }
64
+ return false
65
+ }
66
+
67
+ function getFirstNonDDPathAndLine () {
68
+ return getFirstNonDDPathAndLineFromCallsites(getCallSiteInfo())
69
+ }
70
+ module.exports = pathLine