dd-trace 4.18.0 → 4.22.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 (110) hide show
  1. package/LICENSE-3rdparty.csv +3 -2
  2. package/README.md +3 -3
  3. package/ext/kinds.d.ts +1 -0
  4. package/ext/kinds.js +2 -1
  5. package/ext/tags.d.ts +2 -1
  6. package/ext/tags.js +6 -1
  7. package/index.d.ts +29 -0
  8. package/package.json +11 -10
  9. package/packages/datadog-core/src/storage/async_resource.js +1 -1
  10. package/packages/datadog-esbuild/index.js +1 -20
  11. package/packages/datadog-instrumentations/src/aerospike.js +47 -0
  12. package/packages/datadog-instrumentations/src/apollo-server-core.js +41 -0
  13. package/packages/datadog-instrumentations/src/apollo-server.js +83 -0
  14. package/packages/datadog-instrumentations/src/graphql.js +18 -4
  15. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +1 -2
  16. package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
  17. package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
  18. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  19. package/packages/datadog-instrumentations/src/http/client.js +10 -0
  20. package/packages/datadog-instrumentations/src/jest.js +11 -5
  21. package/packages/datadog-instrumentations/src/kafkajs.js +27 -0
  22. package/packages/datadog-instrumentations/src/next.js +18 -6
  23. package/packages/datadog-instrumentations/src/restify.js +14 -1
  24. package/packages/datadog-instrumentations/src/rhea.js +15 -9
  25. package/packages/datadog-plugin-aerospike/src/index.js +113 -0
  26. package/packages/datadog-plugin-graphql/src/resolve.js +26 -18
  27. package/packages/datadog-plugin-http/src/client.js +19 -2
  28. package/packages/datadog-plugin-kafkajs/src/consumer.js +59 -6
  29. package/packages/datadog-plugin-kafkajs/src/producer.js +64 -6
  30. package/packages/datadog-plugin-next/src/index.js +40 -14
  31. package/packages/dd-trace/src/appsec/activation.js +29 -0
  32. package/packages/dd-trace/src/appsec/addresses.js +3 -1
  33. package/packages/dd-trace/src/appsec/api_security_sampler.js +48 -0
  34. package/packages/dd-trace/src/appsec/blocked_templates.js +4 -1
  35. package/packages/dd-trace/src/appsec/blocking.js +95 -43
  36. package/packages/dd-trace/src/appsec/channels.js +5 -2
  37. package/packages/dd-trace/src/appsec/graphql.js +146 -0
  38. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  39. package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +105 -0
  40. package/packages/dd-trace/src/appsec/iast/iast-log.js +1 -1
  41. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +1 -1
  42. package/packages/dd-trace/src/appsec/iast/index.js +1 -1
  43. package/packages/dd-trace/src/appsec/iast/path-line.js +1 -1
  44. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +1 -1
  45. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/constants.js +7 -0
  46. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +12 -19
  47. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/header-sensitive-analyzer.js +20 -0
  48. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +6 -10
  49. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +18 -25
  50. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +79 -85
  51. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +27 -36
  52. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +14 -11
  53. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
  54. package/packages/dd-trace/src/appsec/index.js +33 -32
  55. package/packages/dd-trace/src/appsec/recommended.json +1737 -120
  56. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
  57. package/packages/dd-trace/src/appsec/remote_config/index.js +36 -15
  58. package/packages/dd-trace/src/appsec/reporter.js +50 -34
  59. package/packages/dd-trace/src/appsec/rule_manager.js +9 -6
  60. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  61. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +28 -13
  62. package/packages/dd-trace/src/appsec/waf/waf_manager.js +0 -1
  63. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +17 -1
  64. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +75 -56
  65. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +22 -6
  66. package/packages/dd-trace/src/config.js +48 -7
  67. package/packages/dd-trace/src/datastreams/processor.js +166 -26
  68. package/packages/dd-trace/src/format.js +6 -1
  69. package/packages/dd-trace/src/id.js +12 -0
  70. package/packages/dd-trace/src/iitm.js +1 -1
  71. package/packages/dd-trace/src/log/channels.js +1 -1
  72. package/packages/dd-trace/src/noop/proxy.js +4 -0
  73. package/packages/dd-trace/src/opentelemetry/span.js +95 -2
  74. package/packages/dd-trace/src/opentelemetry/tracer.js +9 -10
  75. package/packages/dd-trace/src/opentracing/propagation/text_map.js +14 -5
  76. package/packages/dd-trace/src/opentracing/span.js +6 -0
  77. package/packages/dd-trace/src/opentracing/span_context.js +5 -2
  78. package/packages/dd-trace/src/plugin_manager.js +1 -1
  79. package/packages/dd-trace/src/plugins/ci_plugin.js +2 -1
  80. package/packages/dd-trace/src/plugins/database.js +1 -1
  81. package/packages/dd-trace/src/plugins/index.js +1 -0
  82. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  83. package/packages/dd-trace/src/plugins/util/ci.js +6 -19
  84. package/packages/dd-trace/src/plugins/util/git.js +4 -3
  85. package/packages/dd-trace/src/plugins/util/ip_extractor.js +7 -6
  86. package/packages/dd-trace/src/plugins/util/test.js +3 -2
  87. package/packages/dd-trace/src/plugins/util/url.js +26 -0
  88. package/packages/dd-trace/src/plugins/util/user-provided-git.js +4 -16
  89. package/packages/dd-trace/src/profiler.js +5 -3
  90. package/packages/dd-trace/src/profiling/config.js +26 -2
  91. package/packages/dd-trace/src/profiling/profiler.js +17 -10
  92. package/packages/dd-trace/src/profiling/profilers/events.js +264 -0
  93. package/packages/dd-trace/src/profiling/profilers/shared.js +39 -0
  94. package/packages/dd-trace/src/profiling/profilers/space.js +2 -1
  95. package/packages/dd-trace/src/profiling/profilers/wall.js +121 -58
  96. package/packages/dd-trace/src/proxy.js +25 -1
  97. package/packages/dd-trace/src/ritm.js +1 -1
  98. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +5 -0
  99. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
  100. package/packages/dd-trace/src/span_processor.js +4 -0
  101. package/packages/dd-trace/src/spanleak.js +98 -0
  102. package/packages/dd-trace/src/startup-log.js +7 -1
  103. package/packages/dd-trace/src/telemetry/dependencies.js +56 -10
  104. package/packages/dd-trace/src/telemetry/index.js +136 -44
  105. package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
  106. package/packages/dd-trace/src/telemetry/send-data.js +47 -5
  107. package/packages/dd-trace/src/tracer.js +8 -2
  108. package/scripts/install_plugin_modules.js +11 -3
  109. package/packages/diagnostics_channel/index.js +0 -3
  110. package/packages/diagnostics_channel/src/index.js +0 -121
@@ -9,5 +9,6 @@ module.exports = {
9
9
  ASM_USER_BLOCKING: 1n << 7n,
10
10
  ASM_CUSTOM_RULES: 1n << 8n,
11
11
  ASM_CUSTOM_BLOCKING_RESPONSE: 1n << 9n,
12
- ASM_TRUSTED_IPS: 1n << 10n
12
+ ASM_TRUSTED_IPS: 1n << 10n,
13
+ ASM_API_SECURITY_SAMPLE_RATE: 1n << 11n
13
14
  }
@@ -1,38 +1,59 @@
1
1
  'use strict'
2
2
 
3
+ const Activation = require('../activation')
4
+
3
5
  const RemoteConfigManager = require('./manager')
4
6
  const RemoteConfigCapabilities = require('./capabilities')
7
+ const apiSecuritySampler = require('../api_security_sampler')
5
8
 
6
9
  let rc
7
10
 
8
11
  function enable (config) {
9
12
  rc = new RemoteConfigManager(config)
10
13
 
11
- if (config.appsec.enabled === undefined) { // only activate ASM_FEATURES when conf is not set locally
12
- rc.updateCapabilities(RemoteConfigCapabilities.ASM_ACTIVATION, true)
14
+ const activation = Activation.fromConfig(config)
15
+
16
+ if (activation !== Activation.DISABLED) {
17
+ if (activation === Activation.ONECLICK) {
18
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_ACTIVATION, true)
19
+ }
13
20
 
14
- rc.on('ASM_FEATURES', (action, conf) => {
15
- if (conf && conf.asm && typeof conf.asm.enabled === 'boolean') {
16
- let shouldEnable
21
+ if (config.appsec.apiSecurity?.enabled) {
22
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_API_SECURITY_SAMPLE_RATE, true)
23
+ }
17
24
 
18
- if (action === 'apply' || action === 'modify') {
19
- shouldEnable = conf.asm.enabled // take control
20
- } else {
21
- shouldEnable = config.appsec.enabled // give back control to local config
22
- }
25
+ rc.on('ASM_FEATURES', (action, rcConfig) => {
26
+ if (!rcConfig) return
23
27
 
24
- if (shouldEnable) {
25
- require('..').enable(config)
26
- } else {
27
- require('..').disable()
28
- }
28
+ if (activation === Activation.ONECLICK) {
29
+ enableOrDisableAppsec(action, rcConfig, config)
29
30
  }
31
+
32
+ apiSecuritySampler.setRequestSampling(rcConfig.api_security?.request_sample_rate)
30
33
  })
31
34
  }
32
35
 
33
36
  return rc
34
37
  }
35
38
 
39
+ function enableOrDisableAppsec (action, rcConfig, config) {
40
+ if (typeof rcConfig.asm?.enabled === 'boolean') {
41
+ let shouldEnable
42
+
43
+ if (action === 'apply' || action === 'modify') {
44
+ shouldEnable = rcConfig.asm.enabled // take control
45
+ } else {
46
+ shouldEnable = config.appsec.enabled // give back control to local config
47
+ }
48
+
49
+ if (shouldEnable) {
50
+ require('..').enable(config)
51
+ } else {
52
+ require('..').disable()
53
+ }
54
+ }
55
+ }
56
+
36
57
  function enableWafUpdate (appsecConfig) {
37
58
  if (rc && appsecConfig && !appsecConfig.customRulesProvided) {
38
59
  // dirty require to make startup faster for serverless
@@ -3,64 +3,61 @@
3
3
  const Limiter = require('../rate_limiter')
4
4
  const { storage } = require('../../../datadog-core')
5
5
  const web = require('../plugins/util/web')
6
+ const { ipHeaderList } = require('../plugins/util/ip_extractor')
6
7
  const {
7
8
  incrementWafInitMetric,
8
9
  updateWafRequestsMetricTags,
9
10
  incrementWafUpdatesMetric,
10
11
  incrementWafRequestsMetric
11
12
  } = require('./telemetry')
13
+ const zlib = require('zlib')
12
14
 
13
15
  // default limiter, configurable with setRateLimit()
14
16
  let limiter = new Limiter(100)
15
17
 
16
- // TODO: use precomputed maps instead
17
- const REQUEST_HEADERS_PASSLIST = [
18
- 'accept',
19
- 'accept-encoding',
20
- 'accept-language',
18
+ const metricsQueue = new Map()
19
+
20
+ const contentHeaderList = [
21
21
  'content-encoding',
22
22
  'content-language',
23
23
  'content-length',
24
- 'content-type',
25
- 'forwarded',
26
- 'forwarded-for',
24
+ 'content-type'
25
+ ]
26
+
27
+ const REQUEST_HEADERS_MAP = mapHeaderAndTags([
28
+ 'accept',
29
+ 'accept-encoding',
30
+ 'accept-language',
27
31
  'host',
28
- 'true-client-ip',
29
32
  'user-agent',
33
+ 'forwarded',
30
34
  'via',
31
- 'x-client-ip',
32
- 'x-cluster-client-ip',
33
- 'x-forwarded',
34
- 'x-forwarded-for',
35
- 'x-real-ip'
36
- ]
37
35
 
38
- const RESPONSE_HEADERS_PASSLIST = [
39
- 'content-encoding',
40
- 'content-language',
41
- 'content-length',
42
- 'content-type'
43
- ]
36
+ ...ipHeaderList,
37
+ ...contentHeaderList
38
+ ], 'http.request.headers.')
44
39
 
45
- const metricsQueue = new Map()
40
+ const RESPONSE_HEADERS_MAP = mapHeaderAndTags(contentHeaderList, 'http.response.headers.')
46
41
 
47
- function filterHeaders (headers, passlist, prefix) {
42
+ function mapHeaderAndTags (headerList, tagPrefix) {
43
+ return new Map(headerList.map(headerName => [headerName, `${tagPrefix}${formatHeaderName(headerName)}`]))
44
+ }
45
+
46
+ function filterHeaders (headers, map) {
48
47
  const result = {}
49
48
 
50
49
  if (!headers) return result
51
50
 
52
- for (let i = 0; i < passlist.length; ++i) {
53
- const headerName = passlist[i]
54
-
55
- if (headers[headerName]) {
56
- result[`${prefix}${formatHeaderName(headerName)}`] = '' + headers[headerName]
51
+ for (const [headerName, tagName] of map) {
52
+ const headerValue = headers[headerName]
53
+ if (headerValue) {
54
+ result[tagName] = '' + headerValue
57
55
  }
58
56
  }
59
57
 
60
58
  return result
61
59
  }
62
60
 
63
- // TODO: this can be precomputed at start time
64
61
  function formatHeaderName (name) {
65
62
  return name
66
63
  .trim()
@@ -86,7 +83,7 @@ function reportWafInit (wafVersion, rulesVersion, diagnosticsRules = {}) {
86
83
  function reportMetrics (metrics) {
87
84
  // TODO: metrics should be incremental, there already is an RFC to report metrics
88
85
  const store = storage.getStore()
89
- const rootSpan = store && store.req && web.root(store.req)
86
+ const rootSpan = store?.req && web.root(store.req)
90
87
  if (!rootSpan) return
91
88
 
92
89
  if (metrics.duration) {
@@ -106,13 +103,13 @@ function reportMetrics (metrics) {
106
103
 
107
104
  function reportAttack (attackData) {
108
105
  const store = storage.getStore()
109
- const req = store && store.req
106
+ const req = store?.req
110
107
  const rootSpan = web.root(req)
111
108
  if (!rootSpan) return
112
109
 
113
110
  const currentTags = rootSpan.context()._tags
114
111
 
115
- const newTags = filterHeaders(req.headers, REQUEST_HEADERS_PASSLIST, 'http.request.headers.')
112
+ const newTags = filterHeaders(req.headers, REQUEST_HEADERS_MAP)
116
113
 
117
114
  newTags['appsec.event'] = 'true'
118
115
 
@@ -144,6 +141,23 @@ function reportAttack (attackData) {
144
141
  rootSpan.addTags(newTags)
145
142
  }
146
143
 
144
+ function reportSchemas (derivatives) {
145
+ if (!derivatives) return
146
+
147
+ const req = storage.getStore()?.req
148
+ const rootSpan = web.root(req)
149
+
150
+ if (!rootSpan) return
151
+
152
+ const tags = {}
153
+ for (const [address, value] of Object.entries(derivatives)) {
154
+ const gzippedValue = zlib.gzipSync(JSON.stringify(value))
155
+ tags[address] = gzippedValue.toString('base64')
156
+ }
157
+
158
+ rootSpan.addTags(tags)
159
+ }
160
+
147
161
  function finishRequest (req, res) {
148
162
  const rootSpan = web.root(req)
149
163
  if (!rootSpan) return
@@ -158,7 +172,7 @@ function finishRequest (req, res) {
158
172
 
159
173
  if (!rootSpan.context()._tags['appsec.event']) return
160
174
 
161
- const newTags = filterHeaders(res.getHeaders(), RESPONSE_HEADERS_PASSLIST, 'http.response.headers.')
175
+ const newTags = filterHeaders(res.getHeaders(), RESPONSE_HEADERS_MAP)
162
176
 
163
177
  if (req.route && typeof req.route.path === 'string') {
164
178
  newTags['http.endpoint'] = req.route.path
@@ -179,6 +193,8 @@ module.exports = {
179
193
  reportMetrics,
180
194
  reportAttack,
181
195
  reportWafUpdate: incrementWafUpdatesMetric,
196
+ reportSchemas,
182
197
  finishRequest,
183
- setRateLimit
198
+ setRateLimit,
199
+ mapHeaderAndTags
184
200
  }
@@ -1,5 +1,6 @@
1
1
  'use strict'
2
2
 
3
+ const fs = require('fs')
3
4
  const waf = require('./waf')
4
5
  const { ACKNOWLEDGED, ERROR } = require('./remote_config/apply_states')
5
6
  const blocking = require('./blocking')
@@ -13,13 +14,15 @@ let appliedExclusions = new Map()
13
14
  let appliedCustomRules = new Map()
14
15
  let appliedActions = new Map()
15
16
 
16
- function applyRules (rules, config) {
17
- defaultRules = rules
17
+ function loadRules (config) {
18
+ defaultRules = config.rules
19
+ ? JSON.parse(fs.readFileSync(config.rules))
20
+ : require('./recommended.json')
18
21
 
19
- waf.init(rules, config)
22
+ waf.init(defaultRules, config)
20
23
 
21
- if (rules.actions) {
22
- blocking.updateBlockingConfiguration(rules.actions.find(action => action.id === 'block'))
24
+ if (defaultRules.actions) {
25
+ blocking.updateBlockingConfiguration(defaultRules.actions.find(action => action.id === 'block'))
23
26
  }
24
27
  }
25
28
 
@@ -252,7 +255,7 @@ function clearAllRules () {
252
255
  }
253
256
 
254
257
  module.exports = {
255
- applyRules,
258
+ loadRules,
256
259
  updateWafFromRC,
257
260
  clearAllRules
258
261
  }
@@ -9,7 +9,7 @@ const { setUserTags } = require('./set_user')
9
9
  const log = require('../../log')
10
10
 
11
11
  function isUserBlocked (user) {
12
- const actions = waf.run({ [USER_ID]: user.id })
12
+ const actions = waf.run({ persistent: { [USER_ID]: user.id } })
13
13
 
14
14
  if (!actions) return false
15
15
 
@@ -10,37 +10,50 @@ const preventDuplicateAddresses = new Set([
10
10
  ])
11
11
 
12
12
  class WAFContextWrapper {
13
- constructor (ddwafContext, requiredAddresses, wafTimeout, wafVersion, rulesVersion) {
13
+ constructor (ddwafContext, wafTimeout, wafVersion, rulesVersion) {
14
14
  this.ddwafContext = ddwafContext
15
- this.requiredAddresses = requiredAddresses
16
15
  this.wafTimeout = wafTimeout
17
16
  this.wafVersion = wafVersion
18
17
  this.rulesVersion = rulesVersion
19
18
  this.addressesToSkip = new Set()
20
19
  }
21
20
 
22
- run (params) {
21
+ run ({ persistent, ephemeral }) {
22
+ const payload = {}
23
+ let payloadHasData = false
23
24
  const inputs = {}
24
- let someInputAdded = false
25
25
  const newAddressesToSkip = new Set(this.addressesToSkip)
26
26
 
27
- // TODO: possible optimizaion: only send params that haven't already been sent with same value to this wafContext
28
- for (const key of Object.keys(params)) {
29
- if (this.requiredAddresses.has(key) && !this.addressesToSkip.has(key)) {
30
- inputs[key] = params[key]
31
- if (preventDuplicateAddresses.has(key)) {
32
- newAddressesToSkip.add(key)
27
+ if (persistent && typeof persistent === 'object') {
28
+ // TODO: possible optimization: only send params that haven't already been sent with same value to this wafContext
29
+ for (const key of Object.keys(persistent)) {
30
+ // TODO: requiredAddresses is no longer used due to processor addresses are not included in the list. Check on
31
+ // future versions when the actual addresses are included in the 'loaded' section inside diagnostics.
32
+ if (!this.addressesToSkip.has(key)) {
33
+ inputs[key] = persistent[key]
34
+ if (preventDuplicateAddresses.has(key)) {
35
+ newAddressesToSkip.add(key)
36
+ }
33
37
  }
34
- someInputAdded = true
35
38
  }
36
39
  }
37
40
 
38
- if (!someInputAdded) return
41
+ if (Object.keys(inputs).length) {
42
+ payload['persistent'] = inputs
43
+ payloadHasData = true
44
+ }
45
+
46
+ if (ephemeral && Object.keys(ephemeral).length) {
47
+ payload['ephemeral'] = ephemeral
48
+ payloadHasData = true
49
+ }
50
+
51
+ if (!payloadHasData) return
39
52
 
40
53
  try {
41
54
  const start = process.hrtime.bigint()
42
55
 
43
- const result = this.ddwafContext.run(inputs, this.wafTimeout)
56
+ const result = this.ddwafContext.run(payload, this.wafTimeout)
44
57
 
45
58
  const end = process.hrtime.bigint()
46
59
 
@@ -63,6 +76,8 @@ class WAFContextWrapper {
63
76
  Reporter.reportAttack(JSON.stringify(result.events))
64
77
  }
65
78
 
79
+ Reporter.reportSchemas(result.derivatives)
80
+
66
81
  return result.actions
67
82
  } catch (err) {
68
83
  log.error('Error while running the AppSec WAF')
@@ -37,7 +37,6 @@ class WAFManager {
37
37
  if (!wafContext) {
38
38
  wafContext = new WAFContextWrapper(
39
39
  this.ddwaf.createContext(),
40
- this.ddwaf.requiredAddresses,
41
40
  this.wafTimeout,
42
41
  this.ddwafVersion,
43
42
  this.rulesVersion
@@ -143,7 +143,23 @@ class CiVisibilityExporter extends AgentInfoExporter {
143
143
  * where the tests run in a subprocess, because `getItrConfiguration` is called only once.
144
144
  */
145
145
  this._itrConfig = itrConfig
146
- callback(err, itrConfig)
146
+
147
+ if (err) {
148
+ callback(err, {})
149
+ } else if (itrConfig?.requireGit) {
150
+ // If the backend requires git, we'll wait for the upload to finish and request settings again
151
+ this._gitUploadPromise.then(gitUploadError => {
152
+ if (gitUploadError) {
153
+ return callback(gitUploadError, {})
154
+ }
155
+ getItrConfigurationRequest(configuration, (err, finalItrConfig) => {
156
+ this._itrConfig = finalItrConfig
157
+ callback(err, finalItrConfig)
158
+ })
159
+ })
160
+ } else {
161
+ callback(null, itrConfig)
162
+ }
147
163
  })
148
164
  })
149
165
  }
@@ -10,7 +10,7 @@ const {
10
10
  getLatestCommits,
11
11
  getRepositoryUrl,
12
12
  generatePackFilesForCommits,
13
- getCommitsToUpload,
13
+ getCommitsRevList,
14
14
  isShallowRepository,
15
15
  unshallowRepository
16
16
  } = require('../../../plugins/util/git')
@@ -46,11 +46,7 @@ function getCommonRequestOptions (url) {
46
46
  * The response are the commits for which the backend already has information
47
47
  * This response is used to know which commits can be ignored from there on
48
48
  */
49
- function getCommitsToExclude ({ url, isEvpProxy, repositoryUrl }, callback) {
50
- const latestCommits = getLatestCommits()
51
-
52
- log.debug(`There were ${latestCommits.length} commits since last month.`)
53
-
49
+ function getCommitsToUpload ({ url, repositoryUrl, latestCommits, isEvpProxy }, callback) {
54
50
  const commonOptions = getCommonRequestOptions(url)
55
51
 
56
52
  const options = {
@@ -83,13 +79,23 @@ function getCommitsToExclude ({ url, isEvpProxy, repositoryUrl }, callback) {
83
79
  const error = new Error(`Error fetching commits to exclude: ${err.message}`)
84
80
  return callback(error)
85
81
  }
86
- let commitsToExclude
82
+ let alreadySeenCommits
87
83
  try {
88
- commitsToExclude = validateCommits(JSON.parse(response).data)
84
+ alreadySeenCommits = validateCommits(JSON.parse(response).data)
89
85
  } catch (e) {
90
86
  return callback(new Error(`Can't parse commits to exclude response: ${e.message}`))
91
87
  }
92
- callback(null, commitsToExclude, latestCommits)
88
+ log.debug(`There are ${alreadySeenCommits.length} commits to exclude.`)
89
+ const commitsToInclude = latestCommits.filter((commit) => !alreadySeenCommits.includes(commit))
90
+ log.debug(`There are ${commitsToInclude.length} commits to include.`)
91
+
92
+ if (!commitsToInclude.length) {
93
+ return callback(null, [])
94
+ }
95
+
96
+ const commitsToUpload = getCommitsRevList(alreadySeenCommits, commitsToInclude)
97
+
98
+ callback(null, commitsToUpload)
93
99
  })
94
100
  }
95
101
 
@@ -150,6 +156,53 @@ function uploadPackFile ({ url, isEvpProxy, packFileToUpload, repositoryUrl, hea
150
156
  })
151
157
  }
152
158
 
159
+ function generateAndUploadPackFiles ({
160
+ url,
161
+ isEvpProxy,
162
+ commitsToUpload,
163
+ repositoryUrl,
164
+ headCommit
165
+ }, callback) {
166
+ log.debug(`There are ${commitsToUpload.length} commits to upload`)
167
+
168
+ const packFilesToUpload = generatePackFilesForCommits(commitsToUpload)
169
+
170
+ log.debug(`Uploading ${packFilesToUpload.length} packfiles.`)
171
+
172
+ if (!packFilesToUpload.length) {
173
+ return callback(new Error('Failed to generate packfiles'))
174
+ }
175
+
176
+ let packFileIndex = 0
177
+ // This uploads packfiles sequentially
178
+ const uploadPackFileCallback = (err) => {
179
+ if (err || packFileIndex === packFilesToUpload.length) {
180
+ return callback(err)
181
+ }
182
+ return uploadPackFile(
183
+ {
184
+ packFileToUpload: packFilesToUpload[packFileIndex++],
185
+ url,
186
+ isEvpProxy,
187
+ repositoryUrl,
188
+ headCommit
189
+ },
190
+ uploadPackFileCallback
191
+ )
192
+ }
193
+
194
+ uploadPackFile(
195
+ {
196
+ packFileToUpload: packFilesToUpload[packFileIndex++],
197
+ url,
198
+ isEvpProxy,
199
+ repositoryUrl,
200
+ headCommit
201
+ },
202
+ uploadPackFileCallback
203
+ )
204
+ }
205
+
153
206
  /**
154
207
  * This function uploads git metadata to CI Visibility's backend.
155
208
  */
@@ -165,65 +218,31 @@ function sendGitMetadata (url, isEvpProxy, configRepositoryUrl, callback) {
165
218
  return callback(new Error('Repository URL is empty'))
166
219
  }
167
220
 
168
- if (isShallowRepository()) {
169
- log.debug('It is shallow clone, unshallowing...')
170
- unshallowRepository()
171
- }
221
+ const latestCommits = getLatestCommits()
222
+ log.debug(`There were ${latestCommits.length} commits since last month.`)
223
+ const [headCommit] = latestCommits
172
224
 
173
- getCommitsToExclude({ url, repositoryUrl, isEvpProxy }, (err, commitsToExclude, latestCommits) => {
225
+ const getOnFinishGetCommitsToUpload = (hasCheckedShallow) => (err, commitsToUpload) => {
174
226
  if (err) {
175
227
  return callback(err)
176
228
  }
177
- log.debug(`There are ${commitsToExclude.length} commits to exclude.`)
178
- const [headCommit] = latestCommits
179
- const commitsToInclude = latestCommits.filter((commit) => !commitsToExclude.includes(commit))
180
- log.debug(`There are ${commitsToInclude.length} commits to include.`)
181
-
182
- const commitsToUpload = getCommitsToUpload(commitsToExclude, commitsToInclude)
183
229
 
184
230
  if (!commitsToUpload.length) {
185
231
  log.debug('No commits to upload')
186
232
  return callback(null)
187
233
  }
188
- log.debug(`There are ${commitsToUpload.length} commits to upload`)
189
-
190
- const packFilesToUpload = generatePackFilesForCommits(commitsToUpload)
191
-
192
- log.debug(`Uploading ${packFilesToUpload.length} packfiles.`)
193
-
194
- if (!packFilesToUpload.length) {
195
- return callback(new Error('Failed to generate packfiles'))
196
- }
197
234
 
198
- let packFileIndex = 0
199
- // This uploads packfiles sequentially
200
- const uploadPackFileCallback = (err) => {
201
- if (err || packFileIndex === packFilesToUpload.length) {
202
- return callback(err)
203
- }
204
- return uploadPackFile(
205
- {
206
- packFileToUpload: packFilesToUpload[packFileIndex++],
207
- url,
208
- isEvpProxy,
209
- repositoryUrl,
210
- headCommit
211
- },
212
- uploadPackFileCallback
213
- )
235
+ // If it has already unshallowed or the clone is not shallow, we move on
236
+ if (hasCheckedShallow || !isShallowRepository()) {
237
+ return generateAndUploadPackFiles({ url, isEvpProxy, commitsToUpload, repositoryUrl, headCommit }, callback)
214
238
  }
239
+ // Otherwise we unshallow and get commits to upload again
240
+ log.debug('It is shallow clone, unshallowing...')
241
+ unshallowRepository()
242
+ getCommitsToUpload({ url, repositoryUrl, latestCommits, isEvpProxy }, getOnFinishGetCommitsToUpload(true))
243
+ }
215
244
 
216
- uploadPackFile(
217
- {
218
- packFileToUpload: packFilesToUpload[packFileIndex++],
219
- url,
220
- isEvpProxy,
221
- repositoryUrl,
222
- headCommit
223
- },
224
- uploadPackFileCallback
225
- )
226
- })
245
+ getCommitsToUpload({ url, repositoryUrl, latestCommits, isEvpProxy }, getOnFinishGetCommitsToUpload(false))
227
246
  }
228
247
 
229
248
  module.exports = {
@@ -15,6 +15,7 @@ function getItrConfiguration ({
15
15
  runtimeName,
16
16
  runtimeVersion,
17
17
  branch,
18
+ testLevel = 'suite',
18
19
  custom
19
20
  }, done) {
20
21
  const options = {
@@ -23,7 +24,8 @@ function getItrConfiguration ({
23
24
  headers: {
24
25
  'Content-Type': 'application/json'
25
26
  },
26
- url
27
+ url,
28
+ timeout: 20000
27
29
  }
28
30
 
29
31
  if (isEvpProxy) {
@@ -42,7 +44,7 @@ function getItrConfiguration ({
42
44
  id: id().toString(10),
43
45
  type: 'ci_app_test_service_libraries_settings',
44
46
  attributes: {
45
- test_level: 'suite',
47
+ test_level: testLevel,
46
48
  configurations: {
47
49
  'os.platform': osPlatform,
48
50
  'os.version': osVersion,
@@ -69,13 +71,27 @@ function getItrConfiguration ({
69
71
  data: {
70
72
  attributes: {
71
73
  code_coverage: isCodeCoverageEnabled,
72
- tests_skipping: isSuitesSkippingEnabled
74
+ tests_skipping: isSuitesSkippingEnabled,
75
+ itr_enabled: isItrEnabled,
76
+ require_git: requireGit
73
77
  }
74
78
  }
75
79
  } = JSON.parse(res)
76
- const config = { isCodeCoverageEnabled, isSuitesSkippingEnabled }
77
- log.debug(() => `Received settings: ${config}`)
78
- done(null, config)
80
+
81
+ const settings = { isCodeCoverageEnabled, isSuitesSkippingEnabled, isItrEnabled, requireGit }
82
+
83
+ log.debug(() => `Remote settings: ${JSON.stringify(settings)}`)
84
+
85
+ if (process.env.DD_CIVISIBILITY_DANGEROUSLY_FORCE_COVERAGE) {
86
+ settings.isCodeCoverageEnabled = true
87
+ log.debug(() => 'Dangerously set code coverage to true')
88
+ }
89
+ if (process.env.DD_CIVISIBILITY_DANGEROUSLY_FORCE_TEST_SKIPPING) {
90
+ settings.isSuitesSkippingEnabled = true
91
+ log.debug(() => 'Dangerously set test skipping to true')
92
+ }
93
+
94
+ done(null, settings)
79
95
  } catch (err) {
80
96
  done(err)
81
97
  }