Module: Fizzy::Security
- Defined in:
- lib/fizzy/security.rb
Overview
Security helpers for URL validation, message truncation, and header redaction. Used across the SDK to enforce HTTPS, prevent SSRF, and protect sensitive data.
Constant Summary collapse
- MAX_ERROR_MESSAGE_BYTES =
500- MAX_RESPONSE_BODY_BYTES =
50 MB
50 * 1024 * 1024
- MAX_ERROR_BODY_BYTES =
1 MB
1 * 1024 * 1024
- SENSITIVE_HEADERS =
Headers that contain sensitive values and should be redacted.
%w[ authorization cookie set-cookie x-csrf-token ].freeze
Class Method Summary collapse
- .check_body_size!(body, max, label = "Response") ⇒ Object
- .localhost?(url) ⇒ Boolean
- .normalize_host(uri) ⇒ Object
-
.redact_headers(headers) ⇒ Hash
Returns a copy of the headers with sensitive values replaced by “[REDACTED]”.
- .require_https!(url, label = "URL") ⇒ Object
- .require_https_unless_localhost!(url, label = "URL") ⇒ Object
- .resolve_url(base, target) ⇒ Object
- .same_origin?(a, b) ⇒ Boolean
- .truncate(str, max = MAX_ERROR_MESSAGE_BYTES) ⇒ Object
Class Method Details
.check_body_size!(body, max, label = "Response") ⇒ Object
53 54 55 56 57 58 59 60 61 |
# File 'lib/fizzy/security.rb', line 53 def self.check_body_size!(body, max, label = "Response") return if body.nil? if body.bytesize > max raise Fizzy::APIError.new( "#{label} body too large (#{body.bytesize} bytes, max #{max})" ) end end |
.localhost?(url) ⇒ Boolean
63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/fizzy/security.rb', line 63 def self.localhost?(url) uri = URI.parse(url.to_s) host = uri.host&.downcase return false if host.nil? host == "localhost" || host == "127.0.0.1" || host == "::1" || host == "[::1]" || host.end_with?(".localhost") rescue URI::InvalidURIError false end |
.normalize_host(uri) ⇒ Object
43 44 45 46 47 48 49 50 51 |
# File 'lib/fizzy/security.rb', line 43 def self.normalize_host(uri) host = uri.host&.downcase port = uri.port return host if port.nil? return host if uri.scheme&.downcase == "https" && port == 443 return host if uri.scheme&.downcase == "http" && port == 80 "#{host}:#{port}" end |
.redact_headers(headers) ⇒ Hash
Returns a copy of the headers with sensitive values replaced by “[REDACTED]”.
95 96 97 98 99 100 101 |
# File 'lib/fizzy/security.rb', line 95 def self.redact_headers(headers) result = {} headers.each do |key, value| result[key] = SENSITIVE_HEADERS.include?(key.to_s.downcase) ? "[REDACTED]" : value end result end |
.require_https!(url, label = "URL") ⇒ Object
19 20 21 22 23 24 |
# File 'lib/fizzy/security.rb', line 19 def self.require_https!(url, label = "URL") uri = URI.parse(url.to_s) raise UsageError.new("#{label} must use HTTPS: #{url}") unless uri.scheme&.downcase == "https" rescue URI::InvalidURIError raise UsageError.new("Invalid #{label}: #{url}") end |
.require_https_unless_localhost!(url, label = "URL") ⇒ Object
77 78 79 80 81 |
# File 'lib/fizzy/security.rb', line 77 def self.require_https_unless_localhost!(url, label = "URL") return if localhost?(url) require_https!(url, label) end |
.resolve_url(base, target) ⇒ Object
37 38 39 40 41 |
# File 'lib/fizzy/security.rb', line 37 def self.resolve_url(base, target) URI.join(base, target).to_s rescue URI::InvalidURIError target end |
.same_origin?(a, b) ⇒ Boolean
26 27 28 29 30 31 32 33 34 35 |
# File 'lib/fizzy/security.rb', line 26 def self.same_origin?(a, b) ua = URI.parse(a) ub = URI.parse(b) return false if ua.scheme.nil? || ub.scheme.nil? ua.scheme.downcase == ub.scheme.downcase && normalize_host(ua) == normalize_host(ub) rescue URI::InvalidURIError false end |
.truncate(str, max = MAX_ERROR_MESSAGE_BYTES) ⇒ Object
13 14 15 16 17 |
# File 'lib/fizzy/security.rb', line 13 def self.truncate(str, max = MAX_ERROR_MESSAGE_BYTES) return str if str.nil? || str.bytesize <= max max <= 3 ? str.byteslice(0, max) : str.byteslice(0, max - 3) + "..." end |