Add a Content Security Policy
This commit is contained in:
1
.github/workflows/ci.yml
vendored
1
.github/workflows/ci.yml
vendored
@ -70,6 +70,7 @@ jobs:
|
|||||||
cp config/secrets.yml.ci config/secrets.yml
|
cp config/secrets.yml.ci config/secrets.yml
|
||||||
cp config/docker.yml.erb.ci config/docker.yml.erb
|
cp config/docker.yml.erb.ci config/docker.yml.erb
|
||||||
cp config/mnemosyne.yml.ci config/mnemosyne.yml
|
cp config/mnemosyne.yml.ci config/mnemosyne.yml
|
||||||
|
cp config/content_security_policy.yml.ci config/content_security_policy.yml
|
||||||
|
|
||||||
- name: Create database
|
- name: Create database
|
||||||
env:
|
env:
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,6 +4,7 @@
|
|||||||
/config/mnemosyne.yml
|
/config/mnemosyne.yml
|
||||||
/config/secrets.yml
|
/config/secrets.yml
|
||||||
/config/docker.yml.erb
|
/config/docker.yml.erb
|
||||||
|
/config/content_security_policy.yml
|
||||||
/coverage
|
/coverage
|
||||||
/log/*.*
|
/log/*.*
|
||||||
/public/assets
|
/public/assets
|
||||||
|
@ -36,4 +36,5 @@ h1 = link_to_if(policy(@exercise).show?, @exercise, exercise_path(@exercise))
|
|||||||
|
|
||||||
= render('shared/pagination', collection: @feedbacks)
|
= render('shared/pagination', collection: @feedbacks)
|
||||||
|
|
||||||
script type="text/javascript" $(function () { $('[data-bs-toggle="tooltip"]').tooltip() });
|
= javascript_tag nonce: true do
|
||||||
|
| $(function () { $('[data-bs-toggle="tooltip"]').tooltip() });
|
||||||
|
@ -15,8 +15,8 @@ html lang="#{I18n.locale || I18n.default_locale}"
|
|||||||
= javascript_include_tag('application', 'data-turbolinks-track': true)
|
= javascript_include_tag('application', 'data-turbolinks-track': true)
|
||||||
= yield(:head)
|
= yield(:head)
|
||||||
= csrf_meta_tags
|
= csrf_meta_tags
|
||||||
= timeago_script_tag
|
= timeago_script_tag nonce: true
|
||||||
script type="text/javascript"
|
= javascript_tag nonce: true do
|
||||||
| I18n.defaultLocale = "#{I18n.default_locale}";
|
| I18n.defaultLocale = "#{I18n.default_locale}";
|
||||||
| I18n.locale = "#{I18n.locale}";
|
| I18n.locale = "#{I18n.locale}";
|
||||||
- if SentryJavascript.active?
|
- if SentryJavascript.active?
|
||||||
|
@ -79,7 +79,7 @@
|
|||||||
|
|
||||||
= render('shared/modal', id: 'comment-modal', title: t('exercises.implement.comment.dialogtitle'), template: 'exercises/_comment_dialogcontent')
|
= render('shared/modal', id: 'comment-modal', title: t('exercises.implement.comment.dialogtitle'), template: 'exercises/_comment_dialogcontent')
|
||||||
|
|
||||||
javascript:
|
javascript [nonce=content_security_policy_nonce]:
|
||||||
|
|
||||||
$('.modal-content').draggable({
|
$('.modal-content').draggable({
|
||||||
handle: '.modal-header'
|
handle: '.modal-header'
|
||||||
|
18
config/content_security_policy.yml.ci
Normal file
18
config/content_security_policy.yml.ci
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
default: &default
|
||||||
|
default_src: []
|
||||||
|
|
||||||
|
|
||||||
|
development:
|
||||||
|
<<: *default
|
||||||
|
# Allow the webpack-dev-server in development
|
||||||
|
connect_src:
|
||||||
|
- http://localhost:3035
|
||||||
|
- ws://localhost:3035
|
||||||
|
|
||||||
|
|
||||||
|
production:
|
||||||
|
<<: *default
|
||||||
|
|
||||||
|
|
||||||
|
test:
|
||||||
|
<<: *default
|
29
config/content_security_policy.yml.example
Normal file
29
config/content_security_policy.yml.example
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# This file allows to further customize the Content Security Policy (CSP)
|
||||||
|
# All settings will be applied **in addition** to the application CSP
|
||||||
|
# Default directives are defined here: `initializers/content_security_policy.rb`
|
||||||
|
|
||||||
|
default: &default
|
||||||
|
# Allow the S3 service hosted by the openHPI Cloud to be used for images
|
||||||
|
img_src:
|
||||||
|
- https://s3.xopic.de
|
||||||
|
- https://*.s3.xopic.de
|
||||||
|
- https://s3.openhpicloud.de
|
||||||
|
- https://*.s3.openhpicloud.de
|
||||||
|
# Optionally: Specify a custom, non-Sentry URL for reporting CSP violations
|
||||||
|
# report_uri: https://example.com/csp-report
|
||||||
|
|
||||||
|
|
||||||
|
development:
|
||||||
|
<<: *default
|
||||||
|
# Allow the webpack-dev-server in development
|
||||||
|
connect_src:
|
||||||
|
- http://localhost:3035
|
||||||
|
- ws://localhost:3035
|
||||||
|
|
||||||
|
|
||||||
|
production:
|
||||||
|
<<: *default
|
||||||
|
|
||||||
|
|
||||||
|
test:
|
||||||
|
<<: *default
|
@ -6,28 +6,57 @@
|
|||||||
# For further information see the following documentation
|
# For further information see the following documentation
|
||||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
|
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
|
||||||
|
|
||||||
# Rails.application.config.content_security_policy do |policy|
|
require_relative 'sentry_csp'
|
||||||
# # If you are using webpack-dev-server then specify webpack-dev-server host
|
require_relative 'sentry_javascript'
|
||||||
# policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
|
|
||||||
|
|
||||||
# policy.default_src :self, :https
|
def self.apply_yml_settings_for(policy)
|
||||||
# policy.font_src :self, :https, :data
|
csp_settings = CodeOcean::Config.new(:content_security_policy)
|
||||||
# policy.img_src :self, :https, :data
|
|
||||||
# policy.object_src :none
|
|
||||||
# policy.script_src :self, :https
|
|
||||||
# policy.style_src :self, :https
|
|
||||||
# # If you are using webpack-dev-server then specify webpack-dev-server host
|
|
||||||
# policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
|
|
||||||
|
|
||||||
# # Specify URI for violation reports
|
csp_settings.read.each do |directive, additional_settings|
|
||||||
# # policy.report_uri "/csp-violation-report-endpoint"
|
existing_settings = if directive == 'report_uri'
|
||||||
# end
|
''
|
||||||
|
else
|
||||||
|
policy.public_send(directive) || []
|
||||||
|
end
|
||||||
|
all_settings = existing_settings + additional_settings
|
||||||
|
policy.public_send(directive, *all_settings)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.apply_sentry_settings_for(policy)
|
||||||
|
sentry_domain = URI.parse SentryJavascript.dsn
|
||||||
|
additional_setting = "#{sentry_domain.scheme}://#{sentry_domain.host}"
|
||||||
|
existing_settings = policy.connect_src || []
|
||||||
|
all_settings = existing_settings + [additional_setting]
|
||||||
|
policy.connect_src(*all_settings)
|
||||||
|
end
|
||||||
|
|
||||||
|
Rails.application.config.content_security_policy do |policy|
|
||||||
|
policy.default_src :none
|
||||||
|
policy.base_uri :none
|
||||||
|
policy.font_src :self
|
||||||
|
# Code executions might return a base64 encoded image as a :data URI
|
||||||
|
policy.img_src :self, :data
|
||||||
|
policy.object_src :none
|
||||||
|
policy.script_src :self, :report_sample
|
||||||
|
# Our ACE editor unfortunately requires :unsafe_inline for the code highlighting
|
||||||
|
policy.style_src :self, :unsafe_inline, :report_sample
|
||||||
|
policy.connect_src :self
|
||||||
|
policy.form_action :self
|
||||||
|
policy.frame_ancestors :none
|
||||||
|
|
||||||
|
# Specify URI for violation reports
|
||||||
|
policy.report_uri SentryCsp.report_url if SentryCsp.active?
|
||||||
|
|
||||||
|
apply_yml_settings_for policy
|
||||||
|
apply_sentry_settings_for policy if SentryJavascript.active?
|
||||||
|
end
|
||||||
|
|
||||||
# If you are using UJS then enable automatic nonce generation
|
# If you are using UJS then enable automatic nonce generation
|
||||||
# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
|
Rails.application.config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
|
||||||
|
|
||||||
# Set the nonce only to specific directives
|
# Set the nonce only to specific directives
|
||||||
# Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
|
Rails.application.config.content_security_policy_nonce_directives = %w[script-src]
|
||||||
|
|
||||||
# Report CSP violations to a specified URI
|
# Report CSP violations to a specified URI
|
||||||
# For further information see the following documentation:
|
# For further information see the following documentation:
|
||||||
|
40
config/initializers/sentry_csp.rb
Normal file
40
config/initializers/sentry_csp.rb
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require_relative 'sentry'
|
||||||
|
|
||||||
|
class SentryCsp
|
||||||
|
def self.active?
|
||||||
|
dsn.present? && %w[development test].exclude?(environment)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.report_url
|
||||||
|
parsed_url = URI.parse dsn
|
||||||
|
|
||||||
|
# Add additional variables to the query string
|
||||||
|
query_params = CGI.parse(parsed_url.query || '')
|
||||||
|
query_params[:sentry_release] = release if release
|
||||||
|
query_params[:sentry_environment] = environment if environment
|
||||||
|
|
||||||
|
# Add the query string back to the URL
|
||||||
|
parsed_url.query = URI.encode_www_form(query_params)
|
||||||
|
|
||||||
|
# Return the full URL
|
||||||
|
parsed_url.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
class << self
|
||||||
|
private
|
||||||
|
|
||||||
|
def dsn
|
||||||
|
ENV.fetch('SENTRY_CSP_REPORT_URL', nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
def release
|
||||||
|
Sentry.configuration.release
|
||||||
|
end
|
||||||
|
|
||||||
|
def environment
|
||||||
|
Sentry.configuration.environment
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,5 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require_relative 'sentry'
|
||||||
|
|
||||||
class SentryJavascript
|
class SentryJavascript
|
||||||
def self.active?
|
def self.active?
|
||||||
dsn.present? && %w[development test].exclude?(environment)
|
dsn.present? && %w[development test].exclude?(environment)
|
||||||
|
@ -194,7 +194,7 @@ source "$HOME/.profile"
|
|||||||
- Create all necessary config files:
|
- Create all necessary config files:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
for f in action_mailer.yml database.yml secrets.yml code_ocean.yml docker.yml.erb mnemosyne.yml
|
for f in action_mailer.yml database.yml secrets.yml code_ocean.yml docker.yml.erb mnemosyne.yml content_security_policy.yml
|
||||||
do
|
do
|
||||||
if [ ! -f config/$f ]
|
if [ ! -f config/$f ]
|
||||||
then
|
then
|
||||||
@ -303,7 +303,7 @@ source "$HOME/.profile"
|
|||||||
```
|
```
|
||||||
- Get a local copy of the config files and verify the settings:
|
- Get a local copy of the config files and verify the settings:
|
||||||
```shell script
|
```shell script
|
||||||
for f in action_mailer.yml database.yml secrets.yml code_ocean.yml docker.yml.erb mnemosyne.yml
|
for f in action_mailer.yml database.yml secrets.yml code_ocean.yml docker.yml.erb mnemosyne.yml content_security_policy.yml
|
||||||
do
|
do
|
||||||
if [ ! -f config/$f ]
|
if [ ! -f config/$f ]
|
||||||
then
|
then
|
||||||
|
@ -92,7 +92,7 @@ gem install bundler
|
|||||||
cd /home/vagrant/codeocean
|
cd /home/vagrant/codeocean
|
||||||
|
|
||||||
# config
|
# config
|
||||||
for f in action_mailer.yml database.yml secrets.yml docker.yml.erb mnemosyne.yml
|
for f in action_mailer.yml database.yml secrets.yml docker.yml.erb mnemosyne.yml content_security_policy.yml
|
||||||
do
|
do
|
||||||
if [ ! -f config/$f ]
|
if [ ! -f config/$f ]
|
||||||
then
|
then
|
||||||
|
Reference in New Issue
Block a user