New Upstream Release - ruby-secure-headers

Ready changes

Summary

Merged new upstream version: 6.5.0 (was: 6.3.2).

Resulting package

Built on 2022-12-14T02:43 (took 3m16s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases ruby-secure-headers

Lintian Result

Diff

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index dcd9cd0..3588214 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,5 +1,5 @@
 name: Build + Test
-on: [pull_request]
+on: [pull_request, push]
 
 jobs:
   build:
@@ -7,12 +7,12 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        ruby: [ '2.4', '2.5', '2.6', '2.7' ]
+        ruby: [ '2.6', '2.7', '3.0', '3.1' ]
 
     steps:
     - uses: actions/checkout@v2
     - name: Set up Ruby ${{ matrix.ruby }}
-      uses: actions/setup-ruby@v1
+      uses: ruby/setup-ruby@v1
       with:
         ruby-version: ${{ matrix.ruby }}
     - name: Build and test with Rake
diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml
new file mode 100644
index 0000000..ef6018a
--- /dev/null
+++ b/.github/workflows/github-release.yml
@@ -0,0 +1,28 @@
+name: GitHub Release
+
+on:
+  push:
+    tags:
+      - v*
+
+jobs:
+  Publish:
+    permissions:
+      contents: write
+    runs-on: ubuntu-latest
+    if: startsWith(github.ref, 'refs/tags/v')
+    steps:
+      - name: Calculate release name
+        run: |
+          GITHUB_REF=${{ github.ref }}
+          RELEASE_NAME=${GITHUB_REF#"refs/tags/"}
+          echo "RELEASE_NAME=${RELEASE_NAME}" >> $GITHUB_ENV
+      - name: Publish release
+        uses: actions/create-release@v1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          tag_name: ${{ github.ref }}
+          release_name: ${{ env.RELEASE_NAME }}
+          draft: false
+          prerelease: false
diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml
deleted file mode 100644
index 319e847..0000000
--- a/.github/workflows/sync.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-# This workflow ensures the "master" branch is always up-to-date with the
-# "main" branch (our default one)
-name: sync_main_branch
-on:
-  push:
-    branches: [ main ]
-jobs:
-  catch_up:
-    runs-on: ubuntu-latest
-    steps:
-      - name: Check out the repository
-        uses: actions/checkout@v2
-        with:
-         fetch-depth: 0
-      - name: Merge development into master, then push it
-        run: |
-          git pull
-          git checkout master
-          git merge main
-          git push
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1ebb4ca..662bdd1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,19 @@
+## 6.5.0
+
+- CSP: Remove source expression deduplication. (@lgarron) https://github.com/github/secure_headers/pull/499
+
+## 6.4.0
+
+- CSP: Add support for trusted-types, require-trusted-types-for directive (@JackMc): https://github.com/github/secure_headers/pull/486
+
+## 6.3.4
+
+- CSP: Do not deduplicate alternate schema source expressions (@keithamus): https://github.com/github/secure_headers/pull/478
+
+## 6.3.3
+
+Fix hash generation for indented helper methods (@rahearn)
+
 ## 6.3.2
 
 Add support for style-src-attr, style-src-elem, script-src-attr, and script-src-elem directives (@ggalmazor)
diff --git a/Gemfile b/Gemfile
index a6b3e54..78cca83 100644
--- a/Gemfile
+++ b/Gemfile
@@ -9,7 +9,7 @@ group :test do
   gem "pry-nav"
   gem "rack"
   gem "rspec"
-  gem "rubocop", "< 0.68"
+  gem "rubocop"
   gem "rubocop-github"
   gem "rubocop-performance"
   gem "term-ansicolor"
diff --git a/README.md b/README.md
index 4cc5706..b842cac 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
 **main branch represents 6.x line**. See the [upgrading to 4.x doc](docs/upgrading-to-4-0.md), [upgrading to 5.x doc](docs/upgrading-to-5-0.md), or [upgrading to 6.x doc](docs/upgrading-to-6-0.md) for instructions on how to upgrade. Bug fixes should go in the 5.x branch for now.
 
 The gem will automatically apply several headers that are related to security.  This includes:
-- Content Security Policy (CSP) - Helps detect/prevent XSS, mixed-content, and other classes of attack.  [CSP 2 Specification](http://www.w3.org/TR/CSP2/)
+- Content Security Policy (CSP) - Helps detect/prevent XSS, mixed-content, and other classes of attack.  [CSP 2 Specification](https://www.w3.org/TR/CSP2/)
   - https://csp.withgoogle.com
   - https://csp.withgoogle.com/docs/strict-csp.html
   - https://csp-evaluator.withgoogle.com
@@ -62,7 +62,7 @@ SecureHeaders::Configuration.default do |config|
     # directive values: these values will directly translate into source directives
     default_src: %w('none'),
     base_uri: %w('self'),
-    block_all_mixed_content: true, # see http://www.w3.org/TR/mixed-content/
+    block_all_mixed_content: true, # see https://www.w3.org/TR/mixed-content/
     child_src: %w('self'), # if child-src isn't supported, the value for frame-src will be set.
     connect_src: %w(wss:),
     font_src: %w('self' data:),
@@ -170,7 +170,7 @@ If you've made a contribution and see your name missing from the list, make a PR
 * Node.js (express) [helmet](https://github.com/helmetjs/helmet) and [hood](https://github.com/seanmonstar/hood)
 * Node.js (hapi) [blankie](https://github.com/nlf/blankie)
 * ASP.NET - [NWebsec](https://github.com/NWebsec/NWebsec/wiki)
-* Python - [django-csp](https://github.com/mozilla/django-csp) + [commonware](https://github.com/jsocol/commonware/); [django-security](https://github.com/sdelements/django-security)
+* Python - [django-csp](https://github.com/mozilla/django-csp) + [commonware](https://github.com/jsocol/commonware/); [django-security](https://github.com/sdelements/django-security), [secure](https://github.com/TypeError/secure)
 * Go - [secureheader](https://github.com/kr/secureheader)
 * Elixir [secure_headers](https://github.com/anotherhale/secure_headers)
 * Dropwizard [dropwizard-web-security](https://github.com/palantir/dropwizard-web-security)
diff --git a/debian/changelog b/debian/changelog
index a15bffc..570fe7c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+ruby-secure-headers (6.5.0-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Wed, 14 Dec 2022 02:41:00 -0000
+
 ruby-secure-headers (6.3.2-1) unstable; urgency=medium
 
   * Team upload
diff --git a/docs/per_action_configuration.md b/docs/per_action_configuration.md
index c17a9e5..63716db 100644
--- a/docs/per_action_configuration.md
+++ b/docs/per_action_configuration.md
@@ -54,7 +54,7 @@ Code  | Result
 
 #### Nonce
 
-You can use a view helper to automatically add nonces to script tags:
+You can use a view helper to automatically add nonces to script tags. Currently, using a nonce helper or calling `content_security_policy_nonce` will populate all configured CSP headers, including report-only and enforced policies. 
 
 ```erb
 <%= nonced_javascript_tag do %>
@@ -120,9 +120,7 @@ You can clear the browser cache after the logout request by using the following.
 class ApplicationController < ActionController::Base
   # Configuration override to send the Clear-Site-Data header.
   SecureHeaders::Configuration.override(:clear_browser_cache) do |config|
-    config.clear_site_data = [
-      SecureHeaders::ClearSiteData::ALL_TYPES
-    ]
+    config.clear_site_data = SecureHeaders::ClearSiteData::ALL_TYPES
   end
 
 
diff --git a/lib/secure_headers/configuration.rb b/lib/secure_headers/configuration.rb
index 538365a..996ca74 100644
--- a/lib/secure_headers/configuration.rb
+++ b/lib/secure_headers/configuration.rb
@@ -84,11 +84,12 @@ module SecureHeaders
       def deep_copy(config)
         return unless config
         config.each_with_object({}) do |(key, value), hash|
-          hash[key] = if value.is_a?(Array)
-            value.dup
-          else
-            value
-          end
+          hash[key] =
+            if value.is_a?(Array)
+              value.dup
+            else
+              value
+            end
         end
       end
 
diff --git a/lib/secure_headers/headers/content_security_policy.rb b/lib/secure_headers/headers/content_security_policy.rb
index cb75050..566004b 100644
--- a/lib/secure_headers/headers/content_security_policy.rb
+++ b/lib/secure_headers/headers/content_security_policy.rb
@@ -7,17 +7,18 @@ module SecureHeaders
     include PolicyManagement
 
     def initialize(config = nil)
-      @config = if config.is_a?(Hash)
-        if config[:report_only]
-          ContentSecurityPolicyReportOnlyConfig.new(config || DEFAULT_CONFIG)
+      @config =
+        if config.is_a?(Hash)
+          if config[:report_only]
+            ContentSecurityPolicyReportOnlyConfig.new(config || DEFAULT_CONFIG)
+          else
+            ContentSecurityPolicyConfig.new(config || DEFAULT_CONFIG)
+          end
+        elsif config.nil?
+          ContentSecurityPolicyConfig.new(DEFAULT_CONFIG)
         else
-          ContentSecurityPolicyConfig.new(config || DEFAULT_CONFIG)
+          config
         end
-      elsif config.nil?
-        ContentSecurityPolicyConfig.new(DEFAULT_CONFIG)
-      else
-        config
-      end
 
       @preserve_schemes = @config.preserve_schemes
       @script_nonce = @config.script_nonce
@@ -34,11 +35,12 @@ module SecureHeaders
     ##
     # Return the value of the CSP header
     def value
-      @value ||= if @config
-        build_value
-      else
-        DEFAULT_VALUE
-      end
+      @value ||=
+        if @config
+          build_value
+        else
+          DEFAULT_VALUE
+        end
     end
 
     private
@@ -51,7 +53,9 @@ module SecureHeaders
     def build_value
       directives.map do |directive_name|
         case DIRECTIVE_VALUE_TYPES[directive_name]
-        when :source_list, :require_sri_for_list # require_sri is a simple set of strings that don't need to deal with symbol casing
+        when :source_list,
+             :require_sri_for_list, # require_sri is a simple set of strings that don't need to deal with symbol casing
+             :require_trusted_types_for_list
           build_source_list_directive(directive_name)
         when :boolean
           symbol_to_hyphen_case(directive_name) if @config.directive_value(directive_name)
@@ -129,7 +133,7 @@ module SecureHeaders
         unless directive == REPORT_URI || @preserve_schemes
           source_list = strip_source_schemes(source_list)
         end
-        dedup_source_list(source_list)
+        source_list.uniq
       end
     end
 
@@ -147,23 +151,6 @@ module SecureHeaders
       end
     end
 
-    # Removes duplicates and sources that already match an existing wild card.
-    #
-    # e.g. *.github.com asdf.github.com becomes *.github.com
-    def dedup_source_list(sources)
-      sources = sources.uniq
-      wild_sources = sources.select { |source| source =~ STAR_REGEXP }
-
-      if wild_sources.any?
-        sources.reject do |source|
-          !wild_sources.include?(source) &&
-            wild_sources.any? { |pattern| File.fnmatch(pattern, source) }
-        end
-      else
-        sources
-      end
-    end
-
     # Private: append a nonce to the script/style directories if script_nonce
     # or style_nonce are provided.
     def populate_nonces(directive, source_list)
diff --git a/lib/secure_headers/headers/content_security_policy_config.rb b/lib/secure_headers/headers/content_security_policy_config.rb
index 3c47716..5d3c755 100644
--- a/lib/secure_headers/headers/content_security_policy_config.rb
+++ b/lib/secure_headers/headers/content_security_policy_config.rb
@@ -35,6 +35,7 @@ module SecureHeaders
       @report_only = nil
       @report_uri = nil
       @require_sri_for = nil
+      @require_trusted_types_for = nil
       @sandbox = nil
       @script_nonce = nil
       @script_src = nil
@@ -44,6 +45,7 @@ module SecureHeaders
       @style_src = nil
       @style_src_elem = nil
       @style_src_attr = nil
+      @trusted_types = nil
       @worker_src = nil
       @upgrade_insecure_requests = nil
       @disable_nonce_backwards_compatibility = nil
diff --git a/lib/secure_headers/headers/policy_management.rb b/lib/secure_headers/headers/policy_management.rb
index b81f3b0..1d464f4 100644
--- a/lib/secure_headers/headers/policy_management.rb
+++ b/lib/secure_headers/headers/policy_management.rb
@@ -98,7 +98,19 @@ module SecureHeaders
       STYLE_SRC_ATTR
     ].flatten.freeze
 
-    ALL_DIRECTIVES = (DIRECTIVES_1_0 + DIRECTIVES_2_0 + DIRECTIVES_3_0).uniq.sort
+    # Experimental directives - these vary greatly in support
+    # See MDN for details.
+    # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/trusted-types
+    TRUSTED_TYPES = :trusted_types
+    # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/require-trusted-types-for
+    REQUIRE_TRUSTED_TYPES_FOR = :require_trusted_types_for
+
+    DIRECTIVES_EXPERIMENTAL = [
+      TRUSTED_TYPES,
+      REQUIRE_TRUSTED_TYPES_FOR,
+    ].flatten.freeze
+
+    ALL_DIRECTIVES = (DIRECTIVES_1_0 + DIRECTIVES_2_0 + DIRECTIVES_3_0 + DIRECTIVES_EXPERIMENTAL).uniq.sort
 
     # Think of default-src and report-uri as the beginning and end respectively,
     # everything else is in between.
@@ -121,6 +133,7 @@ module SecureHeaders
       OBJECT_SRC                => :source_list,
       PLUGIN_TYPES              => :media_type_list,
       REQUIRE_SRI_FOR           => :require_sri_for_list,
+      REQUIRE_TRUSTED_TYPES_FOR => :require_trusted_types_for_list,
       REPORT_URI                => :source_list,
       PREFETCH_SRC              => :source_list,
       SANDBOX                   => :sandbox_list,
@@ -130,6 +143,7 @@ module SecureHeaders
       STYLE_SRC                 => :source_list,
       STYLE_SRC_ELEM            => :source_list,
       STYLE_SRC_ATTR            => :source_list,
+      TRUSTED_TYPES             => :source_list,
       WORKER_SRC                => :source_list,
       UPGRADE_INSECURE_REQUESTS => :boolean,
     }.freeze
@@ -175,6 +189,7 @@ module SecureHeaders
     ].freeze
 
     REQUIRE_SRI_FOR_VALUES = Set.new(%w(script style))
+    REQUIRE_TRUSTED_TYPES_FOR_VALUES = Set.new(%w('script'))
 
     module ClassMethods
       # Public: generate a header name, value array that is user-agent-aware.
@@ -270,7 +285,8 @@ module SecureHeaders
         source_list?(directive) ||
           sandbox_list?(directive) ||
           media_type_list?(directive) ||
-          require_sri_for_list?(directive)
+          require_sri_for_list?(directive) ||
+          require_trusted_types_for_list?(directive)
       end
 
       # For each directive in additions that does not exist in the original config,
@@ -278,11 +294,12 @@ module SecureHeaders
       def populate_fetch_source_with_default!(original, additions)
         # in case we would be appending to an empty directive, fill it with the default-src value
         additions.each_key do |directive|
-          directive = if directive.to_s.end_with?("_nonce")
-            directive.to_s.gsub(/_nonce/, "_src").to_sym
-          else
-            directive
-          end
+          directive =
+            if directive.to_s.end_with?("_nonce")
+              directive.to_s.gsub(/_nonce/, "_src").to_sym
+            else
+              directive
+            end
           # Don't set a default if directive has an existing value
           next if original[directive]
           if FETCH_SOURCES.include?(directive)
@@ -307,6 +324,10 @@ module SecureHeaders
         DIRECTIVE_VALUE_TYPES[directive] == :require_sri_for_list
       end
 
+      def require_trusted_types_for_list?(directive)
+        DIRECTIVE_VALUE_TYPES[directive] == :require_trusted_types_for_list
+      end
+
       # Private: Validates that the configuration has a valid type, or that it is a valid
       # source expression.
       def validate_directive!(directive, value)
@@ -324,6 +345,8 @@ module SecureHeaders
           validate_media_type_expression!(directive, value)
         when :require_sri_for_list
           validate_require_sri_source_expression!(directive, value)
+        when :require_trusted_types_for_list
+          validate_require_trusted_types_for_source_expression!(directive, value)
         else
           raise ContentSecurityPolicyConfigError.new("Unknown directive #{directive}")
         end
@@ -368,6 +391,16 @@ module SecureHeaders
         end
       end
 
+      # Private: validates that a require trusted types for expression:
+      # 1. is an array of strings
+      # 2. is a subset of ["'script'"]
+      def validate_require_trusted_types_for_source_expression!(directive, require_trusted_types_for_expression)
+        ensure_array_of_strings!(directive, require_trusted_types_for_expression)
+        unless require_trusted_types_for_expression.to_set.subset?(REQUIRE_TRUSTED_TYPES_FOR_VALUES)
+          raise ContentSecurityPolicyConfigError.new(%(require-trusted-types-for for must be a subset of #{REQUIRE_TRUSTED_TYPES_FOR_VALUES.to_a} but was #{require_trusted_types_for_expression}))
+        end
+      end
+
       # Private: validates that a source expression:
       # 1. is an array of strings
       # 2. does not contain any deprecated, now invalid values (inline, eval, self, none)
diff --git a/lib/secure_headers/version.rb b/lib/secure_headers/version.rb
index 52b6f10..b65e0a0 100644
--- a/lib/secure_headers/version.rb
+++ b/lib/secure_headers/version.rb
@@ -1,5 +1,5 @@
 # frozen_string_literal: true
 
 module SecureHeaders
-  VERSION = "6.3.2"
+  VERSION = "6.5.0"
 end
diff --git a/lib/secure_headers/view_helper.rb b/lib/secure_headers/view_helper.rb
index 91eef4d..7ed5731 100644
--- a/lib/secure_headers/view_helper.rb
+++ b/lib/secure_headers/view_helper.rb
@@ -147,12 +147,13 @@ module SecureHeaders
 
     def nonced_tag(type, content_or_options, block)
       options = {}
-      content = if block
-        options = content_or_options
-        capture(&block)
-      else
-        content_or_options.html_safe # :'(
-      end
+      content =
+        if block
+          options = content_or_options
+          capture(&block)
+        else
+          content_or_options.html_safe # :'(
+        end
       content_tag type, content, options.merge(nonce: _content_security_policy_nonce(type))
     end
 
diff --git a/lib/tasks/tasks.rake b/lib/tasks/tasks.rake
index 374fea5..cb07824 100644
--- a/lib/tasks/tasks.rake
+++ b/lib/tasks/tasks.rake
@@ -20,10 +20,11 @@ namespace :secure_headers do
       (is_erb?(filename) && inline_script =~ /<%.*%>/)
   end
 
-  def find_inline_content(filename, regex, hashes)
+  def find_inline_content(filename, regex, hashes, strip_trailing_whitespace)
     file = File.read(filename)
     file.scan(regex) do # TODO don't use gsub
       inline_script = Regexp.last_match.captures.last
+      inline_script.gsub!(/(\r?\n)[\t ]+\z/, '\1') if strip_trailing_whitespace
       if dynamic_content?(filename, inline_script)
         puts "Looks like there's some dynamic content inside of a tag :-/"
         puts "That pretty much means the hash value will never match."
@@ -38,9 +39,8 @@ namespace :secure_headers do
   def generate_inline_script_hashes(filename)
     hashes = []
 
-    [INLINE_SCRIPT_REGEX, INLINE_HASH_SCRIPT_HELPER_REGEX].each do |regex|
-      find_inline_content(filename, regex, hashes)
-    end
+    find_inline_content(filename, INLINE_SCRIPT_REGEX, hashes, false)
+    find_inline_content(filename, INLINE_HASH_SCRIPT_HELPER_REGEX, hashes, true)
 
     hashes
   end
@@ -48,9 +48,8 @@ namespace :secure_headers do
   def generate_inline_style_hashes(filename)
     hashes = []
 
-    [INLINE_STYLE_REGEX, INLINE_HASH_STYLE_HELPER_REGEX].each do |regex|
-      find_inline_content(filename, regex, hashes)
-    end
+    find_inline_content(filename, INLINE_STYLE_REGEX, hashes, false)
+    find_inline_content(filename, INLINE_HASH_STYLE_HELPER_REGEX, hashes, true)
 
     hashes
   end
diff --git a/secure_headers.gemspec b/secure_headers.gemspec
index c5f7292..4880c3f 100644
--- a/secure_headers.gemspec
+++ b/secure_headers.gemspec
@@ -9,12 +9,12 @@ Gem::Specification.new do |gem|
   gem.version       = SecureHeaders::VERSION
   gem.authors       = ["Neil Matatall"]
   gem.email         = ["neil.matatall@gmail.com"]
-  gem.description   = "Manages application of security headers with many safe defaults."
-  gem.summary       = 'Add easily configured security headers to responses
+  gem.summary       = "Manages application of security headers with many safe defaults."
+  gem.description   = 'Add easily configured security headers to responses
     including content-security-policy, x-frame-options,
     strict-transport-security, etc.'
   gem.homepage      = "https://github.com/twitter/secureheaders"
-  gem.license       = "Apache Public License 2.0"
+  gem.license       = "MIT"
   gem.files         = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
   gem.executables   = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
   gem.test_files    = gem.files.grep(%r{^(test|spec|features)/})
diff --git a/spec/lib/secure_headers/headers/content_security_policy_spec.rb b/spec/lib/secure_headers/headers/content_security_policy_spec.rb
index c080283..314b896 100644
--- a/spec/lib/secure_headers/headers/content_security_policy_spec.rb
+++ b/spec/lib/secure_headers/headers/content_security_policy_spec.rb
@@ -48,12 +48,12 @@ module SecureHeaders
         expect(csp.value).to eq("default-src * 'unsafe-inline' 'unsafe-eval' data: blob:")
       end
 
-      it "minifies source expressions based on overlapping wildcards" do
+      it "does not minify source expressions based on overlapping wildcards" do
         config = {
           default_src: %w(a.example.org b.example.org *.example.org https://*.example.org)
         }
         csp = ContentSecurityPolicy.new(config)
-        expect(csp.value).to eq("default-src *.example.org")
+        expect(csp.value).to eq("default-src a.example.org b.example.org *.example.org")
       end
 
       it "removes http/s schemes from hosts" do
@@ -101,11 +101,21 @@ module SecureHeaders
         expect(csp.value).to eq("default-src example.org; block-all-mixed-content")
       end
 
-      it "deduplicates any source expressions" do
-        csp = ContentSecurityPolicy.new(default_src: %w(example.org example.org example.org))
+      it "handles wildcard subdomain with wildcard port" do
+        csp = ContentSecurityPolicy.new(default_src: %w(https://*.example.org:*))
+        expect(csp.value).to eq("default-src *.example.org:*")
+      end
+
+      it "deduplicates source expressions that match exactly (after scheme stripping)" do
+        csp = ContentSecurityPolicy.new(default_src: %w(example.org https://example.org example.org))
         expect(csp.value).to eq("default-src example.org")
       end
 
+      it "does not deduplicate non-matching schema source expressions" do
+        csp = ContentSecurityPolicy.new(default_src: %w(*.example.org wss://example.example.org))
+        expect(csp.value).to eq("default-src *.example.org wss://example.example.org")
+      end
+
       it "creates maximally strict sandbox policy when passed no sandbox token values" do
         csp = ContentSecurityPolicy.new(default_src: %w(example.org), sandbox: [])
         expect(csp.value).to eq("default-src example.org; sandbox")
@@ -141,6 +151,11 @@ module SecureHeaders
         expect(csp.value).to eq("default-src 'self'; require-sri-for script style")
       end
 
+      it "allows style as a require-trusted-types-for source" do
+        csp = ContentSecurityPolicy.new(default_src: %w('self'), require_trusted_types_for: %w(script))
+        expect(csp.value).to eq("default-src 'self'; require-trusted-types-for script")
+      end
+
       it "includes prefetch-src" do
         csp = ContentSecurityPolicy.new(default_src: %w('self'), prefetch_src: %w(foo.com))
         expect(csp.value).to eq("default-src 'self'; prefetch-src foo.com")
@@ -180,6 +195,21 @@ module SecureHeaders
         csp = ContentSecurityPolicy.new({style_src: %w('self'), style_src_attr: %w('self')})
         expect(csp.value).to eq("style-src 'self'; style-src-attr 'self'")
       end
+
+      it "supports trusted-types directive" do
+        csp = ContentSecurityPolicy.new({trusted_types: %w(blahblahpolicy)})
+        expect(csp.value).to eq("trusted-types blahblahpolicy")
+      end
+
+      it "supports trusted-types directive with 'none'" do
+        csp = ContentSecurityPolicy.new({trusted_types: %w('none')})
+        expect(csp.value).to eq("trusted-types 'none'")
+      end
+
+      it "allows duplicate policy names in trusted-types directive" do
+        csp = ContentSecurityPolicy.new({trusted_types: %w(blahblahpolicy 'allow-duplicates')})
+        expect(csp.value).to eq("trusted-types blahblahpolicy 'allow-duplicates'")
+      end
     end
   end
 end
diff --git a/spec/lib/secure_headers/headers/policy_management_spec.rb b/spec/lib/secure_headers/headers/policy_management_spec.rb
index 1a9ac4e..0f7cf8a 100644
--- a/spec/lib/secure_headers/headers/policy_management_spec.rb
+++ b/spec/lib/secure_headers/headers/policy_management_spec.rb
@@ -45,6 +45,7 @@ module SecureHeaders
           plugin_types: %w(application/x-shockwave-flash),
           prefetch_src: %w(fetch.com),
           require_sri_for: %w(script style),
+          require_trusted_types_for: %w('script'),
           script_src: %w('self'),
           style_src: %w('unsafe-inline'),
           upgrade_insecure_requests: true, # see https://www.w3.org/TR/upgrade-insecure-requests/
@@ -53,6 +54,7 @@ module SecureHeaders
           script_src_attr: %w(example.com),
           style_src_elem: %w(example.com),
           style_src_attr: %w(example.com),
+          trusted_types: %w(abcpolicy),
 
           report_uri: %w(https://example.com/uri-directive),
         }
@@ -120,6 +122,12 @@ module SecureHeaders
         end.to raise_error(ContentSecurityPolicyConfigError)
       end
 
+      it "rejects style for trusted types" do
+        expect do
+          ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(style_src: %w('self'), require_trusted_types_for: %w(script style), trusted_types: %w(abcpolicy))))
+        end
+      end
+
       # this is mostly to ensure people don't use the antiquated shorthands common in other configs
       it "performs light validation on source lists" do
         expect do
diff --git a/spec/lib/secure_headers/view_helpers_spec.rb b/spec/lib/secure_headers/view_helpers_spec.rb
index c71534a..2a7f56e 100644
--- a/spec/lib/secure_headers/view_helpers_spec.rb
+++ b/spec/lib/secure_headers/view_helpers_spec.rb
@@ -6,7 +6,7 @@ class Message < ERB
   include SecureHeaders::ViewHelpers
 
   def self.template
-<<-TEMPLATE
+    <<-TEMPLATE
 <% hashed_javascript_tag(raise_error_on_unrecognized_hash = true) do %>
   console.log(1)
 <% end %>
@@ -62,9 +62,10 @@ TEMPLATE
   end
 
   def content_tag(type, content = nil, options = nil, &block)
-    content = if block_given?
-      capture(block)
-    end
+    content =
+      if block_given?
+        capture(block)
+      end
 
     if options.is_a?(Hash)
       options = options.map { |k, v| " #{k}=#{v}" }

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers/configuration.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers/hash_helper.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers/headers/clear_site_data.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers/headers/content_security_policy.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers/headers/content_security_policy_config.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers/headers/cookie.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers/headers/expect_certificate_transparency.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers/headers/policy_management.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers/headers/referrer_policy.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers/headers/strict_transport_security.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers/headers/x_content_type_options.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers/headers/x_download_options.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers/headers/x_frame_options.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers/headers/x_xss_protection.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers/middleware.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers/railtie.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers/utils/cookies_config.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers/version.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/secure_headers/view_helper.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.5.0/lib/tasks/tasks.rake
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/specifications/secure_headers-6.5.0.gemspec

Files in first set of .debs but not in second

-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers/configuration.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers/hash_helper.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers/headers/clear_site_data.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers/headers/content_security_policy.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers/headers/content_security_policy_config.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers/headers/cookie.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers/headers/expect_certificate_transparency.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers/headers/policy_management.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers/headers/referrer_policy.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers/headers/strict_transport_security.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers/headers/x_content_type_options.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers/headers/x_download_options.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers/headers/x_frame_options.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers/headers/x_xss_protection.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers/middleware.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers/railtie.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers/utils/cookies_config.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers/version.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/secure_headers/view_helper.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/gems/secure_headers-6.3.2/lib/tasks/tasks.rake
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/specifications/secure_headers-6.3.2.gemspec

Control files: lines which differ (wdiff format)

  • Ruby-Versions: all

More details

Full run details