diff --git a/app/controllers/concerns/idv/document_capture_concern.rb b/app/controllers/concerns/idv/document_capture_concern.rb
index d28f0d68398..3eb49969f0f 100644
--- a/app/controllers/concerns/idv/document_capture_concern.rb
+++ b/app/controllers/concerns/idv/document_capture_concern.rb
@@ -3,6 +3,14 @@ module DocumentCaptureConcern
def override_document_capture_step_csp
return if params[:step] != 'document_capture'
+ if FeatureManagement.rails_csp_tooling_enabled?
+ override_document_capture_step_csp_with_rails_csp_tooling
+ else
+ override_document_capture_step_csp_with_secure_headers
+ end
+ end
+
+ def override_document_capture_step_csp_with_secure_headers
SecureHeaders.append_content_security_policy_directives(
request,
# required to run wasm until wasm-eval is available
@@ -13,5 +21,13 @@ def override_document_capture_step_csp
img_src: ['blob:'],
)
end
+
+ def override_document_capture_step_csp_with_rails_csp_tooling
+ policy = current_content_security_policy
+ policy.script_src(*policy.script_src, :unsafe_eval)
+ policy.style_src(*policy.style_src, :unsafe_inline)
+ policy.img_src(*policy.img_src, 'blob:')
+ request.content_security_policy = policy
+ end
end
end
diff --git a/app/controllers/concerns/piv_cac_concern.rb b/app/controllers/concerns/piv_cac_concern.rb
index a0d426d2fa4..a305c4d42a9 100644
--- a/app/controllers/concerns/piv_cac_concern.rb
+++ b/app/controllers/concerns/piv_cac_concern.rb
@@ -28,10 +28,7 @@ def piv_session
end
def set_piv_cac_setup_csp_form_action_uris
- override_content_security_policy_directives(
- form_action: piv_cac_setup_csp_form_action_uris,
- preserve_schemes: true,
- )
+ override_form_action_csp(piv_cac_setup_csp_form_action_uris)
end
def piv_cac_setup_csp_form_action_uris
diff --git a/app/controllers/concerns/secure_headers_concern.rb b/app/controllers/concerns/secure_headers_concern.rb
index c1f2c9e478d..ff3e271151e 100644
--- a/app/controllers/concerns/secure_headers_concern.rb
+++ b/app/controllers/concerns/secure_headers_concern.rb
@@ -5,19 +5,31 @@ def apply_secure_headers_override
return if stored_url_for_user.blank?
authorize_form = OpenidConnectAuthorizeForm.new(authorize_params)
-
return unless authorize_form.valid?
- override_csp_with_uris
+ override_form_action_csp(csp_uris)
+ end
+
+ def override_form_action_csp(uris)
+ if FeatureManagement.rails_csp_tooling_enabled?
+ apply_secure_headers_override_with_rails_csp_tooling(uris)
+ else
+ apply_secure_headers_override_with_secure_headers(uris)
+ end
end
- def override_csp_with_uris
+ def apply_secure_headers_override_with_secure_headers(uris)
override_content_security_policy_directives(
- form_action: csp_uris,
- preserve_schemes: true,
+ form_action: uris,
)
end
+ def apply_secure_headers_override_with_rails_csp_tooling(uris)
+ policy = current_content_security_policy
+ policy.form_action(*uris)
+ request.content_security_policy = policy
+ end
+
def csp_uris
return ["'self'"] if stored_url_for_user.blank?
# Returns fully formed CSP array w/"'self'" and redirect_uris
diff --git a/app/controllers/openid_connect/authorization_controller.rb b/app/controllers/openid_connect/authorization_controller.rb
index b5b423110bc..eda2da11c66 100644
--- a/app/controllers/openid_connect/authorization_controller.rb
+++ b/app/controllers/openid_connect/authorization_controller.rb
@@ -12,7 +12,7 @@ class AuthorizationController < ApplicationController
before_action :sign_out_if_prompt_param_is_login_and_user_is_signed_in, only: [:index]
before_action :store_request, only: [:index]
before_action :check_sp_active, only: [:index]
- before_action :override_csp_with_uris, only: [:index]
+ before_action :apply_secure_headers_override, only: [:index]
before_action :confirm_user_is_authenticated_with_fresh_mfa, only: :index
before_action :prompt_for_password_if_ial2_request_and_pii_locked, only: [:index]
before_action :bump_auth_count, only: [:index]
diff --git a/app/controllers/saml_idp_controller.rb b/app/controllers/saml_idp_controller.rb
index ecfb0878783..d6d4d0d1565 100644
--- a/app/controllers/saml_idp_controller.rb
+++ b/app/controllers/saml_idp_controller.rb
@@ -11,6 +11,7 @@ class SamlIdpController < ApplicationController
include VerifyProfileConcern
include AuthorizationCountConcern
include BillableEventTrackable
+ include SecureHeadersConcern
prepend_before_action :skip_session_load, only: :metadata
prepend_before_action :skip_session_expiration, only: :metadata
@@ -118,7 +119,7 @@ def render_template_for(message, action_url, type)
csp_uris = SecureHeadersAllowList.csp_with_sp_redirect_uris(
action_url, decorated_session.sp_redirect_uris
)
- override_content_security_policy_directives(form_action: csp_uris)
+ override_form_action_csp(csp_uris)
render(
template: 'saml_idp/shared/saml_post_binding',
diff --git a/app/helpers/secure_headers_helper.rb b/app/helpers/secure_headers_helper.rb
new file mode 100644
index 00000000000..0c018da20d7
--- /dev/null
+++ b/app/helpers/secure_headers_helper.rb
@@ -0,0 +1,34 @@
+module SecureHeadersHelper
+ def backwards_compatible_javascript_tag(*args, **opts, &block)
+ if FeatureManagement.rails_csp_tooling_enabled?
+ javascript_tag(*args, opts.merge(nonce: true), &block)
+ else
+ nonced_javascript_tag(*args, **opts, &block)
+ end
+ end
+
+ def add_document_capture_image_urls_to_csp(request, urls)
+ cleaned_urls = urls.compact.map do |url|
+ URI(url).tap { |uri| uri.query = nil }.to_s
+ end
+
+ if FeatureManagement.rails_csp_tooling_enabled?
+ add_document_capture_image_urls_to_csp_with_rails_csp_tooling(request, cleaned_urls)
+ else
+ add_document_capture_image_urls_to_csp_with_secure_headers(request, cleaned_urls)
+ end
+ end
+
+ def add_document_capture_image_urls_to_csp_with_secure_headers(request, urls)
+ SecureHeaders.append_content_security_policy_directives(
+ request,
+ connect_src: urls,
+ )
+ end
+
+ def add_document_capture_image_urls_to_csp_with_rails_csp_tooling(request, urls)
+ policy = request.content_security_policy.clone
+ policy.connect_src(*policy.connect_src, *urls)
+ request.content_security_policy = policy
+ end
+end
diff --git a/app/views/idv/shared/_document_capture.html.erb b/app/views/idv/shared/_document_capture.html.erb
index 2652a17d180..6bfe52e6503 100644
--- a/app/views/idv/shared/_document_capture.html.erb
+++ b/app/views/idv/shared/_document_capture.html.erb
@@ -4,17 +4,10 @@
<%= tag.meta name: 'acuant-sdk-initialization-creds', content: IdentityConfig.store.acuant_sdk_initialization_creds %>
<%= stylesheet_link_tag 'document-capture' %>
<% end %>
-<% SecureHeaders.append_content_security_policy_directives(
+<% add_document_capture_image_urls_to_csp(
request,
- connect_src: [
- front_image_upload_url,
- back_image_upload_url,
- selfie_image_upload_url,
- ].
- compact.
- map { |url| URI(url).tap { |uri| uri.query = nil }.to_s },
+ [front_image_upload_url, back_image_upload_url, selfie_image_upload_url],
)
-
session_id = flow_session[:document_capture_session_uuid]
%>
<%= tag.div id: 'document-capture-form', data: {
@@ -147,7 +140,7 @@
) %>
-<%= nonced_javascript_tag do %>
+<%= backwards_compatible_javascript_tag do %>
<% asset_keys = [
'close-white-alt.svg',
'id-card.svg',
diff --git a/app/views/layouts/base.html.erb b/app/views/layouts/base.html.erb
index 0bc90419613..6e1a48d1f7d 100644
--- a/app/views/layouts/base.html.erb
+++ b/app/views/layouts/base.html.erb
@@ -23,7 +23,7 @@
) %>
- <%= nonced_javascript_tag do %>
+ <%= backwards_compatible_javascript_tag do %>
document.documentElement.className = document.documentElement.className.replace(/\bno-js\b/, 'js');
<% end %>
<%= preload_link_tag font_url('identity-style-guide/dist/assets/fonts/source-sans-pro/sourcesanspro-regular-webfont.woff2') %>
diff --git a/app/views/shared/_banner.html.erb b/app/views/shared/_banner.html.erb
index 292ff01cc30..1799d82dcef 100644
--- a/app/views/shared/_banner.html.erb
+++ b/app/views/shared/_banner.html.erb
@@ -22,7 +22,7 @@
- <%= nonced_javascript_tag do %>
+ <%= backwards_compatible_javascript_tag do %>
document.getElementById('gov-banner').setAttribute('hidden', '');
<% end %>
diff --git a/app/views/shared/_dap_analytics.html.erb b/app/views/shared/_dap_analytics.html.erb
index 951e42b3f02..effe6144aa8 100644
--- a/app/views/shared/_dap_analytics.html.erb
+++ b/app/views/shared/_dap_analytics.html.erb
@@ -1,4 +1,4 @@
<% dap_source = 'https://dap.digitalgov.gov/Universal-Federated-Analytics-Min.js?agency=GSA&subagency=TTS' %>
-<%= nonced_javascript_tag({ src: dap_source, async: true, id: '_fed_an_ua_tag' }) do %>
+<%= backwards_compatible_javascript_tag({ src: dap_source, async: true, id: '_fed_an_ua_tag' }) do %>
<% end %>
diff --git a/app/views/shared/_failure.html.erb b/app/views/shared/_failure.html.erb
index ad33067e6ca..884f0910377 100644
--- a/app/views/shared/_failure.html.erb
+++ b/app/views/shared/_failure.html.erb
@@ -23,5 +23,5 @@
<% presenter.next_steps.each do |step| %>
<%== step %>
<% end; if presenter.js %>
- <%= nonced_javascript_tag presenter.js %>
+ <%= backwards_compatible_javascript_tag presenter.js %>
<% end %>
diff --git a/app/views/shared/newrelic/_browser_instrumentation.html.erb b/app/views/shared/newrelic/_browser_instrumentation.html.erb
index a544f6c2ddb..c5289006f6e 100644
--- a/app/views/shared/newrelic/_browser_instrumentation.html.erb
+++ b/app/views/shared/newrelic/_browser_instrumentation.html.erb
@@ -1,4 +1,4 @@
-<%= nonced_javascript_tag do %>
+<%= backwards_compatible_javascript_tag do %>
window.NREUM||(NREUM={}),__nr_require=function(t,e,n){function r(n){if(!e[n]){var o=e[n]={exports:{}};t[n][0].call(o.exports,function(e){var o=t[n][1][e];return r(o||e)},o,o.exports)}return e[n].exports}if("function"==typeof __nr_require)return __nr_require;for(var o=0;o
0&&(d-=1)}),c.on("internal-error",function(t){i("ierr",[t,(new Date).getTime(),!0])})},{}],3:[function(t,e,n){t("loader").features.ins=!0},{}],4:[function(t,e,n){function r(t){}if(window.performance&&window.performance.timing&&window.performance.getEntriesByType){var o=t("ee"),i=t("handle"),a=t(8),c=t(7),s="learResourceTimings",f="addEventListener",u="resourcetimingbufferfull",d="bstResource",l="resource",p="-start",h="-end",m="fn"+p,w="fn"+h,v="bstTimer",y="pushState";t("loader").features.stn=!0,t(6);var g=NREUM.o.EV;o.on(m,function(t,e){var n=t[0];n instanceof g&&(this.bstStart=Date.now())}),o.on(w,function(t,e){var n=t[0];n instanceof g&&i("bst",[n,e,this.bstStart,Date.now()])}),a.on(m,function(t,e,n){this.bstStart=Date.now(),this.bstType=n}),a.on(w,function(t,e){i(v,[e,this.bstStart,Date.now(),this.bstType])}),c.on(m,function(){this.bstStart=Date.now()}),c.on(w,function(t,e){i(v,[e,this.bstStart,Date.now(),"requestAnimationFrame"])}),o.on(y+p,function(t){this.time=Date.now(),this.startPath=location.pathname+location.hash}),o.on(y+h,function(t){i("bstHist",[location.pathname+location.hash,this.startPath,this.time])}),f in window.performance&&(window.performance["c"+s]?window.performance[f](u,function(t){i(d,[window.performance.getEntriesByType(l)]),window.performance["c"+s]()},!1):window.performance[f]("webkit"+u,function(t){i(d,[window.performance.getEntriesByType(l)]),window.performance["webkitC"+s]()},!1)),document[f]("scroll",r,!1),document[f]("keypress",r,!1),document[f]("click",r,!1)}},{}],5:[function(t,e,n){function r(t){for(var e=t;e&&!e.hasOwnProperty(u);)e=Object.getPrototypeOf(e);e&&o(e)}function o(t){c.inPlace(t,[u,d],"-",i)}function i(t,e){return t[1]}var a=t("ee").get("events"),c=t(17)(a,!0),s=t("gos"),f=XMLHttpRequest,u="addEventListener",d="removeEventListener";e.exports=a,"getPrototypeOf"in Object?(r(document),r(window),r(f.prototype)):f.prototype.hasOwnProperty(u)&&(o(window),o(f.prototype)),a.on(u+"-start",function(t,e){var n=t[1],r=s(n,"nr@wrapped",function(){function t(){if("function"==typeof n.handleEvent)return n.handleEvent.apply(n,arguments)}var e={object:t,"function":n}[typeof n];return e?c(e,"fn-",null,e.name||"anonymous"):n});this.wrapped=t[1]=r}),a.on(d+"-start",function(t){t[1]=this.wrapped||t[1]})},{}],6:[function(t,e,n){var r=t("ee").get("history"),o=t(17)(r);e.exports=r,o.inPlace(window.history,["pushState","replaceState"],"-")},{}],7:[function(t,e,n){var r=t("ee").get("raf"),o=t(17)(r),i="equestAnimationFrame";e.exports=r,o.inPlace(window,["r"+i,"mozR"+i,"webkitR"+i,"msR"+i],"raf-"),r.on("raf-start",function(t){t[0]=o(t[0],"fn-")})},{}],8:[function(t,e,n){function r(t,e,n){t[0]=a(t[0],"fn-",null,n)}function o(t,e,n){this.method=n,this.timerDuration="number"==typeof t[1]?t[1]:0,t[0]=a(t[0],"fn-",this,n)}var i=t("ee").get("timer"),a=t(17)(i),c="setTimeout",s="setInterval",f="clearTimeout",u="-start",d="-";e.exports=i,a.inPlace(window,[c,"setImmediate"],c+d),a.inPlace(window,[s],s+d),a.inPlace(window,[f,"clearImmediate"],f+d),i.on(s+u,r),i.on(c+u,o)},{}],9:[function(t,e,n){function r(t,e){d.inPlace(e,["onreadystatechange"],"fn-",c)}function o(){var t=this,e=u.context(t);t.readyState>3&&!e.resolved&&(e.resolved=!0,u.emit("xhr-resolved",[],t)),d.inPlace(t,w,"fn-",c)}function i(t){v.push(t),h&&(g=-g,b.data=g)}function a(){for(var t=0;t34||p<10)||window.opera||t.addEventListener("progress",function(t){e.lastSize=t.loaded},!1)}),f.on("open-xhr-start",function(t){this.params={method:t[0]},i(this,t[1]),this.metrics={}}),f.on("open-xhr-end",function(t,e){"loader_config"in NREUM&&"xpid"in NREUM.loader_config&&this.sameOrigin&&e.setRequestHeader("X-NewRelic-ID",NREUM.loader_config.xpid)}),f.on("send-xhr-start",function(t,e){var n=this.metrics,r=t[0],o=this;if(n&&r){var i=h(r);i&&(n.txSize=i)}this.startTime=(new Date).getTime(),this.listener=function(t){try{"abort"===t.type&&(o.params.aborted=!0),("load"!==t.type||o.called===o.totalCbs&&(o.onloadCalled||"function"!=typeof e.onload))&&o.end(e)}catch(n){try{f.emit("internal-error",[n])}catch(r){}}};for(var a=0;a",applicationID:"<%= IdentityConfig.store.newrelic_browser_app_id %>",sa:1}
<% end %>
diff --git a/config/application.yml.default b/config/application.yml.default
index bbd8b8948d1..fe75f67e10f 100644
--- a/config/application.yml.default
+++ b/config/application.yml.default
@@ -174,6 +174,7 @@ push_notifications_enabled: 'false'
pwned_passwords_file_path: 'pwned_passwords/pwned_passwords.txt'
rack_mini_profiler: 'false'
rack_timeout_service_timeout_seconds: '15'
+rails_csp_tooling_enabled: 'true'
rails_mailer_previews_enabled: 'false'
reauthn_window: '120'
recovery_code_length: '4'
diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb
new file mode 100644
index 00000000000..98cc6aa2336
--- /dev/null
+++ b/config/initializers/content_security_policy.rb
@@ -0,0 +1,67 @@
+require 'feature_management'
+
+if FeatureManagement.rails_csp_tooling_enabled?
+ # rubocop:disable Metrics/BlockLength
+ Rails.application.config.content_security_policy do |policy|
+ connect_src = ["'self'", '*.nr-data.net', '*.google-analytics.com', 'us.acas.acuant.net']
+
+ font_src = [:self, :data, IdentityConfig.store.asset_host.presence].compact
+
+ image_src = [
+ "'self'",
+ 'data:',
+ 'login.gov',
+ IdentityConfig.store.asset_host.presence,
+ 'idscangoweb.acuant.com',
+ IdentityConfig.store.aws_region.presence &&
+ "https://s3.#{IdentityConfig.store.aws_region}.amazonaws.com",
+ ].select(&:present?)
+
+ script_src = [
+ :self,
+ 'js-agent.newrelic.com',
+ '*.nr-data.net',
+ 'dap.digitalgov.gov',
+ '*.google-analytics.com',
+ IdentityConfig.store.asset_host.presence,
+ ].compact
+
+ script_src = [:self, :unsafe_eval] if !Rails.env.production?
+
+ style_src = [:self, IdentityConfig.store.asset_host.presence].compact
+
+ if ENV['WEBPACK_PORT']
+ connect_src << "ws://localhost:#{ENV['WEBPACK_PORT']}"
+ script_src << "localhost:#{ENV['WEBPACK_PORT']}"
+ end
+
+ if !IdentityConfig.store.disable_csp_unsafe_inline
+ script_src << :unsafe_inline
+ style_src << :unsafe_inline
+ end
+
+ if IdentityConfig.store.rails_mailer_previews_enabled
+ style_src << :unsafe_inline
+ # CSP 2.0 only; overriden by x_frame_options in some browsers
+ policy.frame_ancestors :self
+ end
+
+ policy.default_src :self
+ policy.child_src :self # CSP 2.0 only; replaces frame_src
+ policy.form_action :self
+ policy.block_all_mixed_content true # CSP 2.0 only;
+ policy.connect_src(*connect_src.flatten.compact)
+ policy.font_src(*font_src)
+ policy.img_src(*image_src)
+ policy.media_src :self
+ policy.object_src :none
+ policy.script_src(*script_src)
+ policy.style_src(*style_src)
+ policy.base_uri :self
+ end
+ # rubocop:enable Metrics/BlockLength
+ Rails.application.configure do
+ config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
+ config.content_security_policy_nonce_directives = ['script-src']
+ end
+end
diff --git a/config/initializers/secure_headers.rb b/config/initializers/secure_headers.rb
index f805ff60a33..49ce7b42ecb 100644
--- a/config/initializers/secure_headers.rb
+++ b/config/initializers/secure_headers.rb
@@ -1,3 +1,5 @@
+require 'feature_management'
+
SecureHeaders::Configuration.default do |config| # rubocop:disable Metrics/BlockLength
config.hsts = "max-age=#{365.days.to_i}; includeSubDomains; preload"
config.x_frame_options = 'DENY'
@@ -40,21 +42,22 @@
}
if IdentityConfig.store.rails_mailer_previews_enabled
+ default_csp_config[:style_src] << "'unsafe-inline'"
# CSP 2.0 only; overriden by x_frame_options in some browsers
default_csp_config[:frame_ancestors] = %w['self']
end
default_csp_config[:script_src] = ["'self'", "'unsafe-eval'"] if !Rails.env.production?
- if IdentityConfig.store.rails_mailer_previews_enabled
- default_csp_config[:style_src] << "'unsafe-inline'"
+ if ENV['WEBPACK_PORT']
+ default_csp_config[:connect_src] << "ws://localhost:#{ENV['WEBPACK_PORT']}"
+ default_csp_config[:script_src] << "localhost:#{ENV['WEBPACK_PORT']}"
end
- config.csp = default_csp_config
-
- if ENV['WEBPACK_PORT']
- config.csp[:connect_src] << "ws://localhost:#{ENV['WEBPACK_PORT']}"
- config.csp[:script_src] << "localhost:#{ENV['WEBPACK_PORT']}"
+ if FeatureManagement.rails_csp_tooling_enabled?
+ config.csp = SecureHeaders::OPT_OUT
+ else
+ config.csp = default_csp_config
end
config.cookies = {
diff --git a/lib/feature_management.rb b/lib/feature_management.rb
index a26e92bbe38..9d78ffee3cd 100644
--- a/lib/feature_management.rb
+++ b/lib/feature_management.rb
@@ -120,4 +120,8 @@ def self.voip_allowed_phones
allowed_phones.map { |p| Phonelib.parse(p).e164 }.to_set
end
end
+
+ def self.rails_csp_tooling_enabled?
+ IdentityConfig.store.rails_csp_tooling_enabled == true
+ end
end
diff --git a/lib/identity_config.rb b/lib/identity_config.rb
index d7d7be1c243..b5c29e2cdfd 100644
--- a/lib/identity_config.rb
+++ b/lib/identity_config.rb
@@ -257,6 +257,7 @@ def self.build_store(config_map)
config.add(:pwned_passwords_file_path, type: :string)
config.add(:rack_mini_profiler, type: :boolean)
config.add(:rack_timeout_service_timeout_seconds, type: :integer)
+ config.add(:rails_csp_tooling_enabled, type: :boolean)
config.add(:rails_mailer_previews_enabled, type: :boolean)
config.add(:reauthn_window, type: :integer)
config.add(:recovery_code_length, type: :integer)
diff --git a/spec/controllers/concerns/idv/document_capture_concern_spec.rb b/spec/controllers/concerns/idv/document_capture_concern_spec.rb
index 1dc62cafa4d..7b111605230 100644
--- a/spec/controllers/concerns/idv/document_capture_concern_spec.rb
+++ b/spec/controllers/concerns/idv/document_capture_concern_spec.rb
@@ -13,7 +13,7 @@ def index; end
it 'sets the headers for the document capture step' do
get :index, params: { step: 'document_capture' }
- csp = response.request.headers.env['secure_headers_request_config'].csp
+ csp = response.request.content_security_policy
expect(csp.script_src).to include("'unsafe-eval'")
expect(csp.style_src).to include("'unsafe-inline'")
expect(csp.img_src).to include('blob:')
diff --git a/spec/controllers/saml_idp_controller_spec.rb b/spec/controllers/saml_idp_controller_spec.rb
index 9b904cb454f..458a9b2424b 100644
--- a/spec/controllers/saml_idp_controller_spec.rb
+++ b/spec/controllers/saml_idp_controller_spec.rb
@@ -1267,7 +1267,7 @@ def name_id_version(format_urn)
end
it 'sets correct CSP config that includes any custom app scheme uri from SP redirect_uris' do
- form_action = response.request.headers.env['secure_headers_request_config'].csp.form_action
+ form_action = response.request.content_security_policy.form_action
csp_array = ["'self'", 'http://localhost:3000', 'x-example-app:']
expect(form_action).to match_array(csp_array)
end
diff --git a/spec/requests/csp_spec.rb b/spec/requests/csp_spec.rb
index 911e163c568..27c160d429c 100644
--- a/spec/requests/csp_spec.rb
+++ b/spec/requests/csp_spec.rb
@@ -49,7 +49,9 @@
)
expect(content_security_policy['media-src']).to eq("'self'")
expect(content_security_policy['object-src']).to eq("'none'")
- expect(content_security_policy['script-src']).to eq("'self' 'unsafe-eval'")
+ expect(content_security_policy['script-src']).to match(
+ /'self' 'unsafe-eval' 'nonce-[\w\d=\/+]+'/,
+ )
expect(content_security_policy['style-src']).to eq("'self'")
end
end
diff --git a/spec/views/idv/shared/_document_capture.html.erb_spec.rb b/spec/views/idv/shared/_document_capture.html.erb_spec.rb
index c8eb6ae747a..72a77f389aa 100644
--- a/spec/views/idv/shared/_document_capture.html.erb_spec.rb
+++ b/spec/views/idv/shared/_document_capture.html.erb_spec.rb
@@ -37,13 +37,12 @@
let(:async_uploads_enabled) { false }
it 'does not modify CSP connect_src headers' do
- allow(SecureHeaders).to receive(:append_content_security_policy_directives).with(any_args)
- expect(SecureHeaders).to receive(:append_content_security_policy_directives).with(
- controller.request,
- connect_src: [],
- )
-
render_partial
+
+ connect_src = controller.request.content_security_policy.connect_src
+ expect(connect_src).to eq(
+ ["'self'", '*.nr-data.net', '*.google-analytics.com', 'us.acas.acuant.net'],
+ )
end
end
@@ -54,17 +53,12 @@
let(:selfie_image_upload_url) { 'https://s3.example.com/bucket/c?X-Amz-Security-Token=UAOL2' }
it 'does modifies CSP connect_src headers to include upload urls' do
- allow(SecureHeaders).to receive(:append_content_security_policy_directives).with(any_args)
- expect(SecureHeaders).to receive(:append_content_security_policy_directives).with(
- controller.request,
- connect_src: [
- 'https://s3.example.com/bucket/a',
- 'https://s3.example.com/bucket/b',
- 'https://s3.example.com/bucket/c',
- ],
- )
-
render_partial
+
+ connect_src = controller.request.content_security_policy.connect_src
+ expect(connect_src).to include('https://s3.example.com/bucket/a')
+ expect(connect_src).to include('https://s3.example.com/bucket/b')
+ expect(connect_src).to include('https://s3.example.com/bucket/c')
end
end
end