macaw_framework 1.0.2 → 1.0.3
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/CHANGELOG.md +6 -0
- data/Gemfile +2 -0
- data/README.md +10 -0
- data/lib/macaw_framework/aspects/cache_aspect.rb +5 -1
- data/lib/macaw_framework/aspects/logging_aspect.rb +9 -4
- data/lib/macaw_framework/core/server.rb +12 -4
- data/lib/macaw_framework/data_filters/log_data_filter.rb +51 -0
- data/lib/macaw_framework/data_filters/request_data_filtering.rb +1 -1
- data/lib/macaw_framework/utils/supported_ssl_versions.rb +13 -0
- data/lib/macaw_framework/version.rb +1 -1
- data/lib/macaw_framework.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 25f8f9d1a39c45e8cab35097244e958621fd566992335198ad0fae2fd482bdbd
|
4
|
+
data.tar.gz: 1c0d3558ccd3aaa5b81ee4df1c80227aa558df0a62cc5b82695f0aa58b5b875f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 25013e659bf86515d3d730a2d8b98c7efdb3f69fc691122e7515b6de38c772d0973d3c23d8dac9824001eb4a34b9ffe99f0c08826f71b14263fdb273b74c0cdb
|
7
|
+
data.tar.gz: 872c0b12123044cf2ae34c01e2b6ff05cf1bfaa0698de8d4571fdf8492647b24c88ba415407753423db8dfcd6c6854b5cc4c16b2c856884b4ae260f25c8b6454
|
data/CHANGELOG.md
CHANGED
@@ -53,3 +53,9 @@
|
|
53
53
|
- Fixing a bug with cache where ignored_headers where not being properly loaded
|
54
54
|
- Fixed a bug with cache where URL parameters were not being considered in the strategy
|
55
55
|
- Updating SECURITY.md with more information
|
56
|
+
|
57
|
+
## [1.0.3] - 2023-05-10
|
58
|
+
|
59
|
+
- Fixing issue of error responses being cached
|
60
|
+
- Implementing support for min and max SSL version
|
61
|
+
- Creating log sanitization to prevent log forging
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -85,6 +85,12 @@ end
|
|
85
85
|
"port": 8080,
|
86
86
|
"bind": "localhost",
|
87
87
|
"threads": 10,
|
88
|
+
"log": {
|
89
|
+
"max_length": 1024,
|
90
|
+
"sensitive_fields": [
|
91
|
+
"password"
|
92
|
+
]
|
93
|
+
},
|
88
94
|
"cache": {
|
89
95
|
"cache_invalidation": 3600,
|
90
96
|
"ignore_headers": [
|
@@ -100,6 +106,8 @@ end
|
|
100
106
|
"max_requests": 3
|
101
107
|
},
|
102
108
|
"ssl": {
|
109
|
+
"min": "SSL3",
|
110
|
+
"max": "TLS1.3",
|
103
111
|
"cert_file_name": "path/to/cert/file/file.crt",
|
104
112
|
"key_file_name": "path/to/cert/key/file.key"
|
105
113
|
}
|
@@ -135,6 +143,8 @@ exists inside `application.json`.
|
|
135
143
|
If the SSL configuration is provided in the `application.json` file with valid certificate and key files, the TCP server
|
136
144
|
will be wrapped with HTTPS security using the provided certificate.
|
137
145
|
|
146
|
+
The supported values for `min` and `max` in the SSL configuration are: `SSL2`, `SSL3`, `TLS1.1`, `TLS1.2` and `TLS1.3`
|
147
|
+
|
138
148
|
If prometheus is enabled, a get endpoint will be defined at path `/metrics` to collect prometheus metrics. This path
|
139
149
|
is configurable via the `application.json` file.
|
140
150
|
|
@@ -12,7 +12,7 @@ module CacheAspect
|
|
12
12
|
return cache[:cache].cache[cache_filtered_name][0] unless cache[:cache].cache[cache_filtered_name].nil?
|
13
13
|
|
14
14
|
response = super(*args)
|
15
|
-
cache[:cache].cache[cache_filtered_name] = [response, Time.now]
|
15
|
+
cache[:cache].cache[cache_filtered_name] = [response, Time.now] if should_cache_response?(response[1])
|
16
16
|
response
|
17
17
|
end
|
18
18
|
end
|
@@ -23,4 +23,8 @@ module CacheAspect
|
|
23
23
|
filtered_headers = client_data[:headers].filter { |key, _value| !ignored_headers&.include?(key) }
|
24
24
|
[{ body: client_data[:body], params: client_data[:params], headers: filtered_headers }].to_s.to_sym
|
25
25
|
end
|
26
|
+
|
27
|
+
def should_cache_response?(status)
|
28
|
+
(200..299).include?(status.to_i)
|
29
|
+
end
|
26
30
|
end
|
@@ -1,6 +1,7 @@
|
|
1
|
-
# frozen_string_literal:
|
1
|
+
# frozen_string_literal: false
|
2
2
|
|
3
3
|
require "logger"
|
4
|
+
require_relative "../data_filters/log_data_filter"
|
4
5
|
|
5
6
|
##
|
6
7
|
# This Aspect is responsible for logging
|
@@ -9,13 +10,17 @@ require "logger"
|
|
9
10
|
module LoggingAspect
|
10
11
|
def call_endpoint(logger, *args)
|
11
12
|
endpoint_name = args[1].split(".")[1..].join("/")
|
12
|
-
logger.info(
|
13
|
+
logger.info(LogDataFilter.sanitize_for_logging(
|
14
|
+
"Request received for #{endpoint_name} with arguments: #{args[2..]}"
|
15
|
+
))
|
13
16
|
|
14
17
|
begin
|
15
18
|
response = super(*args)
|
16
|
-
logger.info("Response for #{endpoint_name}: #{response}")
|
19
|
+
logger.info(LogDataFilter.sanitize_for_logging("Response for #{endpoint_name}: #{response}"))
|
17
20
|
rescue StandardError => e
|
18
|
-
logger.error(
|
21
|
+
logger.error(
|
22
|
+
LogDataFilter.sanitize_for_logging("Error processing #{endpoint_name}: #{e.message}\n#{e.backtrace.join("\n")}")
|
23
|
+
)
|
19
24
|
raise e
|
20
25
|
end
|
21
26
|
|
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "../middlewares/memory_invalidation_middleware"
|
3
4
|
require_relative "../middlewares/rate_limiter_middleware"
|
4
5
|
require_relative "../data_filters/response_data_filter"
|
5
|
-
require_relative "../middlewares/memory_invalidation_middleware"
|
6
6
|
require_relative "../errors/too_many_requests_error"
|
7
|
+
require_relative "../utils/supported_ssl_versions"
|
7
8
|
require_relative "../aspects/prometheus_aspect"
|
8
9
|
require_relative "../aspects/logging_aspect"
|
9
10
|
require_relative "../aspects/cache_aspect"
|
@@ -18,6 +19,8 @@ class Server
|
|
18
19
|
prepend PrometheusAspect
|
19
20
|
# rubocop:disable Metrics/ParameterLists
|
20
21
|
|
22
|
+
attr_reader :context
|
23
|
+
|
21
24
|
##
|
22
25
|
# Create a new instance of Server.
|
23
26
|
# @param {Macaw} macaw
|
@@ -129,10 +132,15 @@ class Server
|
|
129
132
|
end
|
130
133
|
|
131
134
|
def set_ssl
|
132
|
-
if @macaw.config&.dig("macaw", "ssl")
|
135
|
+
ssl_config = @macaw.config["macaw"]["ssl"] if @macaw.config&.dig("macaw", "ssl")
|
136
|
+
ssl_config ||= nil
|
137
|
+
unless ssl_config.nil?
|
138
|
+
version_config = { min: ssl_config["min"], max: ssl_config["max"] }
|
133
139
|
@context = OpenSSL::SSL::SSLContext.new
|
134
|
-
@context.
|
135
|
-
@context.
|
140
|
+
@context.min_version = SupportedSSLVersions::VERSIONS[version_config[:min]] unless version_config[:min].nil?
|
141
|
+
@context.max_version = SupportedSSLVersions::VERSIONS[version_config[:max]] unless version_config[:max].nil?
|
142
|
+
@context.cert = OpenSSL::X509::Certificate.new(File.read(ssl_config["cert_file_name"]))
|
143
|
+
@context.key = OpenSSL::PKey::RSA.new(File.read(ssl_config["key_file_name"]))
|
136
144
|
end
|
137
145
|
@context ||= nil
|
138
146
|
rescue IOError => e
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
##
|
6
|
+
# Module responsible for sanitizing log data
|
7
|
+
module LogDataFilter
|
8
|
+
DEFAULT_MAX_LENGTH = 512
|
9
|
+
DEFAULT_SENSITIVE_FIELDS = [].freeze
|
10
|
+
|
11
|
+
def self.config
|
12
|
+
@config ||= begin
|
13
|
+
file_path = "application.json"
|
14
|
+
config = {
|
15
|
+
max_length: DEFAULT_MAX_LENGTH,
|
16
|
+
sensitive_fields: DEFAULT_SENSITIVE_FIELDS
|
17
|
+
}
|
18
|
+
|
19
|
+
if File.exist?(file_path)
|
20
|
+
json = JSON.parse(File.read(file_path))
|
21
|
+
|
22
|
+
if json["macaw"] && json["macaw"]["log"]
|
23
|
+
log_config = json["macaw"]["log"]
|
24
|
+
config[:max_length] = log_config["max_length"] if log_config["max_length"]
|
25
|
+
config[:sensitive_fields] = log_config["sensitive_fields"] if log_config["sensitive_fields"]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
config
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.sanitize_for_logging(data, sensitive_fields: config[:sensitive_fields])
|
34
|
+
return "" if data.nil?
|
35
|
+
|
36
|
+
data = data.to_s.force_encoding("UTF-8")
|
37
|
+
data = data.gsub(/[\x00-\x1F\x7F]/, "")
|
38
|
+
data = data.gsub(/\s+/, " ")
|
39
|
+
data = data.slice(0, config[:max_length])
|
40
|
+
|
41
|
+
sensitive_fields.each do |field|
|
42
|
+
next unless data.include?(field.to_s)
|
43
|
+
|
44
|
+
data = data.gsub(/(#{Regexp.escape(field.to_s)}\s*[:=]\s*)([^\s]+)/) do |_match|
|
45
|
+
"#{::Regexp.last_match(1)}#{Digest::SHA256.hexdigest(::Regexp.last_match(2))}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
data
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "openssl"
|
4
|
+
|
5
|
+
module SupportedSSLVersions
|
6
|
+
VERSIONS = {
|
7
|
+
"SSL2" => OpenSSL::SSL::SSL2_VERSION,
|
8
|
+
"SSL3" => OpenSSL::SSL::SSL3_VERSION,
|
9
|
+
"TLS1.1" => OpenSSL::SSL::TLS1_1_VERSION,
|
10
|
+
"TLS1.2" => OpenSSL::SSL::TLS1_2_VERSION,
|
11
|
+
"TLS1.3" => OpenSSL::SSL::TLS1_3_VERSION
|
12
|
+
}.freeze
|
13
|
+
end
|
data/lib/macaw_framework.rb
CHANGED
@@ -37,7 +37,7 @@ module MacawFramework
|
|
37
37
|
@prometheus_middleware = PrometheusMiddleware.new if @config["macaw"]["prometheus"]
|
38
38
|
@prometheus_middleware.configure_prometheus(@prometheus, @config, self) if @config["macaw"]["prometheus"]
|
39
39
|
rescue StandardError => e
|
40
|
-
@macaw_log.
|
40
|
+
@macaw_log.warn(e.message)
|
41
41
|
end
|
42
42
|
@port ||= 8080
|
43
43
|
@bind ||= "localhost"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: macaw_framework
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aria Diniz
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-05-
|
11
|
+
date: 2023-05-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: prometheus-client
|
@@ -46,6 +46,7 @@ files:
|
|
46
46
|
- lib/macaw_framework/aspects/logging_aspect.rb
|
47
47
|
- lib/macaw_framework/aspects/prometheus_aspect.rb
|
48
48
|
- lib/macaw_framework/core/server.rb
|
49
|
+
- lib/macaw_framework/data_filters/log_data_filter.rb
|
49
50
|
- lib/macaw_framework/data_filters/request_data_filtering.rb
|
50
51
|
- lib/macaw_framework/data_filters/response_data_filter.rb
|
51
52
|
- lib/macaw_framework/errors/endpoint_not_mapped_error.rb
|
@@ -54,6 +55,7 @@ files:
|
|
54
55
|
- lib/macaw_framework/middlewares/prometheus_middleware.rb
|
55
56
|
- lib/macaw_framework/middlewares/rate_limiter_middleware.rb
|
56
57
|
- lib/macaw_framework/utils/http_status_code.rb
|
58
|
+
- lib/macaw_framework/utils/supported_ssl_versions.rb
|
57
59
|
- lib/macaw_framework/version.rb
|
58
60
|
- macaw_logo.png
|
59
61
|
- main/CODEOWNERS
|
@@ -86,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
86
88
|
- !ruby/object:Gem::Version
|
87
89
|
version: '0'
|
88
90
|
requirements: []
|
89
|
-
rubygems_version: 3.4.
|
91
|
+
rubygems_version: 3.4.10
|
90
92
|
signing_key:
|
91
93
|
specification_version: 4
|
92
94
|
summary: A lightweight back-end web framework
|