Class: Shugoi::Csp
- Inherits:
-
Object
- Object
- Shugoi::Csp
- Defined in:
- lib/shugoi/csp.rb
Overview
Construction + fusion de la Content-Security-Policy — parité avec csp.ts (module Node).
Le module npm pose la CSP fusionnée sur chaque réponse (y compris les pages passées) :
la base inclut l'origine de l'API (pour le guard client), les directives additionnelles
et, en split-render, unsafe-eval (nécessaire au bootcode eval([...])).
Constant Summary collapse
- SHUGOI_ORIGIN =
"https://shugoi.com".freeze
Class Method Summary collapse
-
.build(site_key:, api_origin: nil, extra_directives: {}, split_render: true) ⇒ String
Header CSP.
-
.merge(existing, added) ⇒ Object
Fusionne une CSP existante (posée par l'app client) avec celle du module.
-
.origin_of(base_url) ⇒ Object
Retourne l'origine (schéma://host) d'une base URL, nil sinon.
-
.parse(csp) ⇒ Object
Parse une CSP en Hash<String, Set
>.
Class Method Details
.build(site_key:, api_origin: nil, extra_directives: {}, split_render: true) ⇒ String
Returns header CSP.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/shugoi/csp.rb', line 30 def self.build(site_key:, api_origin: nil, extra_directives: {}, split_render: true) api = [SHUGOI_ORIGIN, api_origin].compact.uniq merged = { "default-src" => ["'self'"], "script-src" => ["'self'", "'unsafe-inline'", "'unsafe-eval'", *api], "connect-src" => ["'self'", *api], "style-src" => ["'self'", "'unsafe-inline'", *api], "font-src" => ["'self'", *api, "data:"], "img-src" => ["'self'", *api, "data:", "blob:"], "frame-ancestors" => ["'self'"], "object-src" => ["'none'"], "base-uri" => ["'self'"], "form-action" => ["'self'"] } extra_directives.each do |key, values| merged[key] = ((merged[key] || []) + Array(values)).uniq end if split_render == false merged["script-src"] = (merged["script-src"] || []).reject { |v| v == "'unsafe-eval'" } end merged.map { |k, v| "#{k} #{v.join(' ')}" }.join("; ") end |
.merge(existing, added) ⇒ Object
Fusionne une CSP existante (posée par l'app client) avec celle du module. Spec CSP : le mot-clé 'none' doit être SEUL dans une directive — sinon il est ignoré par le navigateur. Lors d'un merge, on garde uniquement 'none' (le plus restrictif).
56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/shugoi/csp.rb', line 56 def self.merge(existing, added) return added if existing.to_s.empty? base = parse(existing) parse(added).each do |k, set| base[k] = (base[k] || Set.new) | set end base.each do |k, set| base[k] = Set.new(["'none'"]) if set.include?("'none'") && set.size > 1 end base.map { |k, v| "#{k} #{v.to_a.join(' ')}" }.join("; ") end |
.origin_of(base_url) ⇒ Object
Retourne l'origine (schéma://host) d'une base URL, nil sinon.
14 15 16 17 18 19 20 21 22 23 |
# File 'lib/shugoi/csp.rb', line 14 def self.origin_of(base_url) return nil if base_url.to_s.empty? u = URI.parse(base_url) return nil unless %w[http https].include?(u.scheme) origin = "#{u.scheme}://#{u.host}" origin += ":#{u.port}" if u.port && u.port != u.default_port origin rescue URI::InvalidURIError nil end |
.parse(csp) ⇒ Object
Parse une CSP en Hash<String, Set
69 70 71 72 73 74 75 76 77 |
# File 'lib/shugoi/csp.rb', line 69 def self.parse(csp) out = {} csp.to_s.split(";").each do |part| name, *vals = part.strip.split(/\s+/) next if name.nil? || name.empty? out[name] = (out[name] || Set.new) | vals end out end |