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 +4 -4
- data/lib/datadog/lambda/appsec/request.rb +11 -10
- data/lib/datadog/lambda/appsec/response.rb +31 -0
- data/lib/datadog/lambda/appsec/tagging.rb +73 -0
- data/lib/datadog/lambda/appsec.rb +46 -42
- data/lib/datadog/lambda/trace/listener.rb +4 -0
- data/lib/datadog/lambda/version.rb +1 -1
- data/lib/datadog/lambda.rb +3 -3
- metadata +10 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 16d17173aa40606df94b042f5b548443b57bc2d548a5da80d984c59a377ddcd5
|
|
4
|
+
data.tar.gz: 8ea8bf2a3559c0748405f249f169581783fb1e0a1ab23703378b119abc8362a8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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:
|
|
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(
|
|
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 '
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
response, context: context
|
|
55
|
-
)
|
|
56
|
+
normalized = ResponseNormalizer.normalize(response)
|
|
57
|
+
response = Response.from_normalized(normalized)
|
|
56
58
|
|
|
57
|
-
interrupt_params =
|
|
58
|
-
|
|
59
|
-
|
|
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
|
|
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
|
data/lib/datadog/lambda.rb
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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.
|
|
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-
|
|
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.
|
|
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.
|
|
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.
|
|
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: []
|