Class: Rhales::CSP
- Inherits:
-
Object
- Object
- Rhales::CSP
- Includes:
- Utils::LoggingHelpers
- Defined in:
- lib/rhales/security/csp.rb
Overview
Content Security Policy (CSP) header generation and management
Provides secure defaults and nonce integration for CSP headers. Converts policy configuration into proper CSP header strings.
Usage: csp = Rhales::CSP.new(config, nonce: ‘abc123’) header = csp.build_header # => “default-src ‘self’; script-src ‘self’ ‘nonce-abc123’; …”
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
Returns the value of attribute config.
-
#nonce ⇒ Object
readonly
Returns the value of attribute nonce.
Class Method Summary collapse
-
.generate_nonce ⇒ Object
Generate a new nonce value.
Instance Method Summary collapse
-
#build_header ⇒ Object
Build CSP header string from configuration.
-
#initialize(config, nonce: nil) ⇒ CSP
constructor
A new instance of CSP.
-
#interpolate_nonce(source) ⇒ Object
private
Interpolate nonce placeholder in source values.
-
#nonce_required? ⇒ Boolean
Check if nonce is required for any directive.
-
#validate_policy! ⇒ Object
Validate CSP policy configuration.
Methods included from Utils::LoggingHelpers
#format_value, #log_timed_operation, #log_with_metadata
Methods included from Utils
Constructor Details
#initialize(config, nonce: nil) ⇒ CSP
Returns a new instance of CSP.
18 19 20 21 |
# File 'lib/rhales/security/csp.rb', line 18 def initialize(config, nonce: nil) @config = config @nonce = nonce end |
Instance Attribute Details
#config ⇒ Object (readonly)
Returns the value of attribute config.
16 17 18 |
# File 'lib/rhales/security/csp.rb', line 16 def config @config end |
#nonce ⇒ Object (readonly)
Returns the value of attribute nonce.
16 17 18 |
# File 'lib/rhales/security/csp.rb', line 16 def nonce @nonce end |
Class Method Details
.generate_nonce ⇒ Object
Generate a new nonce value
60 61 62 63 64 65 66 67 |
# File 'lib/rhales/security/csp.rb', line 60 def self.generate_nonce nonce = SecureRandom.hex(16) # Log nonce generation for security audit trail Rhales.logger.debug("CSP nonce generated: nonce=#{nonce} length=#{nonce.length} entropy_bits=#{nonce.length * 4}") nonce end |
Instance Method Details
#build_header ⇒ Object
Build CSP header string from configuration
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/rhales/security/csp.rb', line 24 def build_header return nil unless @config.csp_enabled policy_directives = [] nonce_used = false @config.csp_policy.each do |directive, sources| if sources.empty? # For directives with no sources (like upgrade-insecure-requests) policy_directives << directive else # Process sources and interpolate nonce if present processed_sources = sources.map do |source| interpolated = interpolate_nonce(source) nonce_used = true if interpolated != source interpolated end directive_string = "#{directive} #{processed_sources.join(' ')}" policy_directives << directive_string end end header = policy_directives.join('; ') # Log CSP header generation for security audit (Rhales.logger, :info, "CSP header generated", nonce_used: nonce_used, nonce: @nonce, directive_count: policy_directives.size, header_length: header.length ) header end |
#interpolate_nonce(source) ⇒ Object (private)
Interpolate nonce placeholder in source values
110 111 112 113 114 |
# File 'lib/rhales/security/csp.rb', line 110 def interpolate_nonce(source) return source unless @nonce && source.include?('{{nonce}}') source.gsub('{{nonce}}', @nonce) end |
#nonce_required? ⇒ Boolean
Check if nonce is required for any directive
101 102 103 104 105 |
# File 'lib/rhales/security/csp.rb', line 101 def nonce_required? return false unless @config.csp_enabled @config.csp_policy.values.flatten.any? { |source| source.include?('{{nonce}}') } end |
#validate_policy! ⇒ Object
Validate CSP policy configuration
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/rhales/security/csp.rb', line 70 def validate_policy! return unless @config.csp_enabled errors = [] # Ensure policy is a hash unless @config.csp_policy.is_a?(Hash) errors << 'csp_policy must be a hash' raise Rhales::Configuration::ConfigurationError, "CSP policy errors: #{errors.join(', ')}" end # Validate each directive @config.csp_policy.each do |directive, sources| unless sources.is_a?(Array) errors << "#{directive} sources must be an array" end # Check for dangerous sources if sources.include?("'unsafe-eval'") errors << "#{directive} contains dangerous 'unsafe-eval' source" end if sources.include?("'unsafe-inline'") && !%w[style-src].include?(directive) errors << "#{directive} contains dangerous 'unsafe-inline' source" end end raise Rhales::Configuration::ConfigurationError, "CSP policy errors: #{errors.join(', ')}" unless errors.empty? end |