mirror of
https://github.com/opf/openproject.git
synced 2026-06-13 19:20:00 +00:00
Move CSP to Rails
This commit is contained in:
@@ -138,9 +138,6 @@ gem "rack-protection", "~> 3.2.0"
|
||||
# https://github.com/kickstarter/rack-attack
|
||||
gem "rack-attack", "~> 6.7.0"
|
||||
|
||||
# CSP headers
|
||||
gem "secure_headers", "~> 7.1.0"
|
||||
|
||||
# Browser detection for incompatibility checks
|
||||
gem "browser", "~> 6.2.0"
|
||||
|
||||
|
||||
@@ -1167,7 +1167,6 @@ GEM
|
||||
nokogiri (>= 1.16.8)
|
||||
scimitar (2.11.0)
|
||||
rails (>= 7.0)
|
||||
secure_headers (7.1.0)
|
||||
securerandom (0.4.1)
|
||||
selenium-devtools (0.138.0)
|
||||
selenium-webdriver (~> 4.2)
|
||||
@@ -1515,7 +1514,6 @@ DEPENDENCIES
|
||||
rubytree (~> 2.1.0)
|
||||
sanitize (~> 7.0.0)
|
||||
scimitar (~> 2.11)
|
||||
secure_headers (~> 7.1.0)
|
||||
selenium-devtools
|
||||
selenium-webdriver (~> 4.20)
|
||||
semantic (~> 1.6.1)
|
||||
@@ -1948,7 +1946,6 @@ CHECKSUMS
|
||||
safety_net_attestation (0.4.0) sha256=96be2d74e7ed26453a51894913449bea0e072f44490021545ac2d1c38b0718ce
|
||||
sanitize (7.0.0) sha256=269d1b9d7326e69307723af5643ec032ff86ad616e72a3b36d301ac75a273984
|
||||
scimitar (2.11.0) sha256=77cf779a843be7d572046acdcf0a1829bd3b1c33db993fa83faf7f1863d8c625
|
||||
secure_headers (7.1.0) sha256=6b1f9d5f9507af2948f4636452c41c09371927836396c2185438ffdf0a731124
|
||||
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
|
||||
selenium-devtools (0.138.0) sha256=596c08e114342dd89513bc3d18bbb2ae39e532864dbc25c09a48fb9922fc5b7b
|
||||
selenium-webdriver (4.34.0) sha256=ec7bb718cbe66fe2b247d8ca5e6ba26caed0976d76579d7cb2fadd8dae8b271e
|
||||
|
||||
@@ -56,6 +56,7 @@ class ApplicationController < ActionController::Base
|
||||
include OpenProjectErrorHelper
|
||||
include Security::DefaultUrlOptions
|
||||
include OpModalFlashable
|
||||
include DynamicContentSecurityPolicy
|
||||
|
||||
layout "base"
|
||||
|
||||
|
||||
+18
-11
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) the OpenProject GmbH
|
||||
@@ -25,18 +27,23 @@
|
||||
#
|
||||
# See COPYRIGHT and LICENSE files for more details.
|
||||
#++
|
||||
#
|
||||
|
||||
module OpenProject::Patches::SecureHeadersTurboAwareNonce
|
||||
def content_security_policy_script_nonce(request)
|
||||
if request.env["HTTP_TURBO_REFERRER"].present?
|
||||
request.env[SecureHeaders::NONCE_KEY] ||= request.env["HTTP_X_TURBO_NONCE"]
|
||||
module DynamicContentSecurityPolicy
|
||||
##
|
||||
# Dynamically append sources to CSP directives
|
||||
# This replaces the secure_headers named append functionality
|
||||
def append_content_security_policy_directives(directives)
|
||||
current_policy = current_content_security_policy
|
||||
directives.each do |directive, source_values|
|
||||
current_value = current_policy.send(directive) || current_policy.default_src
|
||||
new_values =
|
||||
if current_value == %w('none') # rubocop:disable Lint/PercentStringArray
|
||||
source_values.compact.uniq
|
||||
else
|
||||
(current_value + source_values).compact.uniq
|
||||
end
|
||||
|
||||
request.content_security_policy.send(directive, *new_values)
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
OpenProject::Patches.patch_gem_version "secure_headers", "7.1.0" do
|
||||
SecureHeaders.singleton_class.prepend OpenProject::Patches::SecureHeadersTurboAwareNonce
|
||||
end
|
||||
@@ -219,6 +219,12 @@ module ApplicationHelper
|
||||
end
|
||||
end
|
||||
|
||||
# Backward compatibility helper for secure_headers gem migration
|
||||
# Rails built-in CSP equivalent of nonced_javascript_tag
|
||||
def nonced_javascript_tag(**, &)
|
||||
javascript_tag(nonce: true, **, &)
|
||||
end
|
||||
|
||||
def to_path_param(path)
|
||||
path.to_s
|
||||
end
|
||||
|
||||
@@ -66,7 +66,7 @@ module FrontendAssetHelper
|
||||
end
|
||||
|
||||
def nonced_javascript_include_tag(path, **)
|
||||
javascript_include_tag(path, nonce: content_security_policy_script_nonce, **)
|
||||
javascript_include_tag(path, nonce: content_security_policy_nonce, **)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -29,10 +29,8 @@
|
||||
module SecureHeadersHelper
|
||||
##
|
||||
# Output a rails +csp_meta_tag+ compatible tag
|
||||
# while we're still using the +secure_headers+ gem.
|
||||
# using Rails built-in CSP functionality.
|
||||
def secure_header_csp_meta_tag
|
||||
tag :meta,
|
||||
name: "csp-nonce",
|
||||
content: content_security_policy_script_nonce
|
||||
csp_meta_tag
|
||||
end
|
||||
end
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<%= render "common/favicons" %>
|
||||
|
||||
<%# Allow gon output when necessary %>
|
||||
<%= include_gon(nonce: content_security_policy_script_nonce) %>
|
||||
<%= include_gon(nonce: content_security_policy_nonce) %>
|
||||
|
||||
<%# Include CLI assets (development) or prod build assets %>
|
||||
<%= include_frontend_assets %>
|
||||
|
||||
@@ -125,9 +125,6 @@ module OpenProject
|
||||
# http://stackoverflow.com/questions/4590229
|
||||
config.middleware.use Rack::TempfileReaper
|
||||
|
||||
# Move secure_headers middleware to after the ShowExceptions
|
||||
config.middleware.move_after ActionDispatch::ShowExceptions, SecureHeaders::Middleware
|
||||
|
||||
# Add lookbook preview paths when enabled
|
||||
if OpenProject::Configuration.lookbook_enabled?
|
||||
config.paths.add Primer::ViewComponents::Engine.root.join("app/components").to_s, eager_load: true
|
||||
|
||||
@@ -1,25 +1,100 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# Define an application-wide content security policy.
|
||||
# See the Securing Rails Applications Guide for more information:
|
||||
# https://guides.rubyonrails.org/security.html#content-security-policy-header
|
||||
|
||||
# Rails.application.configure do
|
||||
# config.content_security_policy do |policy|
|
||||
# policy.default_src :self, :https
|
||||
# policy.font_src :self, :https, :data
|
||||
# policy.img_src :self, :https, :data
|
||||
# policy.object_src :none
|
||||
# policy.script_src :self, :https
|
||||
# policy.style_src :self, :https
|
||||
# # Specify URI for violation reports
|
||||
# # policy.report_uri "/csp-violation-report-endpoint"
|
||||
# end
|
||||
#
|
||||
# # Generate session nonces for permitted importmap, inline scripts, and inline styles.
|
||||
# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
|
||||
# config.content_security_policy_nonce_directives = %w(script-src style-src)
|
||||
#
|
||||
# # Report violations without enforcing the policy.
|
||||
# # config.content_security_policy_report_only = true
|
||||
# end
|
||||
Rails.application.config.after_initialize do
|
||||
Rails.application.configure do
|
||||
config.content_security_policy do |policy|
|
||||
# Valid for assets
|
||||
assets_src = ["'self'"]
|
||||
asset_host = OpenProject::Configuration.rails_asset_host
|
||||
assets_src << asset_host if asset_host.present?
|
||||
|
||||
# Valid for iframes
|
||||
frame_src = %w['self' https://player.vimeo.com https://www.youtube.com] # rubocop:disable Lint/PercentStringArray
|
||||
frame_src << OpenProject::Configuration[:security_badge_url]
|
||||
|
||||
# Default src
|
||||
default_src = %w('self') # rubocop:disable Lint/PercentStringArray
|
||||
|
||||
# Attachment uploaders
|
||||
default_src += OpenProject::Configuration.remote_storage_hosts
|
||||
|
||||
# Chargebee self-service
|
||||
frame_src += [
|
||||
"https://js.chargebee.com/",
|
||||
"#{OpenProject::Configuration.enterprise_chargebee_site}.chargebee.com"
|
||||
]
|
||||
|
||||
default_src << "#{OpenProject::Configuration.enterprise_chargebee_site}.chargebee.com"
|
||||
|
||||
# Allow requests to CLI in dev mode
|
||||
connect_src = default_src + [OpenProject::Configuration.enterprise_trial_creation_host]
|
||||
|
||||
# Rules for media (e.g. video sources)
|
||||
media_src = default_src
|
||||
media_src << asset_host if asset_host.present?
|
||||
|
||||
if OpenProject::Configuration.appsignal_frontend_key
|
||||
connect_src += ["https://appsignal-endpoint.net"]
|
||||
end
|
||||
|
||||
# Add proxy configuration for Angular CLI to csp
|
||||
if FrontendAssetHelper.assets_proxied?
|
||||
proxied = ["ws://#{Setting.host_name}", "http://#{Setting.host_name}",
|
||||
FrontendAssetHelper.cli_proxy.sub("http", "ws"), FrontendAssetHelper.cli_proxy]
|
||||
connect_src += proxied
|
||||
assets_src += proxied
|
||||
media_src += proxied
|
||||
end
|
||||
|
||||
# Allow to extend the script-src in specific situations
|
||||
script_src = assets_src + %w(js.chargebee.com)
|
||||
|
||||
# Allow unsafe-eval for rack-mini-profiler
|
||||
if Rails.env.development? && ENV.fetch("OPENPROJECT_RACK_PROFILER_ENABLED", false)
|
||||
script_src += %w('unsafe-eval') # rubocop:disable Lint/PercentStringArray
|
||||
end
|
||||
|
||||
# Allow ANDI bookmarklet to run in development mode
|
||||
# https://www.ssa.gov/accessibility/andi/help/install.html
|
||||
if Rails.env.development?
|
||||
script_src += ["https://www.ssa.gov"]
|
||||
assets_src += ["https://www.ssa.gov"]
|
||||
end
|
||||
|
||||
# Configure CSP directives
|
||||
policy.default_src(*default_src)
|
||||
policy.base_uri("'self'")
|
||||
policy.font_src(*assets_src, "data:", "'self'")
|
||||
policy.form_action(*default_src)
|
||||
policy.frame_src(*frame_src, "'self'")
|
||||
policy.frame_ancestors("'self'")
|
||||
policy.img_src("*", "data:", "blob:")
|
||||
policy.script_src(*script_src)
|
||||
policy.script_src_attr("'none'")
|
||||
policy.style_src(*assets_src, "'unsafe-inline'")
|
||||
policy.object_src(OpenProject::Configuration[:security_badge_url])
|
||||
policy.connect_src(*connect_src)
|
||||
policy.media_src(*media_src)
|
||||
end
|
||||
|
||||
# Generate session nonces for permitted importmap, inline scripts, and inline styles.
|
||||
# This handles Turbo integration natively
|
||||
config.content_security_policy_nonce_generator = lambda do |request|
|
||||
# Use Turbo nonce if available (for Turbo navigation)
|
||||
if request.env["HTTP_TURBO_REFERRER"].present? && request.env["HTTP_X_TURBO_NONCE"].present?
|
||||
request.env["HTTP_X_TURBO_NONCE"]
|
||||
else
|
||||
# Generate a new nonce based on session
|
||||
SecureRandom.base64(16)
|
||||
end
|
||||
end
|
||||
|
||||
config.content_security_policy_nonce_directives = %w(script-src)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,49 +22,4 @@ Rails.application.configure do
|
||||
# Show notes first, all other panels next
|
||||
config.lookbook.preview_inspector.drawer_panels = [:notes, "*"]
|
||||
config.lookbook.ui_theme = "blue"
|
||||
|
||||
SecureHeaders::Configuration.named_append(:lookbook) do
|
||||
proxied =
|
||||
if FrontendAssetHelper.assets_proxied?
|
||||
["ws://#{Setting.host_name}", "http://#{Setting.host_name}",
|
||||
FrontendAssetHelper.cli_proxy.sub("http", "ws"), FrontendAssetHelper.cli_proxy]
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
{
|
||||
script_src: proxied + %w('unsafe-eval' 'unsafe-inline' 'self'), # rubocop:disable Lint/PercentStringArray
|
||||
script_src_elem: proxied + %w('unsafe-eval' 'unsafe-inline' 'self'), # rubocop:disable Lint/PercentStringArray
|
||||
style_src: proxied + %w('self' 'unsafe-inline'), # rubocop:disable Lint/PercentStringArray
|
||||
style_src_attr: proxied + %w('self' 'unsafe-inline') # rubocop:disable Lint/PercentStringArray
|
||||
}
|
||||
end
|
||||
|
||||
# rubocop:disable Lint/ConstantDefinitionInBlock
|
||||
module LookbookCspExtender
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
before_action do
|
||||
use_content_security_policy_named_append :lookbook
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable Lint/ConstantDefinitionInBlock
|
||||
|
||||
Rails.application.reloader.to_prepare do
|
||||
Lookbook.add_input_type(:octicon, "lookbook/previews/inputs/octicon")
|
||||
|
||||
[
|
||||
Lookbook::ApplicationController,
|
||||
Lookbook::PreviewController,
|
||||
Lookbook::PreviewsController,
|
||||
Lookbook::PageController,
|
||||
Lookbook::PagesController,
|
||||
Lookbook::InspectorController,
|
||||
Lookbook::EmbedsController
|
||||
].each do |controller|
|
||||
controller.include LookbookCspExtender
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) the OpenProject GmbH
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License version 3.
|
||||
#
|
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
# Copyright (C) 2010-2013 the ChiliProject Team
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# See COPYRIGHT and LICENSE files for more details.
|
||||
#++
|
||||
|
||||
# rubocop:disable Lint/PercentStringArray
|
||||
Rails.application.config.after_initialize do
|
||||
SecureHeaders::Configuration.default do |config|
|
||||
config.cookies = {
|
||||
secure: true,
|
||||
httponly: true
|
||||
}
|
||||
|
||||
# Let Rails ActionDispatch::SSL middleware handle the Strict-Transport-Security header
|
||||
config.hsts = SecureHeaders::OPT_OUT
|
||||
|
||||
config.x_frame_options = "SAMEORIGIN"
|
||||
config.x_content_type_options = "nosniff"
|
||||
config.x_xss_protection = "1; mode=block"
|
||||
config.x_permitted_cross_domain_policies = "none"
|
||||
config.referrer_policy = "origin-when-cross-origin"
|
||||
|
||||
# Valid for assets
|
||||
assets_src = ["'self'"]
|
||||
asset_host = OpenProject::Configuration.rails_asset_host
|
||||
assets_src << asset_host if asset_host.present?
|
||||
|
||||
# Valid for iframes
|
||||
frame_src = %w['self' https://player.vimeo.com https://www.youtube.com]
|
||||
frame_src << OpenProject::Configuration[:security_badge_url]
|
||||
|
||||
# Default src
|
||||
default_src = %w('self')
|
||||
|
||||
# Attachment uploaders
|
||||
default_src += OpenProject::Configuration.remote_storage_hosts
|
||||
|
||||
# Chargebee self-service
|
||||
frame_src += [
|
||||
"https://js.chargebee.com/",
|
||||
"#{OpenProject::Configuration.enterprise_chargebee_site}.chargebee.com"
|
||||
]
|
||||
|
||||
default_src << "#{OpenProject::Configuration.enterprise_chargebee_site}.chargebee.com"
|
||||
|
||||
# Allow requests to CLI in dev mode
|
||||
connect_src = default_src + [OpenProject::Configuration.enterprise_trial_creation_host]
|
||||
|
||||
# Rules for media (e.g. video sources)
|
||||
media_src = default_src
|
||||
media_src << asset_host if asset_host.present?
|
||||
|
||||
if OpenProject::Configuration.appsignal_frontend_key
|
||||
connect_src += ["https://appsignal-endpoint.net"]
|
||||
end
|
||||
|
||||
# Add proxy configuration for Angular CLI to csp
|
||||
if FrontendAssetHelper.assets_proxied?
|
||||
proxied = ["ws://#{Setting.host_name}", "http://#{Setting.host_name}",
|
||||
FrontendAssetHelper.cli_proxy.sub("http", "ws"), FrontendAssetHelper.cli_proxy]
|
||||
connect_src += proxied
|
||||
assets_src += proxied
|
||||
media_src += proxied
|
||||
end
|
||||
|
||||
# Allow to extend the script-src in specific situations
|
||||
script_src = assets_src + %w(js.chargebee.com)
|
||||
|
||||
# Allow unsafe-eval for rack-mini-profiler
|
||||
if Rails.env.development? && ENV.fetch("OPENPROJECT_RACK_PROFILER_ENABLED", false)
|
||||
script_src += %w('unsafe-eval')
|
||||
end
|
||||
|
||||
# Allow ANDI bookmarklet to run in development mode
|
||||
# https://www.ssa.gov/accessibility/andi/help/install.html
|
||||
if Rails.env.development?
|
||||
script_src += ["https://www.ssa.gov"]
|
||||
assets_src += ["https://www.ssa.gov"]
|
||||
end
|
||||
|
||||
config.csp = {
|
||||
preserve_schemes: true,
|
||||
# Don't append unsafe-inline in CSP as a fallback
|
||||
disable_nonce_backwards_compatibility: true,
|
||||
|
||||
# Fallback when no value is defined
|
||||
default_src:,
|
||||
# Allowed uri in <base> tag
|
||||
base_uri: %w('self'),
|
||||
|
||||
# Allow fonts from self, asset host, or DATA uri
|
||||
font_src: assets_src + %w(data: 'self'),
|
||||
# Form targets can only be self
|
||||
form_action: default_src,
|
||||
# Allow iframe from vimeo (welcome video)
|
||||
frame_src: frame_src + %w('self'),
|
||||
frame_ancestors: %w('self'),
|
||||
# Allow images from anywhere including data urls and blobs (used in resizing)
|
||||
img_src: %w(* data: blob:),
|
||||
# Allow scripts from self
|
||||
script_src:,
|
||||
script_src_attr: %w('none'),
|
||||
# Allow unsafe-inline styles
|
||||
style_src: assets_src + %w('unsafe-inline'),
|
||||
# Allow object-src from Release API
|
||||
object_src: [OpenProject::Configuration[:security_badge_url]],
|
||||
|
||||
# Connect sources for CLI in dev mode
|
||||
connect_src:,
|
||||
|
||||
# Allow videos from self and from the asset proxy in dev mode.
|
||||
media_src:
|
||||
}
|
||||
end
|
||||
end
|
||||
# rubocop:enable Lint/PercentStringArray
|
||||
@@ -30,11 +30,11 @@ module ::Recaptcha
|
||||
# Request verification form
|
||||
def perform
|
||||
if OpenProject::Recaptcha::Configuration.use_hcaptcha?
|
||||
use_content_security_policy_named_append(:hcaptcha)
|
||||
allow_captcha_service(:hcaptcha)
|
||||
elsif OpenProject::Recaptcha::Configuration.use_turnstile?
|
||||
use_content_security_policy_named_append(:turnstile)
|
||||
allow_captcha_service(:turnstile)
|
||||
elsif OpenProject::Recaptcha::Configuration.use_recaptcha?
|
||||
use_content_security_policy_named_append(:recaptcha)
|
||||
allow_captcha_service(:recaptcha)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -163,5 +163,31 @@ module ::Recaptcha
|
||||
def failure_stage_redirect
|
||||
redirect_to authentication_stage_failure_path :recaptcha
|
||||
end
|
||||
|
||||
##
|
||||
# Add CAPTCHA service CSP rules
|
||||
def allow_captcha_service(service_type)
|
||||
case service_type.to_sym
|
||||
when :recaptcha
|
||||
append_content_security_policy_directives(
|
||||
frame_src: %w[https://www.recaptcha.net/recaptcha/ https://www.gstatic.com/recaptcha/]
|
||||
)
|
||||
when :hcaptcha
|
||||
sources = %w[https://*.hcaptcha.com]
|
||||
append_content_security_policy_directives(
|
||||
frame_src: sources,
|
||||
script_src: sources,
|
||||
style_src: sources,
|
||||
connect_src: sources
|
||||
)
|
||||
when :turnstile
|
||||
sources = %w[https://challenges.cloudflare.com]
|
||||
append_content_security_policy_directives(
|
||||
frame_src: sources,
|
||||
style_src: sources,
|
||||
connect_src: sources
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<% input_name = "g-recaptcha-response" %>
|
||||
<input type="hidden" name="<%= input_name %>">
|
||||
<%= recaptcha_tags(
|
||||
nonce: content_security_policy_script_nonce,
|
||||
nonce: content_security_policy_nonce,
|
||||
callback: "submitRecaptchaForm",
|
||||
site_key: recaptcha_settings["website_key"]
|
||||
) %>
|
||||
@@ -29,7 +29,7 @@
|
||||
<% end %>
|
||||
<% elsif recaptcha_settings['recaptcha_type'] == ::OpenProject::Recaptcha::TYPE_V3 %>
|
||||
<%= recaptcha_v3 action: "login",
|
||||
nonce: content_security_policy_script_nonce,
|
||||
nonce: content_security_policy_nonce,
|
||||
callback: "submitRecaptchaForm",
|
||||
site_key: recaptcha_settings["website_key"] %>
|
||||
|
||||
|
||||
@@ -28,26 +28,6 @@ module OpenProject::Recaptcha
|
||||
end
|
||||
|
||||
config.after_initialize do
|
||||
SecureHeaders::Configuration.named_append(:recaptcha) do
|
||||
{
|
||||
frame_src: %w[https://www.recaptcha.net/recaptcha/ https://www.gstatic.com/recaptcha/]
|
||||
}
|
||||
end
|
||||
|
||||
SecureHeaders::Configuration.named_append(:hcaptcha) do
|
||||
value = %w(https://*.hcaptcha.com)
|
||||
keys = %i(frame_src script_src style_src connect_src)
|
||||
|
||||
keys.index_with value
|
||||
end
|
||||
|
||||
SecureHeaders::Configuration.named_append(:turnstile) do
|
||||
value = %w(https://challenges.cloudflare.com)
|
||||
keys = %i(frame_src style_src connect_src)
|
||||
|
||||
keys.index_with value
|
||||
end
|
||||
|
||||
OpenProject::Authentication::Stage.register(
|
||||
:recaptcha,
|
||||
nil,
|
||||
|
||||
@@ -65,19 +65,23 @@ class WithDirectUploads
|
||||
def around(example)
|
||||
example.metadata[:javascript_driver] = example.metadata[:driver] = :chrome_billy
|
||||
|
||||
csp_config = SecureHeaders::Configuration.instance_variable_get(:@default_config).csp
|
||||
# Temporarily modify CSP for direct uploads testing
|
||||
original_csp = Rails.application.config.content_security_policy
|
||||
|
||||
connect_src = csp_config[:connect_src].dup
|
||||
form_action = csp_config[:form_action].dup
|
||||
Rails.application.config.content_security_policy do |policy|
|
||||
# Copy existing policy and add test bucket domain
|
||||
original_csp.call(policy) if original_csp
|
||||
|
||||
# Add test bucket to connect_src and form_action for direct uploads
|
||||
policy.connect_src(*(policy.instance_variable_get(:@directives)[:connect_src] || []), "test-bucket.s3.amazonaws.com")
|
||||
policy.form_action(*(policy.instance_variable_get(:@directives)[:form_action] || []), "test-bucket.s3.amazonaws.com")
|
||||
end
|
||||
|
||||
begin
|
||||
csp_config[:connect_src] << "test-bucket.s3.amazonaws.com"
|
||||
csp_config[:form_action] << "test-bucket.s3.amazonaws.com"
|
||||
|
||||
example.run
|
||||
ensure
|
||||
csp_config[:connect_src] = connect_src
|
||||
csp_config[:form_action] = form_action
|
||||
# Restore original CSP configuration
|
||||
Rails.application.config.content_security_policy = original_csp
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user