dd-trace 2.24.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 (38) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/index.d.ts +16 -0
  3. package/package.json +6 -4
  4. package/packages/datadog-instrumentations/src/helpers/register.js +7 -0
  5. package/packages/datadog-instrumentations/src/mocha.js +33 -8
  6. package/packages/datadog-instrumentations/src/pg.js +6 -1
  7. package/packages/datadog-plugin-http/src/client.js +1 -1
  8. package/packages/datadog-plugin-jest/src/index.js +2 -2
  9. package/packages/datadog-plugin-mocha/src/index.js +2 -2
  10. package/packages/datadog-plugin-pg/src/index.js +1 -1
  11. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +6 -6
  12. package/packages/dd-trace/src/appsec/iast/index.js +8 -3
  13. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +20 -1
  14. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +6 -1
  15. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +24 -6
  16. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +63 -41
  17. package/packages/dd-trace/src/appsec/recommended.json +75 -8
  18. package/packages/dd-trace/src/appsec/remote_config/manager.js +2 -2
  19. package/packages/dd-trace/src/config.js +24 -5
  20. package/packages/dd-trace/src/exporters/common/request.js +33 -1
  21. package/packages/dd-trace/src/format.js +5 -1
  22. package/packages/dd-trace/src/lambda/handler.js +72 -0
  23. package/packages/dd-trace/src/lambda/index.js +5 -0
  24. package/packages/dd-trace/src/lambda/runtime/errors.js +20 -0
  25. package/packages/dd-trace/src/lambda/runtime/patch.js +74 -0
  26. package/packages/dd-trace/src/lambda/runtime/ritm.js +143 -0
  27. package/packages/dd-trace/src/plugin_manager.js +4 -0
  28. package/packages/dd-trace/src/plugins/ci_plugin.js +6 -0
  29. package/packages/dd-trace/src/plugins/database.js +4 -4
  30. package/packages/dd-trace/src/plugins/log_plugin.js +2 -2
  31. package/packages/dd-trace/src/plugins/util/ci.js +5 -2
  32. package/packages/dd-trace/src/plugins/util/test.js +2 -2
  33. package/packages/dd-trace/src/plugins/util/user-provided-git.js +14 -1
  34. package/packages/dd-trace/src/priority_sampler.js +6 -2
  35. package/packages/dd-trace/src/proxy.js +4 -3
  36. package/packages/dd-trace/src/ritm.js +7 -1
  37. package/packages/dd-trace/src/span_processor.js +13 -0
  38. package/packages/dd-trace/src/span_sampler.js +1 -4
@@ -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",
@@ -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,
@@ -142,7 +142,12 @@ class Config {
142
142
  process.env.AWS_LAMBDA_FUNCTION_NAME ||
143
143
  pkg.name ||
144
144
  'node'
145
- const DD_SERVICE_MAPPING = process.env.DD_SERVICE_MAPPING || ''
145
+ const DD_SERVICE_MAPPING = coalesce(
146
+ options.serviceMapping,
147
+ process.env.DD_SERVICE_MAPPING ? fromEntries(
148
+ process.env.DD_SERVICE_MAPPING.split(',').map(x => x.trim().split(':'))
149
+ ) : {}
150
+ )
146
151
  const DD_ENV = coalesce(
147
152
  options.env,
148
153
  process.env.DD_ENV,
@@ -271,6 +276,19 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
271
276
  path.join(__dirname, 'appsec', 'templates', 'blocked.json')
272
277
  )
273
278
 
279
+ const inAWSLambda = process.env.AWS_LAMBDA_FUNCTION_NAME !== undefined
280
+
281
+ const remoteConfigOptions = options.remoteConfig || {}
282
+ const DD_REMOTE_CONFIGURATION_ENABLED = coalesce(
283
+ process.env.DD_REMOTE_CONFIGURATION_ENABLED && isTrue(process.env.DD_REMOTE_CONFIGURATION_ENABLED),
284
+ !inAWSLambda
285
+ )
286
+ const DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS = coalesce(
287
+ parseInt(remoteConfigOptions.pollInterval),
288
+ parseInt(process.env.DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS),
289
+ 5 // seconds
290
+ )
291
+
274
292
  const iastOptions = options.experimental && options.experimental.iast
275
293
  const DD_IAST_ENABLED = coalesce(
276
294
  iastOptions &&
@@ -336,7 +354,6 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
336
354
  })
337
355
  }
338
356
 
339
- const inAWSLambda = process.env.AWS_LAMBDA_FUNCTION_NAME !== undefined
340
357
  const defaultFlushInterval = inAWSLambda ? 0 : 2000
341
358
 
342
359
  this.tracing = !isFalse(DD_TRACING_ENABLED)
@@ -355,9 +372,7 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
355
372
  this.clientIpHeader = DD_TRACE_CLIENT_IP_HEADER
356
373
  this.plugins = !!coalesce(options.plugins, true)
357
374
  this.service = DD_SERVICE
358
- this.serviceMapping = DD_SERVICE_MAPPING.length ? fromEntries(
359
- DD_SERVICE_MAPPING.split(',').map(x => x.trim().split(':'))
360
- ) : {}
375
+ this.serviceMapping = DD_SERVICE_MAPPING
361
376
  this.version = DD_VERSION
362
377
  this.dogstatsd = {
363
378
  hostname: coalesce(dogstatsd.hostname, process.env.DD_DOGSTATSD_HOSTNAME, this.hostname),
@@ -395,6 +410,10 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
395
410
  blockedTemplateHtml: DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML,
396
411
  blockedTemplateJson: DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON
397
412
  }
413
+ this.remoteConfig = {
414
+ enabled: DD_REMOTE_CONFIGURATION_ENABLED,
415
+ pollInterval: DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS
416
+ }
398
417
  this.iast = {
399
418
  enabled: isTrue(DD_IAST_ENABLED),
400
419
  requestSampling: DD_IAST_REQUEST_SAMPLING,
@@ -20,16 +20,48 @@ const containerId = docker.id()
20
20
 
21
21
  let activeRequests = 0
22
22
 
23
+ // TODO: Replace with `url.urlToHttpOptions` when supported by all versions
24
+ function urlToOptions (url) {
25
+ const agent = url.agent || http.globalAgent
26
+ const options = {
27
+ protocol: url.protocol || agent.protocol,
28
+ hostname: typeof url.hostname === 'string' && url.hostname.startsWith('[')
29
+ ? url.hostname.slice(1, -1)
30
+ : url.hostname ||
31
+ url.host ||
32
+ 'localhost',
33
+ hash: url.hash,
34
+ search: url.search,
35
+ pathname: url.pathname,
36
+ path: `${url.pathname || ''}${url.search || ''}`,
37
+ href: url.href
38
+ }
39
+ if (url.port !== '') {
40
+ options.port = Number(url.port)
41
+ }
42
+ if (url.username || url.password) {
43
+ options.auth = `${url.username}:${url.password}`
44
+ }
45
+ return options
46
+ }
47
+
48
+ function fromUrlString (url) {
49
+ return typeof urlToHttpOptions === 'function'
50
+ ? urlToOptions(new URL(url))
51
+ : urlParse(url)
52
+ }
53
+
23
54
  function request (data, options, callback) {
24
55
  if (!options.headers) {
25
56
  options.headers = {}
26
57
  }
27
58
 
28
59
  if (options.url) {
29
- const url = typeof options.url === 'object' ? options.url : urlParse(options.url)
60
+ const url = typeof options.url === 'object' ? options.url : fromUrlString(options.url)
30
61
  if (url.protocol === 'unix:') {
31
62
  options.socketPath = url.pathname
32
63
  } else {
64
+ if (!options.path) options.path = url.path
33
65
  options.protocol = url.protocol
34
66
  options.hostname = url.hostname
35
67
  options.port = url.port
@@ -175,7 +175,7 @@ function addTag (meta, metrics, key, value, nested) {
175
175
  metrics[key] = value.toString()
176
176
  } else if (!Array.isArray(value) && !nested) {
177
177
  for (const prop in value) {
178
- if (!value.hasOwnProperty(prop)) continue
178
+ if (!hasOwn(value, prop)) continue
179
179
 
180
180
  addTag(meta, metrics, `${key}.${prop}`, value[prop], true)
181
181
  }
@@ -185,6 +185,10 @@ function addTag (meta, metrics, key, value, nested) {
185
185
  }
186
186
  }
187
187
 
188
+ function hasOwn (object, prop) {
189
+ return Object.prototype.hasOwnProperty.call(object, prop)
190
+ }
191
+
188
192
  function isNodeBuffer (obj) {
189
193
  return obj.constructor && obj.constructor.name === 'Buffer' &&
190
194
  typeof obj.readInt8 === 'function' &&
@@ -0,0 +1,72 @@
1
+ 'use strict'
2
+
3
+ const { channel } = require('../../../datadog-instrumentations/src/helpers/instrument')
4
+ const { ERROR_MESSAGE, ERROR_TYPE } = require('../constants')
5
+ const { ImpendingTimeout } = require('./runtime/errors')
6
+
7
+ const globalTracer = global._ddtrace
8
+ const tracer = globalTracer._tracer
9
+ const timeoutChannel = channel('apm:aws:lambda:timeout')
10
+ // Always crash the flushes when a message is received
11
+ // from this channel.
12
+ timeoutChannel.subscribe(_ => {
13
+ crashFlush()
14
+ })
15
+
16
+ let __lambdaTimeout
17
+
18
+ /**
19
+ * Publishes to the `apm:aws:lambda:timeout` channel when
20
+ * the AWS Lambda run time is about to end.
21
+ *
22
+ * @param {*} context AWS Lambda context object.
23
+ */
24
+ function checkTimeout (context) {
25
+ let remainingTimeInMillis = context.getRemainingTimeInMillis()
26
+ const apmFlushDeadline = parseInt(process.env.DD_APM_FLUSH_DEADLINE)
27
+ if (apmFlushDeadline && apmFlushDeadline <= remainingTimeInMillis) {
28
+ remainingTimeInMillis = apmFlushDeadline
29
+ }
30
+
31
+ __lambdaTimeout = setTimeout(() => {
32
+ timeoutChannel.publish(undefined)
33
+ }, remainingTimeInMillis - 50)
34
+ }
35
+
36
+ /**
37
+ * Grabs the current span, adds an error for an impending timeout.
38
+ *
39
+ * After that, it calls `killAll` on the tracer processor
40
+ * in order to kill remaining unfinished spans.
41
+ *
42
+ * Once that is done, it finishes the last span.
43
+ */
44
+ function crashFlush () {
45
+ const activeSpan = tracer.scope().active()
46
+ const error = new ImpendingTimeout('Datadog detected an impending timeout')
47
+ activeSpan.addTags({
48
+ [ERROR_MESSAGE]: error.message,
49
+ [ERROR_TYPE]: error.name
50
+ })
51
+ tracer._processor.killAll()
52
+ activeSpan.finish()
53
+ }
54
+
55
+ /**
56
+ * Patches your AWS Lambda handler function to add some tracing support.
57
+ *
58
+ * @param {*} lambdaHandler a Lambda handler function.
59
+ */
60
+ exports.datadog = function datadog (lambdaHandler) {
61
+ return (...args) => {
62
+ const context = args[1]
63
+ const patched = lambdaHandler.apply(this, args)
64
+ checkTimeout(context)
65
+
66
+ if (patched) {
67
+ // clear the timeout as soon as a result is returned
68
+ patched.then(_ => clearTimeout(__lambdaTimeout))
69
+ }
70
+ return patched
71
+ }
72
+ }
@@ -0,0 +1,5 @@
1
+ 'use strict'
2
+
3
+ const { registerLambdaHook } = require('./runtime/ritm')
4
+
5
+ registerLambdaHook()
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ *
4
+ * Defines custom error types throwable by the runtime.
5
+ */
6
+ 'use strict'
7
+
8
+ class ExtendedError extends Error {
9
+ constructor (reason) {
10
+ super(reason)
11
+ Object.setPrototypeOf(this, new.target.prototype)
12
+ }
13
+ }
14
+
15
+ class ImpendingTimeout extends ExtendedError {}
16
+ ImpendingTimeout.prototype.name = 'Impending Timeout'
17
+
18
+ module.exports = {
19
+ ImpendingTimeout
20
+ }
@@ -0,0 +1,74 @@
1
+ 'use strict'
2
+
3
+ const path = require('path')
4
+
5
+ const { _extractModuleNameAndHandlerPath, _extractModuleRootAndHandler, _getLambdaFilePath } = require('./ritm')
6
+ const { datadog } = require('../handler')
7
+ const { addHook } = require('../../../../datadog-instrumentations/src/helpers/instrument')
8
+ const shimmer = require('../../../../datadog-shimmer')
9
+
10
+ /**
11
+ * Patches a Datadog Lambda module by calling `patchDatadogLambdaHandler`
12
+ * with the handler name `datadog`.
13
+ *
14
+ * @param {*} datadogLambdaModule node module to be patched.
15
+ * @returns a Datadog Lambda module with the `datadog` function from
16
+ * `datadog-lambda-js` patched.
17
+ */
18
+ const patchDatadogLambdaModule = (datadogLambdaModule) => {
19
+ shimmer.wrap(datadogLambdaModule, 'datadog', patchDatadogLambdaHandler)
20
+
21
+ return datadogLambdaModule
22
+ }
23
+
24
+ /**
25
+ * Patches a Datadog Lambda handler in order to do
26
+ * Datadog instrumentation by getting the Lambda handler from its
27
+ * arguments.
28
+ *
29
+ * @param {*} datadogHandler the Datadog Lambda handler to destructure.
30
+ * @returns the datadogHandler with its arguments patched.
31
+ */
32
+ function patchDatadogLambdaHandler (datadogHandler) {
33
+ return (userHandler) => {
34
+ return datadogHandler(datadog(userHandler))
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Patches a Lambda module on the given handler path.
40
+ *
41
+ * @param {string} handlerPath path of the handler to be patched.
42
+ * @returns a module with the given handler path patched.
43
+ */
44
+ const patchLambdaModule = (handlerPath) => (lambdaModule) => {
45
+ shimmer.wrap(lambdaModule, handlerPath, patchLambdaHandler)
46
+
47
+ return lambdaModule
48
+ }
49
+
50
+ /**
51
+ * Patches a Lambda handler in order to do Datadog instrumentation.
52
+ *
53
+ * @param {*} lambdaHandler the Lambda handler to be patched.
54
+ * @returns a function which patches the given Lambda handler.
55
+ */
56
+ function patchLambdaHandler (lambdaHandler) {
57
+ return datadog(lambdaHandler)
58
+ }
59
+
60
+ const lambdaTaskRoot = process.env.LAMBDA_TASK_ROOT
61
+ const originalLambdaHandler = process.env.DD_LAMBDA_HANDLER
62
+
63
+ if (originalLambdaHandler !== undefined) {
64
+ const [moduleRoot, moduleAndHandler] = _extractModuleRootAndHandler(originalLambdaHandler)
65
+ const [_module, handlerPath] = _extractModuleNameAndHandlerPath(moduleAndHandler)
66
+
67
+ const lambdaStylePath = path.resolve(lambdaTaskRoot, moduleRoot, _module)
68
+ const lambdaFilePath = _getLambdaFilePath(lambdaStylePath)
69
+
70
+ addHook({ name: lambdaFilePath }, patchLambdaModule(handlerPath))
71
+ } else {
72
+ // Instrumentation is done manually.
73
+ addHook({ name: 'datadog-lambda-js', versions: ['>=6.85.0'] }, patchDatadogLambdaModule)
74
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * Modifications copyright 2022 Datadog, Inc.
4
+ *
5
+ * Some functions are part of aws-lambda-nodejs-runtime-interface-client
6
+ * https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/blob/main/src/utils/UserFunction.ts
7
+ */
8
+ 'use strict'
9
+
10
+ const fs = require('fs')
11
+ const path = require('path')
12
+
13
+ const log = require('../../log')
14
+ const Hook = require('../../../../datadog-instrumentations/src/helpers/hook')
15
+ const instrumentations = require('../../../../datadog-instrumentations/src/helpers/instrumentations')
16
+ const {
17
+ filename,
18
+ getVersion,
19
+ matchVersion,
20
+ pathSepExpr
21
+ } = require('../../../../datadog-instrumentations/src/helpers/register')
22
+
23
+ /**
24
+ * Breaks the full handler string into two pieces: the module root
25
+ * and the actual handler string.
26
+ *
27
+ * @param {string} fullHandler user's lambda handler, commonly stored in `DD_LAMBDA_HANDLER`.
28
+ * @returns {string[]} an array containing the module root and the handler string.
29
+ *
30
+ * ```js
31
+ * _extractModuleRootAndHandler('./api/src/index.nested.handler')
32
+ * // => ['./api/src', 'index.nested.handler']
33
+ * ```
34
+ */
35
+ function _extractModuleRootAndHandler (fullHandler) {
36
+ const handlerString = path.basename(fullHandler)
37
+ const moduleRoot = fullHandler.substring(0, fullHandler.indexOf(handlerString))
38
+
39
+ return [moduleRoot, handlerString]
40
+ }
41
+
42
+ /**
43
+ * Splits the handler string into two pieces: the module name
44
+ * and the path to the handler function.
45
+ *
46
+ * @param {string} handler a handler string containing the module and the handler path.
47
+ * @returns {string[]} an array containing the module name and the handler path.
48
+ *
49
+ * ```js
50
+ * _extractModuleNameAndHandlerPath('index.nested.handler')
51
+ * // => ['index', 'nested.handler']
52
+ * ```
53
+ */
54
+ function _extractModuleNameAndHandlerPath (handler) {
55
+ const FUNCTION_EXPR = /^([^.]*)\.(.*)$/
56
+ const match = handler.match(FUNCTION_EXPR)
57
+ if (!match || match.length !== 3) {
58
+ // Malformed Handler Name
59
+ return // TODO: throw error
60
+ }
61
+ return [match[1], match[2]] // [module, handler-path]
62
+ }
63
+
64
+ /**
65
+ * Returns the correct path of the file to be patched
66
+ * when required.
67
+ *
68
+ * @param {*} lambdaStylePath the path comprised of the `LAMBDA_TASK_ROOT`,
69
+ * the root of the module of the Lambda handler, and the module name.
70
+ * @returns the lambdaStylePath with the appropiate extension for the hook.
71
+ */
72
+ function _getLambdaFilePath (lambdaStylePath) {
73
+ let lambdaFilePath = lambdaStylePath
74
+ if (fs.existsSync(lambdaStylePath + '.js')) {
75
+ lambdaFilePath += '.js'
76
+ } else if (fs.existsSync(lambdaStylePath + '.mjs')) {
77
+ lambdaFilePath += '.mjs'
78
+ } else if (fs.existsSync(lambdaStylePath + '.cjs')) {
79
+ lambdaFilePath += '.cjs'
80
+ }
81
+ return lambdaFilePath
82
+ }
83
+
84
+ /**
85
+ * Register a hook for the Lambda handler to be executed when
86
+ * the file is required.
87
+ */
88
+ const registerLambdaHook = () => {
89
+ const lambdaTaskRoot = process.env.LAMBDA_TASK_ROOT
90
+ const originalLambdaHandler = process.env.DD_LAMBDA_HANDLER
91
+
92
+ if (originalLambdaHandler !== undefined) {
93
+ const [moduleRoot, moduleAndHandler] = _extractModuleRootAndHandler(originalLambdaHandler)
94
+ const [_module] = _extractModuleNameAndHandlerPath(moduleAndHandler)
95
+
96
+ const lambdaStylePath = path.resolve(lambdaTaskRoot, moduleRoot, _module)
97
+ const lambdaFilePath = _getLambdaFilePath(lambdaStylePath)
98
+
99
+ Hook([lambdaFilePath], (moduleExports) => {
100
+ require('./patch')
101
+
102
+ for (const { hook } of instrumentations[lambdaFilePath]) {
103
+ try {
104
+ moduleExports = hook(moduleExports)
105
+ } catch (e) {
106
+ log.error(e)
107
+ }
108
+ }
109
+
110
+ return moduleExports
111
+ })
112
+ } else {
113
+ const moduleToPatch = 'datadog-lambda-js'
114
+ Hook([moduleToPatch], (moduleExports, moduleName, moduleBaseDir) => {
115
+ moduleName = moduleName.replace(pathSepExpr, '/')
116
+
117
+ require('./patch')
118
+
119
+ for (const { name, file, versions, hook } of instrumentations[moduleToPatch]) {
120
+ const fullFilename = filename(name, file)
121
+ if (moduleName === fullFilename) {
122
+ const version = getVersion(moduleBaseDir)
123
+ if (matchVersion(version, versions)) {
124
+ try {
125
+ moduleExports = hook(moduleExports)
126
+ } catch (e) {
127
+ log.error(e)
128
+ }
129
+ }
130
+ }
131
+ }
132
+
133
+ return moduleExports
134
+ })
135
+ }
136
+ }
137
+
138
+ module.exports = {
139
+ _extractModuleRootAndHandler,
140
+ _extractModuleNameAndHandlerPath,
141
+ _getLambdaFilePath,
142
+ registerLambdaHook
143
+ }
@@ -9,6 +9,10 @@ const loadChannel = channel('dd-trace:instrumentation:load')
9
9
 
10
10
  // instrument everything that needs Plugin System V2 instrumentation
11
11
  require('../../datadog-instrumentations')
12
+ if (process.env.AWS_LAMBDA_FUNCTION_NAME !== undefined) {
13
+ // instrument lambda environment
14
+ require('./lambda')
15
+ }
12
16
 
13
17
  const { DD_TRACE_DISABLED_PLUGINS } = process.env
14
18
 
@@ -16,6 +16,9 @@ module.exports = class CiPlugin extends Plugin {
16
16
  super(...args)
17
17
 
18
18
  this.addSub(`ci:${this.constructor.name}:itr-configuration`, ({ onDone }) => {
19
+ if (!this.tracer._exporter || !this.tracer._exporter.getItrConfiguration) {
20
+ return onDone({ err: new Error('CI Visibility was not initialized correctly') })
21
+ }
19
22
  this.tracer._exporter.getItrConfiguration(this.testConfiguration, (err, itrConfig) => {
20
23
  if (!err) {
21
24
  this.itrConfig = itrConfig
@@ -25,6 +28,9 @@ module.exports = class CiPlugin extends Plugin {
25
28
  })
26
29
 
27
30
  this.addSub(`ci:${this.constructor.name}:test-suite:skippable`, ({ onDone }) => {
31
+ if (!this.tracer._exporter || !this.tracer._exporter.getSkippableSuites) {
32
+ return onDone({ err: new Error('CI Visibility was not initialized correctly') })
33
+ }
28
34
  this.tracer._exporter.getSkippableSuites(this.testConfiguration, (err, skippableSuites) => {
29
35
  onDone({ err, skippableSuites })
30
36
  })