Skip to content

Commit d793136

Browse files
jlledomclaude
andcommitted
Load CSP from a config file.
Co-Authored-By: Claude <noreply@anthropic.com>
1 parent fc2aada commit d793136

File tree

6 files changed

+130
-27
lines changed

6 files changed

+130
-27
lines changed

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ config/backend_redis.yml
3232
config/cache_store.yml
3333
config/core.yml
3434
config/cors.yml
35+
/config/content_security_policy.yml
3536
config/currencies.yml
3637
config/database.yml
3738
config/domain_substitution.yml

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ C:\\nppdf32Log\\debuglog.txt
5252
/config/cache_store.yml
5353
/config/core.yml
5454
/config/cors.yml
55+
/config/content_security_policy.yml
5556
/config/currencies.yml
5657
/config/database.yml
5758
/config/domain_substitution.yml

config/application.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,10 @@ def cache_store_config
256256
config.three_scale.cors.enabled = false
257257
config.three_scale.cors.merge!(try_config_for(:cors) || {})
258258

259+
config.three_scale.content_security_policy = ActiveSupport::OrderedOptions.new
260+
config.three_scale.content_security_policy.enabled = false
261+
config.three_scale.content_security_policy.merge!(try_config_for(:content_security_policy) || {})
262+
259263
three_scale = config_for(:settings)
260264

261265
three_scale[:error_reporting_stages] = three_scale[:error_reporting_stages].to_s.split(/\W+/)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
base: &default
2+
enabled: true
3+
4+
# Global CSP settings
5+
report_only: false
6+
report_uri: null
7+
nonce_generator: true # Enable nonce for inline scripts/styles
8+
nonce_directives:
9+
- script-src
10+
- style-src
11+
12+
# CSP Policy Directives
13+
policy:
14+
default_src: ["'self'"]
15+
script_src: ["'self'", "'unsafe-eval'", "'unsafe-inline'"]
16+
style_src: ["'self'", "'unsafe-inline'"]
17+
img_src: ["'self'", "data:", "https:"]
18+
font_src: ["'self'", "data:"]
19+
connect_src: ["'self'"]
20+
frame_src: ["'self'"] # Needed for Swagger/ActiveDocs iframes
21+
frame_ancestors: ["'self'"] # Modern replacement for X-Frame-Options: SAMEORIGIN
22+
object_src: ["'none'"]
23+
base_uri: ["'self'"]
24+
form_action: ["'self'"]
25+
26+
development:
27+
<<: *default
28+
enabled: false
29+
30+
test:
31+
<<: *default
32+
enabled: false
33+
34+
production:
35+
<<: *default
36+
enabled: true
Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,57 @@
1-
# Be sure to restart your server when you modify this file.
1+
# frozen_string_literal: true
22

3-
# Define an application-wide content security policy.
4-
# See the Securing Rails Applications Guide for more information:
5-
# https://guides.rubyonrails.org/security.html#content-security-policy-header
3+
# Configure Content Security Policy headers
4+
# See: https://guides.rubyonrails.org/security.html#content-security-policy-header
65

7-
Rails.application.config.to_prepare do
8-
Rails.application.config.content_security_policy do |policy|
9-
policy.default_src '*', :data, :mediastream, :blob, :filesystem, :ws, :wss, :unsafe_eval, :unsafe_inline
6+
require_dependency 'three_scale/content_security_policy'
7+
8+
if ThreeScale::ContentSecurityPolicy.enabled?
9+
# Apply configurable CSP from YAML
10+
Rails.application.configure do
11+
# Configure nonce generation if enabled
12+
if ThreeScale::ContentSecurityPolicy.nonce_enabled?
13+
config.content_security_policy_nonce_generator = ->(request) {
14+
SecureRandom.base64(16)
15+
}
16+
17+
nonce_directives = ThreeScale::ContentSecurityPolicy.nonce_directives
18+
config.content_security_policy_nonce_directives = nonce_directives unless nonce_directives.empty?
19+
end
20+
21+
# Set report-only mode if configured
22+
if ThreeScale::ContentSecurityPolicy.report_only?
23+
config.content_security_policy_report_only = true
24+
end
1025
end
11-
end
1226

13-
# Rails.application.configure do
14-
# config.content_security_policy do |policy|
15-
# policy.default_src :self, :https
16-
# policy.font_src :self, :https, :data
17-
# policy.img_src :self, :https, :data
18-
# policy.object_src :none
19-
# policy.script_src :self, :https
20-
# policy.style_src :self, :https
21-
# # Specify URI for violation reports
22-
# # policy.report_uri "/csp-violation-report-endpoint"
23-
# end
24-
#
25-
# # Generate session nonces for permitted importmap, inline scripts, and inline styles.
26-
# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
27-
# config.content_security_policy_nonce_directives = %w(script-src style-src)
28-
#
29-
# # Report violations without enforcing the policy.
30-
# # config.content_security_policy_report_only = true
31-
# end
27+
# Apply global CSP policy from configuration
28+
Rails.application.config.to_prepare do
29+
policy_config = ThreeScale::ContentSecurityPolicy.policy_config
30+
31+
if policy_config.present?
32+
Rails.application.config.content_security_policy do |policy|
33+
# Apply each directive from YAML config
34+
policy_config.each do |directive, sources|
35+
next unless sources.is_a?(Array)
36+
37+
method_name = directive.to_s
38+
if policy.respond_to?(method_name)
39+
policy.public_send(method_name, *sources)
40+
end
41+
end
42+
43+
# Add report-uri if configured
44+
if (uri = ThreeScale::ContentSecurityPolicy.report_uri)
45+
policy.report_uri uri
46+
end
47+
end
48+
end
49+
end
50+
else
51+
# Fallback to permissive policy when config is disabled
52+
Rails.application.config.to_prepare do
53+
Rails.application.config.content_security_policy do |policy|
54+
policy.default_src '*', :data, :mediastream, :blob, :filesystem, :ws, :wss, :unsafe_eval, :unsafe_inline
55+
end
56+
end
57+
end
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# frozen_string_literal: true
2+
3+
module ThreeScale
4+
module ContentSecurityPolicy
5+
class << self
6+
def config
7+
@config ||= Rails.configuration.three_scale.content_security_policy
8+
end
9+
10+
def enabled?
11+
config&.enabled == true
12+
end
13+
14+
def policy_config
15+
config&.policy&.to_h || {}
16+
end
17+
18+
def report_only?
19+
config&.report_only == true
20+
end
21+
22+
def report_uri
23+
config&.report_uri.presence
24+
end
25+
26+
def nonce_enabled?
27+
config&.nonce_generator == true
28+
end
29+
30+
def nonce_directives
31+
config&.nonce_directives&.map(&:to_s) || []
32+
end
33+
end
34+
end
35+
end

0 commit comments

Comments
 (0)