dd-trace 3.11.0 → 3.12.1

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 +5 -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 +138 -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,
@@ -145,7 +145,12 @@ class Config {
145
145
  process.env.AWS_LAMBDA_FUNCTION_NAME ||
146
146
  pkg.name ||
147
147
  'node'
148
- const DD_SERVICE_MAPPING = process.env.DD_SERVICE_MAPPING || ''
148
+ const DD_SERVICE_MAPPING = coalesce(
149
+ options.serviceMapping,
150
+ process.env.DD_SERVICE_MAPPING ? fromEntries(
151
+ process.env.DD_SERVICE_MAPPING.split(',').map(x => x.trim().split(':'))
152
+ ) : {}
153
+ )
149
154
  const DD_ENV = coalesce(
150
155
  options.env,
151
156
  process.env.DD_ENV,
@@ -274,6 +279,19 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
274
279
  path.join(__dirname, 'appsec', 'templates', 'blocked.json')
275
280
  )
276
281
 
282
+ const inAWSLambda = process.env.AWS_LAMBDA_FUNCTION_NAME !== undefined
283
+
284
+ const remoteConfigOptions = options.remoteConfig || {}
285
+ const DD_REMOTE_CONFIGURATION_ENABLED = coalesce(
286
+ process.env.DD_REMOTE_CONFIGURATION_ENABLED && isTrue(process.env.DD_REMOTE_CONFIGURATION_ENABLED),
287
+ !inAWSLambda
288
+ )
289
+ const DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS = coalesce(
290
+ parseInt(remoteConfigOptions.pollInterval),
291
+ parseInt(process.env.DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS),
292
+ 5 // seconds
293
+ )
294
+
277
295
  const iastOptions = options.experimental && options.experimental.iast
278
296
  const DD_IAST_ENABLED = coalesce(
279
297
  iastOptions &&
@@ -339,7 +357,6 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
339
357
  })
340
358
  }
341
359
 
342
- const inAWSLambda = process.env.AWS_LAMBDA_FUNCTION_NAME !== undefined
343
360
  const defaultFlushInterval = inAWSLambda ? 0 : 2000
344
361
 
345
362
  this.tracing = !isFalse(DD_TRACING_ENABLED)
@@ -358,9 +375,7 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
358
375
  this.clientIpHeader = DD_TRACE_CLIENT_IP_HEADER
359
376
  this.plugins = !!coalesce(options.plugins, true)
360
377
  this.service = DD_SERVICE
361
- this.serviceMapping = DD_SERVICE_MAPPING.length ? fromEntries(
362
- DD_SERVICE_MAPPING.split(',').map(x => x.trim().split(':'))
363
- ) : {}
378
+ this.serviceMapping = DD_SERVICE_MAPPING
364
379
  this.version = DD_VERSION
365
380
  this.dogstatsd = {
366
381
  hostname: coalesce(dogstatsd.hostname, process.env.DD_DOGSTATSD_HOSTNAME, this.hostname),
@@ -398,6 +413,10 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
398
413
  blockedTemplateHtml: DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML,
399
414
  blockedTemplateJson: DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON
400
415
  }
416
+ this.remoteConfig = {
417
+ enabled: DD_REMOTE_CONFIGURATION_ENABLED,
418
+ pollInterval: DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS
419
+ }
401
420
  this.iast = {
402
421
  enabled: isTrue(DD_IAST_ENABLED),
403
422
  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' }, patchDatadogLambdaModule)
74
+ }
@@ -0,0 +1,138 @@
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
+ pathSepExpr
19
+ } = require('../../../../datadog-instrumentations/src/helpers/register')
20
+
21
+ /**
22
+ * Breaks the full handler string into two pieces: the module root
23
+ * and the actual handler string.
24
+ *
25
+ * @param {string} fullHandler user's lambda handler, commonly stored in `DD_LAMBDA_HANDLER`.
26
+ * @returns {string[]} an array containing the module root and the handler string.
27
+ *
28
+ * ```js
29
+ * _extractModuleRootAndHandler('./api/src/index.nested.handler')
30
+ * // => ['./api/src', 'index.nested.handler']
31
+ * ```
32
+ */
33
+ function _extractModuleRootAndHandler (fullHandler) {
34
+ const handlerString = path.basename(fullHandler)
35
+ const moduleRoot = fullHandler.substring(0, fullHandler.indexOf(handlerString))
36
+
37
+ return [moduleRoot, handlerString]
38
+ }
39
+
40
+ /**
41
+ * Splits the handler string into two pieces: the module name
42
+ * and the path to the handler function.
43
+ *
44
+ * @param {string} handler a handler string containing the module and the handler path.
45
+ * @returns {string[]} an array containing the module name and the handler path.
46
+ *
47
+ * ```js
48
+ * _extractModuleNameAndHandlerPath('index.nested.handler')
49
+ * // => ['index', 'nested.handler']
50
+ * ```
51
+ */
52
+ function _extractModuleNameAndHandlerPath (handler) {
53
+ const FUNCTION_EXPR = /^([^.]*)\.(.*)$/
54
+ const match = handler.match(FUNCTION_EXPR)
55
+ if (!match || match.length !== 3) {
56
+ // Malformed Handler Name
57
+ return // TODO: throw error
58
+ }
59
+ return [match[1], match[2]] // [module, handler-path]
60
+ }
61
+
62
+ /**
63
+ * Returns the correct path of the file to be patched
64
+ * when required.
65
+ *
66
+ * @param {*} lambdaStylePath the path comprised of the `LAMBDA_TASK_ROOT`,
67
+ * the root of the module of the Lambda handler, and the module name.
68
+ * @returns the lambdaStylePath with the appropiate extension for the hook.
69
+ */
70
+ function _getLambdaFilePath (lambdaStylePath) {
71
+ let lambdaFilePath = lambdaStylePath
72
+ if (fs.existsSync(lambdaStylePath + '.js')) {
73
+ lambdaFilePath += '.js'
74
+ } else if (fs.existsSync(lambdaStylePath + '.mjs')) {
75
+ lambdaFilePath += '.mjs'
76
+ } else if (fs.existsSync(lambdaStylePath + '.cjs')) {
77
+ lambdaFilePath += '.cjs'
78
+ }
79
+ return lambdaFilePath
80
+ }
81
+
82
+ /**
83
+ * Register a hook for the Lambda handler to be executed when
84
+ * the file is required.
85
+ */
86
+ const registerLambdaHook = () => {
87
+ const lambdaTaskRoot = process.env.LAMBDA_TASK_ROOT
88
+ const originalLambdaHandler = process.env.DD_LAMBDA_HANDLER
89
+
90
+ if (originalLambdaHandler !== undefined) {
91
+ const [moduleRoot, moduleAndHandler] = _extractModuleRootAndHandler(originalLambdaHandler)
92
+ const [_module] = _extractModuleNameAndHandlerPath(moduleAndHandler)
93
+
94
+ const lambdaStylePath = path.resolve(lambdaTaskRoot, moduleRoot, _module)
95
+ const lambdaFilePath = _getLambdaFilePath(lambdaStylePath)
96
+
97
+ Hook([lambdaFilePath], (moduleExports) => {
98
+ require('./patch')
99
+
100
+ for (const { hook } of instrumentations[lambdaFilePath]) {
101
+ try {
102
+ moduleExports = hook(moduleExports)
103
+ } catch (e) {
104
+ log.error(e)
105
+ }
106
+ }
107
+
108
+ return moduleExports
109
+ })
110
+ } else {
111
+ const moduleToPatch = 'datadog-lambda-js'
112
+ Hook([moduleToPatch], (moduleExports, moduleName, _) => {
113
+ moduleName = moduleName.replace(pathSepExpr, '/')
114
+
115
+ require('./patch')
116
+
117
+ for (const { name, file, hook } of instrumentations[moduleToPatch]) {
118
+ const fullFilename = filename(name, file)
119
+ if (moduleName === fullFilename) {
120
+ try {
121
+ moduleExports = hook(moduleExports)
122
+ } catch (e) {
123
+ log.error(e)
124
+ }
125
+ }
126
+ }
127
+
128
+ return moduleExports
129
+ })
130
+ }
131
+ }
132
+
133
+ module.exports = {
134
+ _extractModuleRootAndHandler,
135
+ _extractModuleNameAndHandlerPath,
136
+ _getLambdaFilePath,
137
+ registerLambdaHook
138
+ }
@@ -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
  })