macaw_framework 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
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