datadog-lambda 3.29.0 → 3.30.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c288d37d55d147aaca103a9ddfb7a7b6f9817aad19528a8be645ac81c879bfc
4
- data.tar.gz: 0d7252abd55bbbb0075e9e560292487091c8e462027d5001d2cf89237116a761
3
+ metadata.gz: 16d17173aa40606df94b042f5b548443b57bc2d548a5da80d984c59a377ddcd5
4
+ data.tar.gz: 8ea8bf2a3559c0748405f249f169581783fb1e0a1ab23703378b119abc8362a8
5
5
  SHA512:
6
- metadata.gz: 789cf5a6f51a19ffb8e0ed0e701c976669f841a186245aa58b881d9eca93865e2a76b19866f0426f3e08cda03bc552686ea41893d15ba2ad29d82e88f8f7493d
7
- data.tar.gz: 1ef9d2bc286e9b7279fe55cc548f17b579eb3bad7dedbc5d6109fda28d0a4f964ebe43d8569420e084da33a28386d8c70d48faeb96721277a3f28fe773e7e82e
6
+ metadata.gz: f117a37f4d08a925ff382dc29b01d89d73216351e3ea6452fcb98debeba8a0c07b2cc5b004526c19e8f47bca25034cf3134466c93813d2844df8a36e889d7a1f
7
+ data.tar.gz: b6ea614f6057797c8af1e1ba3385c2fd744fe4c346d9ce3d1cafd21eceb98ff247d312b460f17f63322d6324b9e6940c96d2fe22d29c047f6660a03e0e2b710d
@@ -10,17 +10,16 @@ module Datadog
10
10
  # @see Datadog::AppSec::Event.record
11
11
  # @see Datadog::AppSec::Contrib::Rack::Gateway::Request
12
12
  class Request
13
- attr_reader :host, :user_agent, :remote_addr, :headers
13
+ attr_reader :host, :user_agent, :remote_addr, :headers,
14
+ :request_method, :path
14
15
 
15
16
  class << self
16
17
  def from_normalized(event)
17
- headers = lowercase_headers(event)
18
-
19
18
  new(
20
- host: headers['host'],
21
- user_agent: headers['user-agent'],
22
19
  remote_addr: event['source_ip'],
23
- headers: headers
20
+ headers: lowercase_headers(event),
21
+ request_method: event['method'],
22
+ path: event['path']
24
23
  )
25
24
  end
26
25
 
@@ -33,11 +32,13 @@ module Datadog
33
32
  end
34
33
  end
35
34
 
36
- def initialize(host:, user_agent:, remote_addr:, headers:)
37
- @host = host
38
- @user_agent = user_agent
39
- @remote_addr = remote_addr
35
+ def initialize(remote_addr:, headers:, request_method:, path:)
40
36
  @headers = headers
37
+ @host = headers['host']
38
+ @user_agent = headers['user-agent']
39
+ @remote_addr = remote_addr
40
+ @request_method = request_method
41
+ @path = path
41
42
  end
42
43
  end
43
44
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Lambda
5
+ module AppSec
6
+ # Minimal response object for AppSec event recording.
7
+ #
8
+ # WARNING: It's a minimal data for interface compliance
9
+ #
10
+ # @see Datadog::AppSec::APISecurity.sample?
11
+ # @see Datadog::AppSec::Contrib::Rack::Gateway::Response
12
+ class Response
13
+ attr_reader :status, :headers, :body
14
+
15
+ def self.from_normalized(normalized)
16
+ new(
17
+ status: normalized['status_code'].to_i,
18
+ headers: normalized['headers'] || {},
19
+ body: normalized['body']
20
+ )
21
+ end
22
+
23
+ def initialize(status:, headers:, body:)
24
+ @status = status
25
+ @headers = headers
26
+ @body = body
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ require 'datadog/tracing/client_ip'
6
+ require 'datadog/core/header_collection'
7
+ require 'datadog/appsec/default_header_tags'
8
+
9
+ module Datadog
10
+ module Lambda
11
+ module AppSec
12
+ # Applies AppSec request, response, and keep-decision tags to the Lambda span
13
+ module Tagging
14
+ module_function
15
+
16
+ def tag_request(request, context:)
17
+ span = context.span
18
+ return unless span
19
+
20
+ headers = Datadog::Core::HeaderCollection.from_hash(request.headers)
21
+ Datadog::AppSec::DefaultHeaderTags.tag_request(span, headers)
22
+
23
+ return if span.get_tag(Datadog::Tracing::Metadata::Ext::HTTP::TAG_CLIENT_IP)
24
+
25
+ Datadog::Tracing::ClientIp.set_client_ip_tag!(
26
+ span, headers: headers, remote_ip: request.remote_addr
27
+ )
28
+ end
29
+
30
+ def tag_response(response, context:)
31
+ span = context.span
32
+ return unless span
33
+
34
+ headers = Datadog::Core::HeaderCollection.from_hash(response.headers)
35
+ Datadog::AppSec::DefaultHeaderTags.tag_response(span, headers)
36
+
37
+ return if headers.get('content-length')
38
+
39
+ if (body = response.body)
40
+ span.set_tag('http.response.headers.content-length', body.bytesize.to_s)
41
+ end
42
+ end
43
+
44
+ def tag_and_keep(context, cold_start:)
45
+ span = context.span
46
+ trace = context.trace
47
+
48
+ return unless trace && span
49
+
50
+ span.set_metric(Datadog::AppSec::Ext::TAG_APPSEC_ENABLED, 1)
51
+ span.set_tag('_dd.runtime_family', 'ruby')
52
+ span.set_tag('_dd.appsec.waf.version', Datadog::AppSec::WAF::VERSION::BASE_STRING)
53
+
54
+ ruleset_version = context.waf_runner_ruleset_version
55
+ return unless ruleset_version
56
+
57
+ span.set_tag('_dd.appsec.event_rules.version', ruleset_version)
58
+
59
+ return unless cold_start
60
+
61
+ trace.keep!
62
+ trace.set_tag(
63
+ Datadog::Tracing::Metadata::Ext::Distributed::TAG_DECISION_MAKER,
64
+ Datadog::Tracing::Sampling::Ext::Decision::ASM
65
+ )
66
+ span.set_tag(
67
+ '_dd.appsec.event_rules.addresses', JSON.dump(context.waf_runner_known_addresses)
68
+ )
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -1,7 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'json'
3
+ require 'datadog/appsec/api_security'
4
+
5
+ require_relative 'appsec/tagging'
4
6
  require_relative 'appsec/request'
7
+ require_relative 'appsec/response'
5
8
  require_relative 'appsec/event_normalizer'
6
9
  require_relative 'appsec/response_normalizer'
7
10
 
@@ -10,7 +13,7 @@ module Datadog
10
13
  # AppSec integration for AWS Lambda invocations.
11
14
  module AppSec
12
15
  class << self
13
- # rubocop:disable Metrics/AbcSize
16
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
14
17
  def on_start(event, trace:, span:, cold_start: false)
15
18
  @request = nil
16
19
  return unless enabled?
@@ -18,11 +21,12 @@ module Datadog
18
21
  context = create_context(trace, span)
19
22
  return unless Datadog::AppSec::Context.active
20
23
 
21
- tag_and_keep(context, cold_start: cold_start)
24
+ Tagging.tag_and_keep(context, cold_start: cold_start)
22
25
 
23
26
  event = EventNormalizer.normalize(event)
24
27
  @request = Request.from_normalized(event)
25
28
 
29
+ Tagging.tag_request(@request, context: context)
26
30
  payload = Datadog::AppSec::Instrumentation::Gateway::DataContainer.new(
27
31
  event, context: context
28
32
  )
@@ -40,26 +44,34 @@ module Datadog
40
44
  Datadog::AppSec::Context.deactivate if context
41
45
  Datadog::Utils.logger.debug("failed to start AppSec: #{e}")
42
46
  end
43
- # rubocop:enable Metrics/AbcSize
47
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
44
48
 
45
- # rubocop:disable Metrics/AbcSize
49
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
46
50
  def on_finish(response)
47
51
  return unless enabled?
48
52
 
49
53
  context = Datadog::AppSec::Context.active
50
54
  return unless context
51
55
 
52
- response = ResponseNormalizer.normalize(response)
53
- payload = Datadog::AppSec::Instrumentation::Gateway::DataContainer.new(
54
- response, context: context
55
- )
56
+ normalized = ResponseNormalizer.normalize(response)
57
+ response = Response.from_normalized(normalized)
56
58
 
57
- interrupt_params = catch(Datadog::AppSec::Ext::INTERRUPT) do
58
- Datadog::AppSec::Instrumentation.gateway.push('aws_lambda.response.start', payload)
59
- nil
59
+ interrupt_params = nil
60
+ unless context.interrupted?
61
+ Tagging.tag_response(response, context: context)
62
+ payload = Datadog::AppSec::Instrumentation::Gateway::DataContainer.new(
63
+ normalized, context: context
64
+ )
65
+
66
+ interrupt_params = catch(Datadog::AppSec::Ext::INTERRUPT) do
67
+ Datadog::AppSec::Instrumentation.gateway.push('aws_lambda.response.start', payload)
68
+ nil
69
+ end
70
+
71
+ context.mark_as_interrupted! if interrupt_params
60
72
  end
61
73
 
62
- context.mark_as_interrupted! if interrupt_params
74
+ extract_api_security_schema(context, request: @request, response: response)
63
75
 
64
76
  Datadog::AppSec::Event.record(context, request: @request)
65
77
  context.export_metrics
@@ -71,10 +83,30 @@ module Datadog
71
83
  ensure
72
84
  Datadog::AppSec::Context.deactivate if context
73
85
  end
74
- # rubocop:enable Metrics/AbcSize
86
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
87
+
88
+ def catch_interrupt
89
+ interrupt_params = catch(Datadog::AppSec::Ext::INTERRUPT) { return yield }
90
+
91
+ if (context = Datadog::AppSec::Context.active)
92
+ context.mark_as_interrupted!
93
+ end
94
+
95
+ response_override(interrupt_params, headers: @request.headers)
96
+ end
75
97
 
76
98
  private
77
99
 
100
+ def extract_api_security_schema(context, request:, response:)
101
+ return unless request && response
102
+
103
+ if Datadog::AppSec::APISecurity.enabled? &&
104
+ Datadog::AppSec::APISecurity.sample_trace?(context.trace) &&
105
+ Datadog::AppSec::APISecurity.sample?(request, response)
106
+ context.extract_schema!
107
+ end
108
+ end
109
+
78
110
  def enabled?
79
111
  defined?(Datadog::AppSec) &&
80
112
  Datadog::AppSec.respond_to?(:enabled?) &&
@@ -93,34 +125,6 @@ module Datadog
93
125
  context
94
126
  end
95
127
 
96
- def tag_and_keep(context, cold_start:)
97
- span = context.span
98
- trace = context.trace
99
-
100
- return unless trace && span
101
-
102
- span.set_metric(Datadog::AppSec::Ext::TAG_APPSEC_ENABLED, 1)
103
- span.set_tag('_dd.runtime_family', 'ruby')
104
- span.set_tag('_dd.appsec.waf.version', Datadog::AppSec::WAF::VERSION::BASE_STRING)
105
-
106
- ruleset_version = context.waf_runner_ruleset_version
107
- return unless ruleset_version
108
-
109
- span.set_tag('_dd.appsec.event_rules.version', ruleset_version)
110
-
111
- return unless cold_start
112
-
113
- span.set_tag(
114
- '_dd.appsec.event_rules.addresses', JSON.dump(context.waf_runner_known_addresses)
115
- )
116
-
117
- trace.keep!
118
- trace.set_tag(
119
- Datadog::Tracing::Metadata::Ext::Distributed::TAG_DECISION_MAKER,
120
- Datadog::Tracing::Sampling::Ext::Decision::ASM
121
- )
122
- end
123
-
124
128
  def response_override(interrupt_params, headers:)
125
129
  response = Datadog::AppSec::Response.from_interrupt_params(
126
130
  interrupt_params, headers['accept']
@@ -61,6 +61,10 @@ module Datadog
61
61
  end
62
62
  # rubocop:enable Metrics/AbcSize
63
63
 
64
+ def with_appsec(&block)
65
+ @response_override || Datadog::Lambda::AppSec.catch_interrupt(&block)
66
+ end
67
+
64
68
  def on_end(response:, request_context:)
65
69
  if (override = Datadog::Lambda::AppSec.on_finish(response))
66
70
  @response_override = override
@@ -12,7 +12,7 @@ module Datadog
12
12
  module Lambda
13
13
  module VERSION
14
14
  MAJOR = 3
15
- MINOR = 29
15
+ MINOR = 30
16
16
  PATCH = 0
17
17
  PRE = nil
18
18
 
@@ -60,7 +60,7 @@ module Datadog
60
60
  # @param event [Object] event sent to lambda
61
61
  # @param context [Object] lambda context
62
62
  # @param block [Proc] implementation of the handler function.
63
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
63
+ # rubocop:disable Metrics/CyclomaticComplexity
64
64
  def self.wrap(event, context, &block)
65
65
  @response = nil
66
66
  @listener ||= initialize_listener
@@ -68,7 +68,7 @@ module Datadog
68
68
  begin
69
69
  cold = @is_cold_start
70
70
  @listener&.on_start(event:, request_context: context, cold_start: cold)
71
- @response = @listener&.response_override || block.call
71
+ @response = @listener ? @listener.with_appsec(&block) : block.call
72
72
  rescue StandardError => e
73
73
  record_enhanced('errors', context)
74
74
  raise e
@@ -80,7 +80,7 @@ module Datadog
80
80
  end
81
81
  @response
82
82
  end
83
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
83
+ # rubocop:enable Metrics/CyclomaticComplexity
84
84
 
85
85
  # Gets the current tracing context
86
86
  def self.trace_context
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datadog-lambda
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.29.0
4
+ version: 3.30.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Datadog, Inc.
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-06-01 00:00:00.000000000 Z
11
+ date: 2026-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-xray-sdk
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '2.30'
47
+ version: '2.40'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '2.30'
54
+ version: '2.40'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -122,7 +122,9 @@ files:
122
122
  - lib/datadog/lambda/appsec.rb
123
123
  - lib/datadog/lambda/appsec/event_normalizer.rb
124
124
  - lib/datadog/lambda/appsec/request.rb
125
+ - lib/datadog/lambda/appsec/response.rb
125
126
  - lib/datadog/lambda/appsec/response_normalizer.rb
127
+ - lib/datadog/lambda/appsec/tagging.rb
126
128
  - lib/datadog/lambda/metrics.rb
127
129
  - lib/datadog/lambda/trace/constants.rb
128
130
  - lib/datadog/lambda/trace/context.rb
@@ -138,7 +140,7 @@ licenses:
138
140
  - Apache-2.0
139
141
  metadata:
140
142
  allowed_push_host: https://rubygems.org
141
- post_install_message:
143
+ post_install_message:
142
144
  rdoc_options: []
143
145
  require_paths:
144
146
  - lib
@@ -153,8 +155,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
155
  - !ruby/object:Gem::Version
154
156
  version: '0'
155
157
  requirements: []
156
- rubygems_version: 3.3.5
157
- signing_key:
158
+ rubygems_version: 3.4.19
159
+ signing_key:
158
160
  specification_version: 4
159
161
  summary: Instruments your Ruby AWS Lambda functions with Datadog
160
162
  test_files: []