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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6e3d33329ff53ab220c857c255d70b687d50f0703054ecef2866a17676867eed
4
- data.tar.gz: 2c340f529123646d86a261630f2bcb5bb89dbbfda10a65f4820f1db8ece73504
3
+ metadata.gz: 25f8f9d1a39c45e8cab35097244e958621fd566992335198ad0fae2fd482bdbd
4
+ data.tar.gz: 1c0d3558ccd3aaa5b81ee4df1c80227aa558df0a62cc5b82695f0aa58b5b875f
5
5
  SHA512:
6
- metadata.gz: 79abf5fdf0729ef0464245c8765c88516b5445bff3123160e8f84fe1e43fa5360f311fceb731cb9f2823c49ac0caac5a78e3f35812ef3fe5e38cec2ab373cd81
7
- data.tar.gz: db3b2bec7d0999497c752d9d1664d2bd99e0b35bb1bd67f95d5e529c7a46950ae62a02334f65016b4a947373a4a3871d5aa75832f33765e930dc7c0c4367dd45
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
@@ -13,6 +13,8 @@ gem "rubocop", "~> 1.21"
13
13
 
14
14
  gem "prometheus-client", "~> 4.1"
15
15
 
16
+ gem "openssl"
17
+
16
18
  group :test do
17
19
  gem "simplecov", "~> 0.21.2"
18
20
  gem "simplecov-json"
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: true
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("Request received for #{endpoint_name} with arguments: #{args[2..]}")
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("Error processing #{endpoint_name}: #{e.message}\n#{e.backtrace.join("\n")}")
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.cert = OpenSSL::X509::Certificate.new(File.read(@macaw.config["macaw"]["ssl"]["cert_file_name"]))
135
- @context.key = OpenSSL::PKey::RSA.new(File.read(@macaw.config["macaw"]["ssl"]["key_file_name"]))
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
@@ -114,6 +114,6 @@ module RequestDataFiltering
114
114
  # Method responsible for sanitizing the parameter value
115
115
  def self.sanitize_parameter_value(value)
116
116
  value.gsub(/[^\w\s]/, "")
117
- value.gsub(/[\r\n\s]/, "")
117
+ value.gsub(/\s/, "")
118
118
  end
119
119
  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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MacawFramework
4
- VERSION = "1.0.2"
4
+ VERSION = "1.0.3"
5
5
  end
@@ -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.error(e.message)
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.2
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-07 00:00:00.000000000 Z
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.12
91
+ rubygems_version: 3.4.10
90
92
  signing_key:
91
93
  specification_version: 4
92
94
  summary: A lightweight back-end web framework