dd-trace 2.23.0 → 2.25.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 (66) hide show
  1. package/LICENSE-3rdparty.csv +2 -0
  2. package/index.d.ts +33 -1
  3. package/package.json +10 -7
  4. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  5. package/packages/datadog-instrumentations/src/helpers/register.js +7 -0
  6. package/packages/datadog-instrumentations/src/http/server.js +7 -1
  7. package/packages/datadog-instrumentations/src/ldapjs.js +91 -0
  8. package/packages/datadog-instrumentations/src/mocha.js +33 -8
  9. package/packages/datadog-instrumentations/src/pg.js +6 -2
  10. package/packages/datadog-plugin-http/src/client.js +1 -1
  11. package/packages/datadog-plugin-http/src/server.js +7 -3
  12. package/packages/datadog-plugin-jest/src/index.js +2 -2
  13. package/packages/datadog-plugin-mocha/src/index.js +2 -2
  14. package/packages/datadog-plugin-pg/src/index.js +1 -1
  15. package/packages/datadog-plugin-router/src/index.js +6 -3
  16. package/packages/dd-trace/src/appsec/addresses.js +3 -1
  17. package/packages/dd-trace/src/appsec/blocking.js +44 -0
  18. package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +1 -1
  19. package/packages/dd-trace/src/appsec/gateway/engine/engine.js +1 -1
  20. package/packages/dd-trace/src/appsec/gateway/engine/index.js +6 -1
  21. package/packages/dd-trace/src/appsec/gateway/engine/runner.js +0 -1
  22. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +2 -1
  23. package/packages/dd-trace/src/appsec/iast/analyzers/ldap-injection-analyzer.js +11 -0
  24. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +1 -1
  25. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +41 -3
  26. package/packages/dd-trace/src/appsec/iast/iast-context.js +3 -1
  27. package/packages/dd-trace/src/appsec/iast/index.js +15 -3
  28. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +20 -1
  29. package/packages/dd-trace/src/appsec/iast/path-line.js +6 -5
  30. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +17 -0
  31. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +2 -29
  32. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +16 -15
  33. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +103 -0
  34. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +63 -41
  35. package/packages/dd-trace/src/appsec/index.js +68 -27
  36. package/packages/dd-trace/src/{plugins/util → appsec}/ip_blocklist.js +0 -0
  37. package/packages/dd-trace/src/appsec/ip_extractor.js +98 -0
  38. package/packages/dd-trace/src/appsec/recommended.json +75 -8
  39. package/packages/dd-trace/src/appsec/remote_config/index.js +2 -1
  40. package/packages/dd-trace/src/appsec/remote_config/manager.js +2 -2
  41. package/packages/dd-trace/src/appsec/templates/blocked.html +99 -0
  42. package/packages/dd-trace/src/appsec/templates/blocked.json +8 -0
  43. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +4 -0
  44. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +18 -2
  45. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +12 -6
  46. package/packages/dd-trace/src/config.js +49 -8
  47. package/packages/dd-trace/src/exporters/common/request.js +33 -1
  48. package/packages/dd-trace/src/format.js +5 -1
  49. package/packages/dd-trace/src/lambda/handler.js +72 -0
  50. package/packages/dd-trace/src/lambda/index.js +5 -0
  51. package/packages/dd-trace/src/lambda/runtime/errors.js +20 -0
  52. package/packages/dd-trace/src/lambda/runtime/patch.js +74 -0
  53. package/packages/dd-trace/src/lambda/runtime/ritm.js +143 -0
  54. package/packages/dd-trace/src/plugin_manager.js +5 -11
  55. package/packages/dd-trace/src/plugins/ci_plugin.js +6 -0
  56. package/packages/dd-trace/src/plugins/database.js +4 -4
  57. package/packages/dd-trace/src/plugins/log_plugin.js +2 -2
  58. package/packages/dd-trace/src/plugins/util/ci.js +5 -2
  59. package/packages/dd-trace/src/plugins/util/test.js +2 -2
  60. package/packages/dd-trace/src/plugins/util/user-provided-git.js +14 -1
  61. package/packages/dd-trace/src/plugins/util/web.js +1 -104
  62. package/packages/dd-trace/src/priority_sampler.js +6 -2
  63. package/packages/dd-trace/src/proxy.js +4 -3
  64. package/packages/dd-trace/src/ritm.js +7 -1
  65. package/packages/dd-trace/src/span_processor.js +13 -0
  66. package/packages/dd-trace/src/span_sampler.js +1 -4
@@ -10,31 +10,42 @@ const Gateway = require('./gateway/engine')
10
10
  const addresses = require('./addresses')
11
11
  const Reporter = require('./reporter')
12
12
  const web = require('../plugins/util/web')
13
+ const { extractIp } = require('./ip_extractor')
14
+ const { HTTP_CLIENT_IP } = require('../../../../ext/tags')
15
+ const { block, loadTemplates, loadTemplatesAsync } = require('./blocking')
13
16
 
14
17
  let isEnabled = false
18
+ let config
15
19
 
16
- function enable (config) {
20
+ function enable (_config) {
17
21
  if (isEnabled) return
18
22
 
19
23
  try {
20
- // TODO: enable dc_blocking: config.appsec.blocking === true
24
+ loadTemplates(_config)
25
+ const rules = fs.readFileSync(_config.appsec.rules || path.join(__dirname, 'recommended.json'))
26
+ enableFromRules(_config, JSON.parse(rules))
27
+ } catch (err) {
28
+ abortEnable(err)
29
+ }
30
+ }
21
31
 
22
- let rules = fs.readFileSync(config.appsec.rules || path.join(__dirname, 'recommended.json'))
23
- rules = JSON.parse(rules)
32
+ async function enableAsync (_config) {
33
+ if (isEnabled) return
24
34
 
25
- RuleManager.applyRules(rules, config.appsec)
26
- remoteConfig.enableAsmData(config.appsec)
35
+ try {
36
+ await loadTemplatesAsync(_config)
37
+ const rules = await fs.promises.readFile(_config.appsec.rules || path.join(__dirname, 'recommended.json'))
38
+ enableFromRules(_config, JSON.parse(rules))
27
39
  } catch (err) {
28
- log.error('Unable to start AppSec')
29
- log.error(err)
30
-
31
- // abort AppSec start
32
- RuleManager.clearAllRules()
33
- remoteConfig.disableAsmData()
34
- return
40
+ abortEnable(err)
35
41
  }
42
+ }
43
+
44
+ function enableFromRules (_config, rules) {
45
+ RuleManager.applyRules(rules, _config.appsec)
46
+ remoteConfig.enableAsmData(_config.appsec)
36
47
 
37
- Reporter.setRateLimit(config.appsec.rateLimit)
48
+ Reporter.setRateLimit(_config.appsec.rateLimit)
38
49
 
39
50
  incomingHttpRequestStart.subscribe(incomingHttpStartTranslator)
40
51
  incomingHttpRequestEnd.subscribe(incomingHttpEndTranslator)
@@ -46,27 +57,55 @@ function enable (config) {
46
57
  Gateway.manager.addresses.add(addresses.HTTP_INCOMING_REMOTE_IP)
47
58
 
48
59
  isEnabled = true
60
+ config = _config
49
61
  }
50
62
 
51
- function incomingHttpStartTranslator (data) {
52
- // TODO: get span from datadog-core storage instead
53
- const topSpan = web.root(data.req)
54
- if (topSpan) {
55
- topSpan.addTags({
56
- '_dd.appsec.enabled': 1,
57
- '_dd.runtime_family': 'nodejs'
58
- })
59
- }
63
+ function abortEnable (err) {
64
+ log.error('Unable to start AppSec')
65
+ log.error(err)
66
+
67
+ // abort AppSec start
68
+ RuleManager.clearAllRules()
69
+ remoteConfig.disableAsmData()
60
70
  }
61
71
 
62
- function incomingHttpEndTranslator (data) {
72
+ function incomingHttpStartTranslator ({ req, res, abortController }) {
73
+ const topSpan = web.root(req)
74
+ if (!topSpan) return
75
+
76
+ const clientIp = extractIp(config, req)
77
+
78
+ topSpan.addTags({
79
+ '_dd.appsec.enabled': 1,
80
+ '_dd.runtime_family': 'nodejs',
81
+ [HTTP_CLIENT_IP]: clientIp
82
+ })
83
+
63
84
  const store = Gateway.startContext()
64
85
 
65
- store.set('req', data.req)
66
- store.set('res', data.res)
86
+ store.set('req', req)
87
+ store.set('res', res)
67
88
 
68
89
  const context = store.get('context')
69
90
 
91
+ if (clientIp) {
92
+ const results = Gateway.propagate({
93
+ [addresses.HTTP_CLIENT_IP]: clientIp
94
+ }, context)
95
+
96
+ if (!results || !abortController) return
97
+
98
+ for (const entry of results) {
99
+ if (entry && entry.includes('block')) {
100
+ block(req, res, topSpan, abortController)
101
+ break
102
+ }
103
+ }
104
+ }
105
+ }
106
+
107
+ function incomingHttpEndTranslator (data) {
108
+ const context = Gateway.getContext()
70
109
  if (!context) return
71
110
 
72
111
  const requestHeaders = Object.assign({}, data.req.headers)
@@ -107,7 +146,7 @@ function incomingHttpEndTranslator (data) {
107
146
  payload[addresses.HTTP_INCOMING_COOKIES] = {}
108
147
 
109
148
  for (const k of Object.keys(data.req.cookies)) {
110
- payload[addresses.HTTP_INCOMING_COOKIES][k] = [ data.req.cookies[k] ]
149
+ payload[addresses.HTTP_INCOMING_COOKIES][k] = [data.req.cookies[k]]
111
150
  }
112
151
  }
113
152
 
@@ -118,6 +157,7 @@ function incomingHttpEndTranslator (data) {
118
157
 
119
158
  function disable () {
120
159
  isEnabled = false
160
+ config = null
121
161
 
122
162
  RuleManager.clearAllRules()
123
163
  remoteConfig.disableAsmData()
@@ -129,6 +169,7 @@ function disable () {
129
169
 
130
170
  module.exports = {
131
171
  enable,
172
+ enableAsync,
132
173
  disable,
133
174
  incomingHttpStartTranslator,
134
175
  incomingHttpEndTranslator
@@ -0,0 +1,98 @@
1
+ 'use strict'
2
+
3
+ const BlockList = require('./ip_blocklist')
4
+ const net = require('net')
5
+ const log = require('../log')
6
+
7
+ const ipHeaderList = [
8
+ 'x-forwarded-for',
9
+ 'x-real-ip',
10
+ 'client-ip',
11
+ 'x-forwarded',
12
+ 'x-cluster-client-ip',
13
+ 'forwarded-for',
14
+ 'forwarded',
15
+ 'via',
16
+ 'true-client-ip'
17
+ ]
18
+
19
+ const privateCIDRs = [
20
+ '127.0.0.0/8',
21
+ '10.0.0.0/8',
22
+ '172.16.0.0/12',
23
+ '192.168.0.0/16',
24
+ '169.254.0.0/16',
25
+ '::1/128',
26
+ 'fec0::/10',
27
+ 'fe80::/10',
28
+ 'fc00::/7',
29
+ 'fd00::/8'
30
+ ]
31
+
32
+ const privateIPMatcher = new BlockList()
33
+
34
+ for (const cidr of privateCIDRs) {
35
+ const [address, prefix] = cidr.split('/')
36
+
37
+ privateIPMatcher.addSubnet(address, parseInt(prefix), net.isIPv6(address) ? 'ipv6' : 'ipv4')
38
+ }
39
+
40
+ function extractIp (config, req) {
41
+ const headers = req.headers
42
+ if (config.clientIpHeader) {
43
+ if (!headers) return
44
+ const header = headers[config.clientIpHeader]
45
+ if (!header) return
46
+
47
+ return findFirstIp(header)
48
+ }
49
+
50
+ const foundHeaders = []
51
+ if (headers) {
52
+ for (let i = 0; i < ipHeaderList.length; i++) {
53
+ if (headers[ipHeaderList[i]]) {
54
+ foundHeaders.push(ipHeaderList[i])
55
+ }
56
+ }
57
+ }
58
+
59
+ if (foundHeaders.length === 1) {
60
+ const header = headers[foundHeaders[0]]
61
+ const firstIp = findFirstIp(header)
62
+
63
+ if (firstIp) return firstIp
64
+ } else if (foundHeaders.length > 1) {
65
+ log.error(`Cannot find client IP: multiple IP headers detected ${foundHeaders}`)
66
+ return
67
+ }
68
+
69
+ return req.socket && req.socket.remoteAddress
70
+ }
71
+
72
+ function findFirstIp (str) {
73
+ let firstPrivateIp
74
+ const splitted = str.split(',')
75
+
76
+ for (let i = 0; i < splitted.length; i++) {
77
+ const chunk = splitted[i].trim()
78
+
79
+ // TODO: strip port and interface data ?
80
+
81
+ const type = net.isIP(chunk)
82
+ if (!type) continue
83
+
84
+ if (!privateIPMatcher.check(chunk, type === 6 ? 'ipv6' : 'ipv4')) {
85
+ // it's public, return it immediately
86
+ return chunk
87
+ }
88
+
89
+ // it's private, only save the first one found
90
+ if (!firstPrivateIp) firstPrivateIp = chunk
91
+ }
92
+
93
+ return firstPrivateIp
94
+ }
95
+
96
+ module.exports = {
97
+ extractIp
98
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": "2.2",
3
3
  "metadata": {
4
- "rules_version": "1.4.2"
4
+ "rules_version": "1.4.3"
5
5
  },
6
6
  "rules": [
7
7
  {
@@ -1802,7 +1802,7 @@
1802
1802
  "address": "server.request.path_params"
1803
1803
  }
1804
1804
  ],
1805
- "regex": "^(?i:file|ftps?|https?).*?\\?+$",
1805
+ "regex": "^(?i:file|ftps?|http)://.*?\\?+$",
1806
1806
  "options": {
1807
1807
  "case_sensitive": true,
1808
1808
  "min_length": 4
@@ -2694,8 +2694,9 @@
2694
2694
  "address": "grpc.server.request.message"
2695
2695
  }
2696
2696
  ],
2697
- "regex": "\\b(?:s(?:e(?:t(?:_(?:e(?:xception|rror)_handler|magic_quotes_runtime|include_path)|defaultstub)|ssion_s(?:et_save_handler|tart))|qlite_(?:(?:(?:unbuffered|single|array)_)?query|create_(?:aggregate|function)|p?open|exec)|tr(?:eam_(?:context_create|socket_client)|ipc?slashes|rev)|implexml_load_(?:string|file)|ocket_c(?:onnect|reate)|h(?:ow_sourc|a1_fil)e|pl_autoload_register|ystem)|p(?:r(?:eg_(?:replace(?:_callback(?:_array)?)?|match(?:_all)?|split)|oc_(?:(?:terminat|clos|nic)e|get_status|open)|int_r)|o(?:six_(?:get(?:(?:e[gu]|g)id|login|pwnam)|mk(?:fifo|nod)|ttyname|kill)|pen)|hp(?:_(?:strip_whitespac|unam)e|version|info)|g_(?:(?:execut|prepar)e|connect|query)|a(?:rse_(?:ini_file|str)|ssthru)|utenv)|r(?:unkit_(?:function_(?:re(?:defin|nam)e|copy|add)|method_(?:re(?:defin|nam)e|copy|add)|constant_(?:redefine|add))|e(?:(?:gister_(?:shutdown|tick)|name)_function|ad(?:(?:gz)?file|_exif_data|dir))|awurl(?:de|en)code)|i(?:mage(?:createfrom(?:(?:jpe|pn)g|x[bp]m|wbmp|gif)|(?:jpe|pn)g|g(?:d2?|if)|2?wbmp|xbm)|s_(?:(?:(?:execut|write?|read)ab|fi)le|dir)|ni_(?:get(?:_all)?|set)|terator_apply|ptcembed)|g(?:et(?:_(?:c(?:urrent_use|fg_va)r|meta_tags)|my(?:[gpu]id|inode)|(?:lastmo|cw)d|imagesize|env)|z(?:(?:(?:defla|wri)t|encod|fil)e|compress|open|read)|lob)|a(?:rray_(?:u(?:intersect(?:_u?assoc)?|diff(?:_u?assoc)?)|intersect_u(?:assoc|key)|diff_u(?:assoc|key)|filter|reduce|map)|ssert(?:_options)?|lert|tob)|h(?:tml(?:specialchars(?:_decode)?|_entity_decode|entities)|(?:ash(?:_(?:update|hmac))?|ighlight)_file|e(?:ader_register_callback|x2bin))|f(?:i(?:le(?:(?:[acm]tim|inod)e|(?:_exist|perm)s|group)?|nfo_open)|tp_(?:nb_(?:ge|pu)|connec|ge|pu)t|(?:unction_exis|pu)ts|write|open)|o(?:b_(?:get_(?:c(?:ontents|lean)|flush)|end_(?:clean|flush)|clean|flush|start)|dbc_(?:result(?:_all)?|exec(?:ute)?|connect)|pendir)|m(?:b_(?:ereg(?:_(?:replace(?:_callback)?|match)|i(?:_replace)?)?|parse_str)|(?:ove_uploaded|d5)_file|ethod_exists|ysql_query|kdir)|e(?:x(?:if_(?:t(?:humbnail|agname)|imagetype|read_data)|ec)|scapeshell(?:arg|cmd)|rror_reporting|val)|c(?:url_(?:file_create|exec|init)|onvert_uuencode|reate_function|hr)|u(?:n(?:serialize|pack)|rl(?:de|en)code|[ak]?sort)|b(?:(?:son_(?:de|en)|ase64_en)code|zopen|toa)|(?:json_(?:de|en)cod|debug_backtrac|tmpfil)e|var_dump)(?:\\s|/\\*.*\\*/|//.*|#.*|\\\")*\\(.*\\)",
2697
+ "regex": "\\b(?:s(?:e(?:t(?:_(?:e(?:xception|rror)_handler|magic_quotes_runtime|include_path)|defaultstub)|ssion_s(?:et_save_handler|tart))|qlite_(?:(?:(?:unbuffered|single|array)_)?query|create_(?:aggregate|function)|p?open|exec)|tr(?:eam_(?:context_create|socket_client)|ipc?slashes|rev)|implexml_load_(?:string|file)|ocket_c(?:onnect|reate)|h(?:ow_sourc|a1_fil)e|pl_autoload_register|ystem)|p(?:r(?:eg_(?:replace(?:_callback(?:_array)?)?|match(?:_all)?|split)|oc_(?:(?:terminat|clos|nic)e|get_status|open)|int_r)|o(?:six_(?:get(?:(?:e[gu]|g)id|login|pwnam)|mk(?:fifo|nod)|ttyname|kill)|pen)|hp(?:_(?:strip_whitespac|unam)e|version|info)|g_(?:(?:execut|prepar)e|connect|query)|a(?:rse_(?:ini_file|str)|ssthru)|utenv)|r(?:unkit_(?:function_(?:re(?:defin|nam)e|copy|add)|method_(?:re(?:defin|nam)e|copy|add)|constant_(?:redefine|add))|e(?:(?:gister_(?:shutdown|tick)|name)_function|ad(?:(?:gz)?file|_exif_data|dir))|awurl(?:de|en)code)|i(?:mage(?:createfrom(?:(?:jpe|pn)g|x[bp]m|wbmp|gif)|(?:jpe|pn)g|g(?:d2?|if)|2?wbmp|xbm)|s_(?:(?:(?:execut|write?|read)ab|fi)le|dir)|ni_(?:get(?:_all)?|set)|terator_apply|ptcembed)|g(?:et(?:_(?:c(?:urrent_use|fg_va)r|meta_tags)|my(?:[gpu]id|inode)|(?:lastmo|cw)d|imagesize|env)|z(?:(?:(?:defla|wri)t|encod|fil)e|compress|open|read)|lob)|a(?:rray_(?:u(?:intersect(?:_u?assoc)?|diff(?:_u?assoc)?)|intersect_u(?:assoc|key)|diff_u(?:assoc|key)|filter|reduce|map)|ssert(?:_options)?|lert|tob)|h(?:tml(?:specialchars(?:_decode)?|_entity_decode|entities)|(?:ash(?:_(?:update|hmac))?|ighlight)_file|e(?:ader_register_callback|x2bin))|f(?:i(?:le(?:(?:[acm]tim|inod)e|(?:_exist|perm)s|group)?|nfo_open)|tp_(?:nb_(?:ge|pu)|connec|ge|pu)t|(?:unction_exis|pu)ts|write|open)|o(?:b_(?:get_(?:c(?:ontents|lean)|flush)|end_(?:clean|flush)|clean|flush|start)|dbc_(?:result(?:_all)?|exec(?:ute)?|connect)|pendir)|m(?:b_(?:ereg(?:_(?:replace(?:_callback)?|match)|i(?:_replace)?)?|parse_str)|(?:ove_uploaded|d5)_file|ethod_exists|ysql_query|kdir)|e(?:x(?:if_(?:t(?:humbnail|agname)|imagetype|read_data)|ec)|scapeshell(?:arg|cmd)|rror_reporting|val)|c(?:url_(?:file_create|exec|init)|onvert_uuencode|reate_function|hr)|u(?:n(?:serialize|pack)|rl(?:de|en)code|[ak]?sort)|b(?:(?:son_(?:de|en)|ase64_en)code|zopen|toa)|(?:json_(?:de|en)cod|debug_backtrac|tmpfil)e|var_dump)(?:\\s|/\\*.*\\*/|//.*|#.*|\\\"|')*\\((?:(?:\\s|/\\*.*\\*/|//.*|#.*)*(?:\\$\\w+|[A-Z\\d]\\w*|\\w+\\(.*\\)|\\\\?\"(?:[^\"]|\\\\\"|\"\"|\"\\+\")*\\\\?\"|\\\\?'(?:[^']|''|'\\+')*\\\\?')(?:\\s|/\\*.*\\*/|//.*|#.*)*(?:(?:::|\\.|->)(?:\\s|/\\*.*\\*/|//.*|#.*)*\\w+(?:\\(.*\\))?)?,)*(?:(?:\\s|/\\*.*\\*/|//.*|#.*)*(?:\\$\\w+|[A-Z\\d]\\w*|\\w+\\(.*\\)|\\\\?\"(?:[^\"]|\\\\\"|\"\"|\"\\+\")*\\\\?\"|\\\\?'(?:[^']|''|'\\+')*\\\\?')(?:\\s|/\\*.*\\*/|//.*|#.*)*(?:(?:::|\\.|->)(?:\\s|/\\*.*\\*/|//.*|#.*)*\\w+(?:\\(.*\\))?)?)?\\)",
2698
2698
  "options": {
2699
+ "case_sensitive": true,
2699
2700
  "min_length": 5
2700
2701
  }
2701
2702
  },
@@ -3524,7 +3525,7 @@
3524
3525
  "address": "grpc.server.request.message"
3525
3526
  }
3526
3527
  ],
3527
- "regex": "\\b(?i:eval|settimeout|setinterval|new\\s+Function)\\s*\\(",
3528
+ "regex": "\\b(?i:eval|settimeout|setinterval|new\\s+Function|alert|prompt)\\s*\\([^\\)]",
3528
3529
  "options": {
3529
3530
  "case_sensitive": true,
3530
3531
  "min_length": 5
@@ -3770,7 +3771,7 @@
3770
3771
  "address": "grpc.server.request.message"
3771
3772
  }
3772
3773
  ],
3773
- "regex": "(?i:(?:\\[?\\$(?:(?:s(?:lic|iz)|wher)e|e(?:lemMatch|xists|q)|n(?:o[rt]|in?|e)|l(?:ike|te?)|t(?:ext|ype)|a(?:ll|nd)|jsonSchema|between|regex|x?or|div|mod)\\]?))",
3774
+ "regex": "(?i:(?:\\[?\\$(?:(?:s(?:lic|iz)|wher)e|e(?:lemMatch|xists|q)|n(?:o[rt]|in?|e)|l(?:ike|te?)|t(?:ext|ype)|a(?:ll|nd)|jsonSchema|between|regex|x?or|div|mod)\\]?)\\b)",
3774
3775
  "options": {
3775
3776
  "case_sensitive": true,
3776
3777
  "min_length": 3
@@ -3808,7 +3809,7 @@
3808
3809
  "address": "grpc.server.request.message"
3809
3810
  }
3810
3811
  ],
3811
- "regex": "(?:^[\\W\\d]+\\s*?(?:alter\\s*(?:a(?:(?:pplication\\s*rol|ggregat)e|s(?:ymmetric\\s*ke|sembl)y|u(?:thorization|dit)|vailability\\s*group)|c(?:r(?:yptographic\\s*provider|edential)|o(?:l(?:latio|um)|nversio)n|ertificate|luster)|s(?:e(?:rv(?:ice|er)|curity|quence|ssion|arch)|y(?:mmetric\\s*key|nonym)|togroup|chema)|m(?:a(?:s(?:ter\\s*key|k)|terialized)|e(?:ssage\\s*type|thod)|odule)|l(?:o(?:g(?:file\\s*group|in)|ckdown)|a(?:ngua|r)ge|ibrary)|t(?:(?:abl(?:espac)?|yp)e|r(?:igger|usted)|hreshold|ext)|p(?:a(?:rtition|ckage)|ro(?:cedur|fil)e|ermission)|d(?:i(?:mension|skgroup)|atabase|efault|omain)|r(?:o(?:l(?:lback|e)|ute)|e(?:sourc|mot)e)|f(?:u(?:lltext|nction)|lashback|oreign)|e(?:xte(?:nsion|rnal)|(?:ndpoi|ve)nt)|in(?:dex(?:type)?|memory|stance)|b(?:roker\\s*priority|ufferpool)|x(?:ml\\s*schema|srobject)|w(?:ork(?:load)?|rapper)|hi(?:erarchy|stogram)|o(?:perator|utline)|(?:nicknam|queu)e|us(?:age|er)|group|java|view)\\b|(?:(?:(?:trunc|cre)at|renam)e|d(?:e(?:lete|sc)|rop)|(?:inser|selec)t|load)\\s+\\w+|u(?:nion\\s*(?:(?:distin|sele)ct|all)\\b|pdate\\s+\\w+))|\\b(?:(?:(?:(?:trunc|cre|upd)at|renam)e|(?:inser|selec)t|de(?:lete|sc)|alter|load)\\s+(?:group_concat|load_file|char)\\b\\s*\\(?|end\\s*?\\);)|[\\\"'`\\w]\\s+as\\b\\s*[\\\"'`\\w]+\\s*\\bfrom|[\\s(?:]load_file\\s*?\\(|[\\\"'`]\\s+regexp\\W)",
3812
+ "regex": "(?:^[\\W\\d]+\\s*?(?:alter\\s*(?:a(?:(?:pplication\\s*rol|ggregat)e|s(?:ymmetric\\s*ke|sembl)y|u(?:thorization|dit)|vailability\\s*group)|c(?:r(?:yptographic\\s*provider|edential)|o(?:l(?:latio|um)|nversio)n|ertificate|luster)|s(?:e(?:rv(?:ice|er)|curity|quence|ssion|arch)|y(?:mmetric\\s*key|nonym)|togroup|chema)|m(?:a(?:s(?:ter\\s*key|k)|terialized)|e(?:ssage\\s*type|thod)|odule)|l(?:o(?:g(?:file\\s*group|in)|ckdown)|a(?:ngua|r)ge|ibrary)|t(?:(?:abl(?:espac)?|yp)e|r(?:igger|usted)|hreshold|ext)|p(?:a(?:rtition|ckage)|ro(?:cedur|fil)e|ermission)|d(?:i(?:mension|skgroup)|atabase|efault|omain)|r(?:o(?:l(?:lback|e)|ute)|e(?:sourc|mot)e)|f(?:u(?:lltext|nction)|lashback|oreign)|e(?:xte(?:nsion|rnal)|(?:ndpoi|ve)nt)|in(?:dex(?:type)?|memory|stance)|b(?:roker\\s*priority|ufferpool)|x(?:ml\\s*schema|srobject)|w(?:ork(?:load)?|rapper)|hi(?:erarchy|stogram)|o(?:perator|utline)|(?:nicknam|queu)e|us(?:age|er)|group|java|view)|union\\s*(?:(?:distin|sele)ct|all))\\b|\\b(?:(?:(?:trunc|cre|upd)at|renam)e|(?:inser|selec)t|de(?:lete|sc)|alter|load)\\s+(?:group_concat|load_file|char)\\b\\s*\\(?|[\\s(]load_file\\s*?\\(|[\\\"'`]\\s+regexp\\W)",
3812
3813
  "options": {
3813
3814
  "min_length": 5
3814
3815
  }
@@ -4177,7 +4178,7 @@
4177
4178
  "address": "grpc.server.request.message"
4178
4179
  }
4179
4180
  ],
4180
- "regex": "[#%$]{[^}]+[^\\w\\s][^}]+}",
4181
+ "regex": "[#%$]{(?:[^}]+[^\\w\\s}\\-_][^}]+|\\d+-\\d+)}",
4181
4182
  "options": {
4182
4183
  "case_sensitive": true
4183
4184
  }
@@ -4352,6 +4353,38 @@
4352
4353
  ],
4353
4354
  "transformers": []
4354
4355
  },
4356
+ {
4357
+ "id": "dog-931-001",
4358
+ "name": "RFI: URL Payload to well known RFI target",
4359
+ "tags": {
4360
+ "type": "rfi",
4361
+ "category": "attack_attempt"
4362
+ },
4363
+ "conditions": [
4364
+ {
4365
+ "parameters": {
4366
+ "inputs": [
4367
+ {
4368
+ "address": "server.request.query"
4369
+ },
4370
+ {
4371
+ "address": "server.request.body"
4372
+ },
4373
+ {
4374
+ "address": "server.request.path_params"
4375
+ }
4376
+ ],
4377
+ "regex": "^(?i:file|ftps?|https?).*/rfiinc\\.txt\\?+$",
4378
+ "options": {
4379
+ "case_sensitive": true,
4380
+ "min_length": 17
4381
+ }
4382
+ },
4383
+ "operator": "match_regex"
4384
+ }
4385
+ ],
4386
+ "transformers": []
4387
+ },
4355
4388
  {
4356
4389
  "id": "nfd-000-001",
4357
4390
  "name": "Detect common directory discovery scans",
@@ -5160,7 +5193,7 @@
5160
5193
  "address": "grpc.server.request.message"
5161
5194
  }
5162
5195
  ],
5163
- "regex": "^(jar:)?(http|https):\\/\\/([0-9oq]{1,5}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}|[0-9]{1,10}|localhost)(:[0-9]{1,5})?(\\/.*|)$"
5196
+ "regex": "^(jar:)?(http|https):\\/\\/([0-9oq]{1,5}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}|[0-9]{1,10})(:[0-9]{1,5})?(\\/.*|)$"
5164
5197
  },
5165
5198
  "operator": "match_regex"
5166
5199
  }
@@ -6417,6 +6450,40 @@
6417
6450
  ],
6418
6451
  "transformers": []
6419
6452
  },
6453
+ {
6454
+ "id": "ua0-600-56x",
6455
+ "name": "Datadog test scanner - blocking version: user-agent",
6456
+ "tags": {
6457
+ "type": "security_scanner",
6458
+ "category": "attack_attempt"
6459
+ },
6460
+ "conditions": [
6461
+ {
6462
+ "parameters": {
6463
+ "inputs": [
6464
+ {
6465
+ "address": "server.request.headers.no_cookies",
6466
+ "key_path": [
6467
+ "user-agent"
6468
+ ]
6469
+ },
6470
+ {
6471
+ "address": "grpc.server.request.metadata",
6472
+ "key_path": [
6473
+ "dd-canary"
6474
+ ]
6475
+ }
6476
+ ],
6477
+ "regex": "^dd-test-scanner-log-block$"
6478
+ },
6479
+ "operator": "match_regex"
6480
+ }
6481
+ ],
6482
+ "transformers": [],
6483
+ "on_match": [
6484
+ "block"
6485
+ ]
6486
+ },
6420
6487
  {
6421
6488
  "id": "ua0-600-5xx",
6422
6489
  "name": "Blind SQL Injection Brute Forcer",
@@ -23,7 +23,7 @@ function enable (config) {
23
23
  }
24
24
 
25
25
  if (shouldEnable) {
26
- require('..').enable(config)
26
+ require('..').enableAsync(config).catch(() => {})
27
27
  } else {
28
28
  require('..').disable()
29
29
  }
@@ -34,6 +34,7 @@ function enable (config) {
34
34
 
35
35
  function enableAsmData (appsecConfig) {
36
36
  if (rc && appsecConfig && appsecConfig.rules === undefined) {
37
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_IP_BLOCKING, true)
37
38
  rc.on('ASM_DATA', _asmDataListener)
38
39
  }
39
40
  }
@@ -9,7 +9,6 @@ const log = require('../../log')
9
9
 
10
10
  const clientId = uuid()
11
11
 
12
- const POLL_INTERVAL = 5e3
13
12
  const DEFAULT_CAPABILITY = Buffer.alloc(1).toString('base64') // 0x00
14
13
 
15
14
  // There MUST NOT exist separate instances of RC clients in a tracer making separate ClientGetConfigsRequest
@@ -18,7 +17,8 @@ class RemoteConfigManager extends EventEmitter {
18
17
  constructor (config) {
19
18
  super()
20
19
 
21
- this.scheduler = new Scheduler((cb) => this.poll(cb), POLL_INTERVAL)
20
+ const pollInterval = config.remoteConfig.pollInterval * 1000
21
+ this.scheduler = new Scheduler((cb) => this.poll(cb), pollInterval)
22
22
 
23
23
  this.requestOptions = {
24
24
  url: config.url,
@@ -0,0 +1,99 @@
1
+ <!-- Sorry, you’ve been blocked -->
2
+ <!DOCTYPE html>
3
+ <html lang="en">
4
+
5
+ <head>
6
+ <meta charset="UTF-8">
7
+ <meta name="viewport" content="width=device-width,initial-scale=1">
8
+ <title>You've been blocked</title>
9
+ <style>
10
+ a,
11
+ body,
12
+ div,
13
+ html,
14
+ span {
15
+ margin: 0;
16
+ padding: 0;
17
+ border: 0;
18
+ font-size: 100%;
19
+ font: inherit;
20
+ vertical-align: baseline
21
+ }
22
+
23
+ body {
24
+ background: -webkit-radial-gradient(26% 19%, circle, #fff, #f4f7f9);
25
+ background: radial-gradient(circle at 26% 19%, #fff, #f4f7f9);
26
+ display: -webkit-box;
27
+ display: -ms-flexbox;
28
+ display: flex;
29
+ -webkit-box-pack: center;
30
+ -ms-flex-pack: center;
31
+ justify-content: center;
32
+ -webkit-box-align: center;
33
+ -ms-flex-align: center;
34
+ align-items: center;
35
+ -ms-flex-line-pack: center;
36
+ align-content: center;
37
+ width: 100%;
38
+ min-height: 100vh;
39
+ line-height: 1;
40
+ flex-direction: column
41
+ }
42
+
43
+ p {
44
+ display: block
45
+ }
46
+
47
+
48
+ main {
49
+ text-align: center;
50
+ flex: 1;
51
+ display: -webkit-box;
52
+ display: -ms-flexbox;
53
+ display: flex;
54
+ -webkit-box-pack: center;
55
+ -ms-flex-pack: center;
56
+ justify-content: center;
57
+ -webkit-box-align: center;
58
+ -ms-flex-align: center;
59
+ align-items: center;
60
+ -ms-flex-line-pack: center;
61
+ align-content: center;
62
+ flex-direction: column
63
+ }
64
+
65
+ p {
66
+ font-size: 18px;
67
+ line-height: normal;
68
+ color: #646464;
69
+ font-family: sans-serif;
70
+ font-weight: 400
71
+ }
72
+
73
+ a {
74
+ color: #4842b7
75
+ }
76
+
77
+ footer {
78
+ width: 100%;
79
+ text-align: center
80
+ }
81
+
82
+ footer p {
83
+ font-size: 16px
84
+ }
85
+ </style>
86
+ </head>
87
+
88
+ <body>
89
+ <main>
90
+ <p>Sorry, you cannot access this page. Please contact the customer service team.</p>
91
+ </main>
92
+ <footer>
93
+ <p>Security provided by <a
94
+ href="https://www.datadoghq.com/product/security-platform/application-security-monitoring/"
95
+ target="_blank">Datadog</a></p>
96
+ </footer>
97
+ </body>
98
+
99
+ </html>
@@ -0,0 +1,8 @@
1
+ {
2
+ "errors": [
3
+ {
4
+ "title": "You've been blocked",
5
+ "detail": "Sorry, you cannot access this page. Please contact the customer service team. Security provided by Datadog."
6
+ }
7
+ ]
8
+ }
@@ -57,6 +57,10 @@ class AgentProxyCiVisibilityExporter extends CiVisibilityExporter {
57
57
  this.exportUncodedCoverages()
58
58
  })
59
59
  }
60
+
61
+ setUrl (url, coverageUrl) {
62
+ this._setUrl(url, coverageUrl)
63
+ }
60
64
  }
61
65
 
62
66
  module.exports = AgentProxyCiVisibilityExporter
@@ -4,6 +4,7 @@ const URL = require('url').URL
4
4
  const Writer = require('./writer')
5
5
  const CoverageWriter = require('./coverage-writer')
6
6
  const CiVisibilityExporter = require('../ci-visibility-exporter')
7
+ const log = require('../../../log')
7
8
 
8
9
  class AgentlessCiVisibilityExporter extends CiVisibilityExporter {
9
10
  constructor (config) {
@@ -19,11 +20,26 @@ class AgentlessCiVisibilityExporter extends CiVisibilityExporter {
19
20
  this._coverageUrl = url || new URL(`https://event-platform-intake.${site}`)
20
21
  this._coverageWriter = new CoverageWriter({ url: this._coverageUrl })
21
22
 
23
+ this._apiUrl = url || new URL(`https://api.${site}`)
24
+
22
25
  if (isGitUploadEnabled) {
23
- const gitUrl = url || new URL(`https://api.${site}`)
24
- this.sendGitMetadata({ url: gitUrl })
26
+ this.sendGitMetadata({ url: this._getApiUrl() })
27
+ }
28
+ }
29
+
30
+ setUrl (url, coverageUrl = url, apiUrl = url) {
31
+ this._setUrl(url, coverageUrl)
32
+ try {
33
+ apiUrl = new URL(apiUrl)
34
+ this._apiUrl = apiUrl
35
+ } catch (e) {
36
+ log.error(e)
25
37
  }
26
38
  }
39
+
40
+ _getApiUrl () {
41
+ return this._apiUrl
42
+ }
27
43
  }
28
44
 
29
45
  module.exports = AgentlessCiVisibilityExporter
@@ -63,9 +63,7 @@ class CiVisibilityExporter extends AgentInfoExporter {
63
63
  }
64
64
 
65
65
  canReportCodeCoverage () {
66
- return this._canUseCiVisProtocol &&
67
- this._itrConfig &&
68
- this._itrConfig.isCodeCoverageEnabled
66
+ return this._canUseCiVisProtocol
69
67
  }
70
68
 
71
69
  // We can't call the skippable endpoint until git upload has finished,
@@ -79,7 +77,7 @@ class CiVisibilityExporter extends AgentInfoExporter {
79
77
  return callback(gitUploadError, [])
80
78
  }
81
79
  const configuration = {
82
- url: this._url,
80
+ url: this._getApiUrl(),
83
81
  site: this._config.site,
84
82
  env: this._config.env,
85
83
  service: this._config.service,
@@ -103,13 +101,17 @@ class CiVisibilityExporter extends AgentInfoExporter {
103
101
  return callback(null, {})
104
102
  }
105
103
  const configuration = {
106
- url: this._url,
104
+ url: this._getApiUrl(),
107
105
  env: this._config.env,
108
106
  service: this._config.service,
109
107
  isEvpProxy: !!this._isUsingEvpProxy,
110
108
  ...testConfiguration
111
109
  }
112
110
  getItrConfigurationRequest(configuration, (err, itrConfig) => {
111
+ /**
112
+ * **Important**: this._itrConfig remains empty in testing frameworks
113
+ * where the tests run in a subprocess, because `getItrConfiguration` is called only once.
114
+ */
113
115
  this._itrConfig = itrConfig
114
116
  callback(err, itrConfig)
115
117
  })
@@ -179,7 +181,7 @@ class CiVisibilityExporter extends AgentInfoExporter {
179
181
  this._coverageBuffer = []
180
182
  }
181
183
 
182
- setUrl (url, coverageUrl = url) {
184
+ _setUrl (url, coverageUrl = url) {
183
185
  try {
184
186
  url = new URL(url)
185
187
  coverageUrl = new URL(coverageUrl)
@@ -191,6 +193,10 @@ class CiVisibilityExporter extends AgentInfoExporter {
191
193
  log.error(e)
192
194
  }
193
195
  }
196
+
197
+ _getApiUrl () {
198
+ return this._url
199
+ }
194
200
  }
195
201
 
196
202
  module.exports = CiVisibilityExporter