Class: RailsAi::Security::SecureHTTPClient
- Inherits:
-
Object
- Object
- RailsAi::Security::SecureHTTPClient
- Defined in:
- lib/rails_ai/security/secure_http_client.rb
Constant Summary collapse
- DEFAULT_TIMEOUT =
30- MAX_REDIRECTS =
5
Class Method Summary collapse
- .blocked_host?(host) ⇒ Boolean
- .create_secure_http_client(uri) ⇒ Object
- .create_secure_request(uri, options) ⇒ Object
- .execute_streaming_request(http, request, timeout, &block) ⇒ Object
- .execute_with_timeout(http, request, timeout) ⇒ Object
- .make_request(url, options = {}) ⇒ Object
- .make_streaming_request(url, options = {}, &block) ⇒ Object
- .private_ip?(host) ⇒ Boolean
- .set_security_headers(request) ⇒ Object
- .validate_response(response) ⇒ Object
- .validate_url(uri) ⇒ Object
Class Method Details
.blocked_host?(host) ⇒ Boolean
151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/rails_ai/security/secure_http_client.rb', line 151 def self.blocked_host?(host) return true if host.nil? blocked_hosts = %w[ localhost 127.0.0.1 0.0.0.0 ::1 metadata.google.internal instance-data.ec2.internal 169.254.169.254 ] blocked_hosts.any? { |blocked| host == blocked || host.end_with?(".#{blocked}") } end |
.create_secure_http_client(uri) ⇒ Object
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/rails_ai/security/secure_http_client.rb', line 61 def self.create_secure_http_client(uri) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = (uri.scheme == 'https') # Security settings http.verify_mode = OpenSSL::SSL::VERIFY_PEER http.verify_depth = 5 # Timeout settings http.read_timeout = DEFAULT_TIMEOUT http.open_timeout = 10 http.ssl_timeout = 10 # Disable SSL compression http.ssl_version = :TLSv1_2 http end |
.create_secure_request(uri, options) ⇒ Object
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/rails_ai/security/secure_http_client.rb', line 80 def self.create_secure_request(uri, ) case [:method]&.upcase || 'GET' when 'GET' Net::HTTP::Get.new(uri) when 'POST' request = Net::HTTP::Post.new(uri) request.body = [:body] if [:body] request when 'PUT' request = Net::HTTP::Put.new(uri) request.body = [:body] if [:body] request else raise SecurityError, "Unsupported HTTP method: #{[:method]}" end end |
.execute_streaming_request(http, request, timeout, &block) ⇒ Object
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/rails_ai/security/secure_http_client.rb', line 122 def self.execute_streaming_request(http, request, timeout, &block) Timeout.timeout(timeout) do http.request(request) do |response| validate_response(response) response.read_body do |chunk| block.call(chunk) if block end end end rescue Timeout::Error raise SecurityError, "Streaming request timeout after #{timeout} seconds" rescue => e raise SecurityError, "Streaming request failed: #{e.}" end |
.execute_with_timeout(http, request, timeout) ⇒ Object
110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/rails_ai/security/secure_http_client.rb', line 110 def self.execute_with_timeout(http, request, timeout) Timeout.timeout(timeout) do response = http.request(request) validate_response(response) response end rescue Timeout::Error raise SecurityError, "Request timeout after #{timeout} seconds" rescue => e raise SecurityError, "HTTP request failed: #{e.}" end |
.make_request(url, options = {}) ⇒ Object
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/rails_ai/security/secure_http_client.rb', line 13 def self.make_request(url, = {}) uri = URI.parse(url) # Security validations validate_url(uri) http = create_secure_http_client(uri) request = create_secure_request(uri, ) # Set security headers set_security_headers(request) # Execute request with timeout execute_with_timeout(http, request, [:timeout] || DEFAULT_TIMEOUT) end |
.make_streaming_request(url, options = {}, &block) ⇒ Object
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/rails_ai/security/secure_http_client.rb', line 29 def self.make_streaming_request(url, = {}, &block) uri = URI.parse(url) # Security validations validate_url(uri) http = create_secure_http_client(uri) request = create_secure_request(uri, ) # Set security headers set_security_headers(request) # Execute streaming request execute_streaming_request(http, request, [:timeout] || DEFAULT_TIMEOUT, &block) end |
.private_ip?(host) ⇒ Boolean
164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/rails_ai/security/secure_http_client.rb', line 164 def self.private_ip?(host) return false if host.nil? require 'ipaddr' begin ip = IPAddr.new(host) ip.private? || ip.loopback? || ip.link_local? rescue IPAddr::InvalidAddressError false end end |
.set_security_headers(request) ⇒ Object
97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/rails_ai/security/secure_http_client.rb', line 97 def self.set_security_headers(request) request['User-Agent'] = 'RailsAI/1.0' request['Accept'] = 'application/json' request['Connection'] = 'close' request['Cache-Control'] = 'no-cache' # Security headers request['X-Requested-With'] = 'RailsAI' request['X-Content-Type-Options'] = 'nosniff' request['X-Frame-Options'] = 'DENY' request['X-XSS-Protection'] = '1; mode=block' end |
.validate_response(response) ⇒ Object
138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/rails_ai/security/secure_http_client.rb', line 138 def self.validate_response(response) case response.code.to_i when 200..299 response when 400..499 raise SecurityError, "Client error: #{response.code} #{response.}" when 500..599 raise SecurityError, "Server error: #{response.code} #{response.}" else raise SecurityError, "Unexpected response: #{response.code} #{response.}" end end |
.validate_url(uri) ⇒ Object
47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/rails_ai/security/secure_http_client.rb', line 47 def self.validate_url(uri) unless %w[http https].include?(uri.scheme) raise SecurityError, "Invalid URL scheme: #{uri.scheme}" end if blocked_host?(uri.host) raise SecurityError, "URL blocked for security reasons: #{uri.host}" end if private_ip?(uri.host) raise SecurityError, "Private IP addresses not allowed: #{uri.host}" end end |