This commit is contained in:
Steve Agalloco 2017-06-01 21:56:25 -04:00
Родитель 194c6e3da3
Коммит e21ab51db5
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: FDAF2BA50D34F744
42 изменённых файлов: 191 добавлений и 149 удалений

Просмотреть файл

@ -1,21 +1,22 @@
# frozen_string_literal: true
source "https://rubygems.org"
gemspec
group :test do
gem "tins", "~> 1.6.0" # 1.7 requires ruby 2.0
gem "pry-nav"
gem "coveralls"
gem "json", "~> 1"
gem "pry-nav"
gem "rack", "~> 1"
gem "rspec"
gem "rubocop", "~> 0.47.0"
gem "rubocop-github"
gem "coveralls"
gem "term-ansicolor", "< 1.4"
gem "tins", "~> 1.6.0" # 1.7 requires ruby 2.0
end
group :guard do
gem "guard-rspec", platforms: [:ruby_19, :ruby_20, :ruby_21, :ruby_22]
gem "growl"
gem "guard-rspec", platforms: [:ruby_19, :ruby_20, :ruby_21, :ruby_22]
gem "rb-fsevent"
end

Просмотреть файл

@ -1,3 +1,4 @@
# frozen_string_literal: true
guard :rspec, cmd: "bundle exec rspec", all_on_start: true, all_after_pass: true do
require "guard/rspec/dsl"
dsl = Guard::RSpec::Dsl.new(self)

Просмотреть файл

@ -1,8 +1,9 @@
#!/usr/bin/env rake
require 'bundler/gem_tasks'
require 'rspec/core/rake_task'
require 'net/http'
require 'net/https'
# frozen_string_literal: true
require "bundler/gem_tasks"
require "rspec/core/rake_task"
require "net/http"
require "net/https"
desc "Run RSpec"
# RSpec::Core::RakeTask.new(:spec)
@ -12,25 +13,25 @@ RSpec::Core::RakeTask.new do |t|
end
begin
require 'rdoc/task'
require "rdoc/task"
rescue LoadError
require 'rdoc/rdoc'
require 'rake/rdoctask'
require "rdoc/rdoc"
require "rake/rdoctask"
RDoc::Task = Rake::RDocTask
end
begin
require 'rubocop/rake_task'
require "rubocop/rake_task"
RuboCop::RakeTask.new
rescue LoadError
task(:rubocop) { $stderr.puts "RuboCop is disabled" }
end
RDoc::Task.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'SecureHeaders'
rdoc.options << '--line-numbers'
rdoc.rdoc_files.include('lib/**/*.rb')
rdoc.rdoc_dir = "rdoc"
rdoc.title = "SecureHeaders"
rdoc.options << "--line-numbers"
rdoc.rdoc_files.include("lib/**/*.rb")
end
task default: [:spec, :rubocop]

Просмотреть файл

@ -1,3 +1,4 @@
# frozen_string_literal: true
require "secure_headers/configuration"
require "secure_headers/hash_helper"
require "secure_headers/headers/cookie"

Просмотреть файл

@ -1,4 +1,5 @@
require 'yaml'
# frozen_string_literal: true
require "yaml"
module SecureHeaders
class Configuration

Просмотреть файл

@ -1,4 +1,5 @@
require 'base64'
# frozen_string_literal: true
require "base64"
module SecureHeaders
module HashHelper

Просмотреть файл

@ -1,3 +1,4 @@
# frozen_string_literal: true
module SecureHeaders
class ClearSiteDataConfigError < StandardError; end
class ClearSiteData
@ -17,7 +18,7 @@ module SecureHeaders
# Public: make an Clear-Site-Data header name, value pair
#
# Returns nil if not configured, returns header name and value if configured.
def make_header(config=nil)
def make_header(config = nil)
case config
when nil, OPT_OUT, []
# noop

Просмотреть файл

@ -1,6 +1,7 @@
require_relative 'policy_management'
require_relative 'content_security_policy_config'
require 'useragent'
# frozen_string_literal: true
require_relative "policy_management"
require_relative "content_security_policy_config"
require "useragent"
module SecureHeaders
class ContentSecurityPolicy
@ -229,7 +230,7 @@ module SecureHeaders
end
def symbol_to_hyphen_case(sym)
sym.to_s.tr('_', '-')
sym.to_s.tr("_", "-")
end
end
end

Просмотреть файл

@ -1,3 +1,4 @@
# frozen_string_literal: true
module SecureHeaders
module DynamicConfig
def self.included(base)

Просмотреть файл

@ -1,5 +1,6 @@
require 'cgi'
require 'secure_headers/utils/cookies_config'
# frozen_string_literal: true
require "cgi"
require "secure_headers/utils/cookies_config"
module SecureHeaders
class CookiesConfigError < StandardError; end
@ -113,7 +114,7 @@ module SecureHeaders
return unless cookie
cookie.split(/[;,]\s?/).each do |pairs|
name, values = pairs.split('=',2)
name, values = pairs.split("=", 2)
name = CGI.unescape(name)
attribute = name.downcase.to_sym

Просмотреть файл

@ -1,3 +1,4 @@
# frozen_string_literal: true
module SecureHeaders
module PolicyManagement
def self.included(base)

Просмотреть файл

@ -1,3 +1,4 @@
# frozen_string_literal: true
module SecureHeaders
class PublicKeyPinsConfigError < StandardError; end
class PublicKeyPins
@ -54,7 +55,7 @@ module SecureHeaders
pin_directives,
report_uri_directive,
subdomain_directive
].compact.join('; ').strip
].compact.join("; ").strip
end
def pin_directives
@ -63,7 +64,7 @@ module SecureHeaders
pin.map do |token, hash|
"pin-#{token}=\"#{hash}\"" if HASH_ALGORITHMS.include?(token)
end
end.join('; ')
end.join("; ")
end
def max_age_directive
@ -75,7 +76,7 @@ module SecureHeaders
end
def subdomain_directive
@include_subdomains ? 'includeSubDomains' : nil
@include_subdomains ? "includeSubDomains" : nil
end
end
end

Просмотреть файл

@ -1,3 +1,4 @@
# frozen_string_literal: true
module SecureHeaders
class ReferrerPolicyConfigError < StandardError; end
class ReferrerPolicy

Просмотреть файл

@ -1,8 +1,9 @@
# frozen_string_literal: true
module SecureHeaders
class STSConfigError < StandardError; end
class StrictTransportSecurity
HEADER_NAME = 'Strict-Transport-Security'.freeze
HEADER_NAME = "Strict-Transport-Security".freeze
HSTS_MAX_AGE = "631138519"
DEFAULT_VALUE = "max-age=" + HSTS_MAX_AGE
VALID_STS_HEADER = /\Amax-age=\d+(; includeSubdomains)?(; preload)?\z/i

Просмотреть файл

@ -1,3 +1,4 @@
# frozen_string_literal: true
module SecureHeaders
class XContentTypeOptionsConfigError < StandardError; end

Просмотреть файл

@ -1,8 +1,9 @@
# frozen_string_literal: true
module SecureHeaders
class XDOConfigError < StandardError; end
class XDownloadOptions
HEADER_NAME = "X-Download-Options".freeze
DEFAULT_VALUE = 'noopen'
DEFAULT_VALUE = "noopen"
CONFIG_KEY = :x_download_options
class << self

Просмотреть файл

@ -1,3 +1,4 @@
# frozen_string_literal: true
module SecureHeaders
class XFOConfigError < StandardError; end
class XFrameOptions

Просмотреть файл

@ -1,8 +1,9 @@
# frozen_string_literal: true
module SecureHeaders
class XPCDPConfigError < StandardError; end
class XPermittedCrossDomainPolicies
HEADER_NAME = "X-Permitted-Cross-Domain-Policies".freeze
DEFAULT_VALUE = 'none'
DEFAULT_VALUE = "none"
VALID_POLICIES = %w(all none master-only by-content-type by-ftp-filename)
CONFIG_KEY = :x_permitted_cross_domain_policies

Просмотреть файл

@ -1,7 +1,8 @@
# frozen_string_literal: true
module SecureHeaders
class XXssProtectionConfigError < StandardError; end
class XXssProtection
HEADER_NAME = 'X-XSS-Protection'.freeze
HEADER_NAME = "X-XSS-Protection".freeze
DEFAULT_VALUE = "1; mode=block"
VALID_X_XSS_HEADER = /\A[01](; mode=block)?(; report=.*)?\z/i
CONFIG_KEY = :x_xss_protection

Просмотреть файл

@ -1,3 +1,4 @@
# frozen_string_literal: true
module SecureHeaders
class Middleware
HPKP_SAME_HOST_WARNING = "[WARNING] HPKP report host should not be the same as the request host. See https://github.com/twitter/secureheaders/issues/166"
@ -25,11 +26,11 @@ module SecureHeaders
# inspired by https://github.com/tobmatth/rack-ssl-enforcer/blob/6c014/lib/rack/ssl-enforcer.rb#L183-L194
def flag_cookies!(headers, config)
if cookies = headers['Set-Cookie']
if cookies = headers["Set-Cookie"]
# Support Rails 2.3 / Rack 1.1 arrays as headers
cookies = cookies.split("\n") unless cookies.is_a?(Array)
headers['Set-Cookie'] = cookies.map do |cookie|
headers["Set-Cookie"] = cookies.map do |cookie|
SecureHeaders::Cookie.new(cookie, config).to_s
end.join("\n")
end
@ -37,8 +38,8 @@ module SecureHeaders
# disable Secure cookies for non-https requests
def override_secure(env, config = {})
if scheme(env) != 'https'
config.merge!(secure: false)
if scheme(env) != "https"
config[:secure] = false
end
config
@ -46,12 +47,12 @@ module SecureHeaders
# derived from https://github.com/tobmatth/rack-ssl-enforcer/blob/6c014/lib/rack/ssl-enforcer.rb#L119
def scheme(env)
if env['HTTPS'] == 'on' || env['HTTP_X_SSL_REQUEST'] == 'on'
'https'
elsif env['HTTP_X_FORWARDED_PROTO']
env['HTTP_X_FORWARDED_PROTO'].split(',')[0]
if env["HTTPS"] == "on" || env["HTTP_X_SSL_REQUEST"] == "on"
"https"
elsif env["HTTP_X_FORWARDED_PROTO"]
env["HTTP_X_FORWARDED_PROTO"].split(",")[0]
else
env['rack.url_scheme']
env["rack.url_scheme"]
end
end
end

Просмотреть файл

@ -1,20 +1,21 @@
# frozen_string_literal: true
# rails 3.1+
if defined?(Rails::Railtie)
module SecureHeaders
class Railtie < Rails::Railtie
isolate_namespace SecureHeaders if defined? isolate_namespace # rails 3.0
conflicting_headers = ['X-Frame-Options', 'X-XSS-Protection',
'X-Permitted-Cross-Domain-Policies', 'X-Download-Options',
'X-Content-Type-Options', 'Strict-Transport-Security',
'Content-Security-Policy', 'Content-Security-Policy-Report-Only',
'Public-Key-Pins', 'Public-Key-Pins-Report-Only', 'Referrer-Policy']
conflicting_headers = ["X-Frame-Options", "X-XSS-Protection",
"X-Permitted-Cross-Domain-Policies", "X-Download-Options",
"X-Content-Type-Options", "Strict-Transport-Security",
"Content-Security-Policy", "Content-Security-Policy-Report-Only",
"Public-Key-Pins", "Public-Key-Pins-Report-Only", "Referrer-Policy"]
initializer "secure_headers.middleware" do
Rails.application.config.middleware.insert_before 0, SecureHeaders::Middleware
end
rake_tasks do
load File.expand_path(File.join('..', '..', 'lib', 'tasks', 'tasks.rake'), File.dirname(__FILE__))
load File.expand_path(File.join("..", "..", "lib", "tasks", "tasks.rake"), File.dirname(__FILE__))
end
initializer "secure_headers.action_controller" do

Просмотреть файл

@ -1,3 +1,4 @@
# frozen_string_literal: true
module SecureHeaders
class CookiesConfig
@ -51,10 +52,10 @@ module SecureHeaders
def validate_samesite_hash_config!
# validate Hash-based samesite configuration
if is_hash?(config[:samesite][:lax])
validate_exclusive_use_of_hash_constraints!(config[:samesite][:lax], 'samesite lax')
validate_exclusive_use_of_hash_constraints!(config[:samesite][:lax], "samesite lax")
if is_hash?(config[:samesite][:strict])
validate_exclusive_use_of_hash_constraints!(config[:samesite][:strict], 'samesite strict')
validate_exclusive_use_of_hash_constraints!(config[:samesite][:strict], "samesite strict")
validate_exclusive_use_of_samesite_enforcement!(:only)
validate_exclusive_use_of_samesite_enforcement!(:except)
end

Просмотреть файл

@ -1,3 +1,4 @@
# frozen_string_literal: true
module SecureHeaders
module ViewHelpers
include SecureHeaders::HashHelper
@ -75,7 +76,7 @@ module SecureHeaders
end
content = capture(&block)
file_path = File.join('app', 'views', self.instance_variable_get(:@virtual_path) + '.html.erb')
file_path = File.join("app", "views", self.instance_variable_get(:@virtual_path) + ".html.erb")
if raise_error_on_unrecognized_hash
hash_value = hash_source(content)

Просмотреть файл

@ -1,3 +1,4 @@
# frozen_string_literal: true
INLINE_SCRIPT_REGEX = /(<script(\s*(?!src)([\w\-])+=([\"\'])[^\"\']+\4)*\s*>)(.*?)<\/script>/mx unless defined? INLINE_SCRIPT_REGEX
INLINE_STYLE_REGEX = /(<style[^>]*>)(.*?)<\/style>/mx unless defined? INLINE_STYLE_REGEX
INLINE_HASH_SCRIPT_HELPER_REGEX = /<%=\s?hashed_javascript_tag(.*?)\s+do\s?%>(.*?)<%\s*end\s*%>/mx unless defined? INLINE_HASH_SCRIPT_HELPER_REGEX
@ -73,7 +74,7 @@ namespace :secure_headers do
end
end
File.open(SecureHeaders::Configuration::HASH_CONFIG_FILE, 'w') do |file|
File.open(SecureHeaders::Configuration::HASH_CONFIG_FILE, "w") do |file|
file.write(script_hashes.to_yaml)
end

Просмотреть файл

@ -1,10 +1,11 @@
# -*- encoding: utf-8 -*-
# frozen_string_literal: true
Gem::Specification.new do |gem|
gem.name = "secure_headers"
gem.version = "3.6.4"
gem.authors = ["Neil Matatall"]
gem.email = ["neil.matatall@gmail.com"]
gem.description = 'Manages application of security headers with many safe defaults.'
gem.description = "Manages application of security headers with many safe defaults."
gem.summary = 'Add easily configured security headers to responses
including content-security-policy, x-frame-options,
strict-transport-security, etc.'

Просмотреть файл

@ -1,4 +1,5 @@
require 'spec_helper'
# frozen_string_literal: true
require "spec_helper"
module SecureHeaders
describe Configuration do

Просмотреть файл

@ -1,4 +1,5 @@
require 'spec_helper'
# frozen_string_literal: true
require "spec_helper"
module SecureHeaders
describe ClearSiteData do

Просмотреть файл

@ -1,4 +1,5 @@
require 'spec_helper'
# frozen_string_literal: true
require "spec_helper"
module SecureHeaders
describe ContentSecurityPolicy do
@ -52,7 +53,7 @@ module SecureHeaders
end
it "does not remove schemes when :preserve_schemes is true" do
csp = ContentSecurityPolicy.new(default_src: %w(https://example.org), :preserve_schemes => true)
csp = ContentSecurityPolicy.new(default_src: %w(https://example.org), preserve_schemes: true)
expect(csp.value).to eq("default-src https://example.org")
end

Просмотреть файл

@ -1,4 +1,5 @@
require 'spec_helper'
# frozen_string_literal: true
require "spec_helper"
module SecureHeaders
describe Cookie do

Просмотреть файл

@ -1,4 +1,5 @@
require 'spec_helper'
# frozen_string_literal: true
require "spec_helper"
module SecureHeaders
describe PolicyManagement do

Просмотреть файл

@ -1,4 +1,5 @@
require 'spec_helper'
# frozen_string_literal: true
require "spec_helper"
module SecureHeaders
describe PublicKeyPins do
@ -8,7 +9,7 @@ module SecureHeaders
specify { expect(PublicKeyPins.new(max_age: 1234).value).to eq("max-age=1234") }
specify { expect(PublicKeyPins.new(max_age: 1234).value).to eq("max-age=1234") }
specify do
config = { max_age: 1234, pins: [{ sha256: 'base64encodedpin1' }, { sha256: 'base64encodedpin2' }] }
config = { max_age: 1234, pins: [{ sha256: "base64encodedpin1" }, { sha256: "base64encodedpin2" }] }
header_value = "max-age=1234; pin-sha256=\"base64encodedpin1\"; pin-sha256=\"base64encodedpin2\""
expect(PublicKeyPins.new(config).value).to eq(header_value)
end
@ -16,19 +17,19 @@ module SecureHeaders
context "with an invalid configuration" do
it "raises an exception when max-age is not provided" do
expect do
PublicKeyPins.validate_config!(foo: 'bar')
PublicKeyPins.validate_config!(foo: "bar")
end.to raise_error(PublicKeyPinsConfigError)
end
it "raises an exception with an invalid max-age" do
expect do
PublicKeyPins.validate_config!(max_age: 'abc123')
PublicKeyPins.validate_config!(max_age: "abc123")
end.to raise_error(PublicKeyPinsConfigError)
end
it 'raises an exception with less than 2 pins' do
it "raises an exception with less than 2 pins" do
expect do
config = { max_age: 1234, pins: [{ sha256: 'base64encodedpin' }] }
config = { max_age: 1234, pins: [{ sha256: "base64encodedpin" }] }
PublicKeyPins.validate_config!(config)
end.to raise_error(PublicKeyPinsConfigError)
end

Просмотреть файл

@ -1,9 +1,10 @@
require 'spec_helper'
# frozen_string_literal: true
require "spec_helper"
module SecureHeaders
describe ReferrerPolicy do
specify { expect(ReferrerPolicy.make_header).to eq([ReferrerPolicy::HEADER_NAME, "origin-when-cross-origin"]) }
specify { expect(ReferrerPolicy.make_header('no-referrer')).to eq([ReferrerPolicy::HEADER_NAME, "no-referrer"]) }
specify { expect(ReferrerPolicy.make_header("no-referrer")).to eq([ReferrerPolicy::HEADER_NAME, "no-referrer"]) }
context "valid configuration values" do
it "accepts 'no-referrer'" do
@ -61,7 +62,7 @@ module SecureHeaders
end
end
context 'invlaid configuration values' do
context "invlaid configuration values" do
it "doesn't accept invalid values" do
expect do
ReferrerPolicy.validate_config!("open")

Просмотреть файл

@ -1,4 +1,5 @@
require 'spec_helper'
# frozen_string_literal: true
require "spec_helper"
module SecureHeaders
describe StrictTransportSecurity do
@ -10,19 +11,19 @@ module SecureHeaders
context "with a string argument" do
it "raises an exception with an invalid max-age" do
expect do
StrictTransportSecurity.validate_config!('max-age=abc123')
StrictTransportSecurity.validate_config!("max-age=abc123")
end.to raise_error(STSConfigError)
end
it "raises an exception if max-age is not supplied" do
expect do
StrictTransportSecurity.validate_config!('includeSubdomains')
StrictTransportSecurity.validate_config!("includeSubdomains")
end.to raise_error(STSConfigError)
end
it "raises an exception with an invalid format" do
expect do
StrictTransportSecurity.validate_config!('max-age=123includeSubdomains')
StrictTransportSecurity.validate_config!("max-age=123includeSubdomains")
end.to raise_error(STSConfigError)
end
end

Просмотреть файл

@ -1,4 +1,5 @@
require 'spec_helper'
# frozen_string_literal: true
require "spec_helper"
module SecureHeaders
describe XContentTypeOptions do

Просмотреть файл

@ -1,9 +1,10 @@
require 'spec_helper'
# frozen_string_literal: true
require "spec_helper"
module SecureHeaders
describe XDownloadOptions do
specify { expect(XDownloadOptions.make_header).to eq([XDownloadOptions::HEADER_NAME, XDownloadOptions::DEFAULT_VALUE]) }
specify { expect(XDownloadOptions.make_header('noopen')).to eq([XDownloadOptions::HEADER_NAME, 'noopen']) }
specify { expect(XDownloadOptions.make_header("noopen")).to eq([XDownloadOptions::HEADER_NAME, "noopen"]) }
context "invalid configuration values" do
it "accepts noopen" do

Просмотреть файл

@ -1,4 +1,5 @@
require 'spec_helper'
# frozen_string_literal: true
require "spec_helper"
module SecureHeaders
describe XFrameOptions do

Просмотреть файл

@ -1,9 +1,10 @@
require 'spec_helper'
# frozen_string_literal: true
require "spec_helper"
module SecureHeaders
describe XPermittedCrossDomainPolicies do
specify { expect(XPermittedCrossDomainPolicies.make_header).to eq([XPermittedCrossDomainPolicies::HEADER_NAME, "none"]) }
specify { expect(XPermittedCrossDomainPolicies.make_header('master-only')).to eq([XPermittedCrossDomainPolicies::HEADER_NAME, 'master-only']) }
specify { expect(XPermittedCrossDomainPolicies.make_header("master-only")).to eq([XPermittedCrossDomainPolicies::HEADER_NAME, "master-only"]) }
context "valid configuration values" do
it "accepts 'all'" do
@ -36,7 +37,7 @@ module SecureHeaders
end
end
context 'invlaid configuration values' do
context "invlaid configuration values" do
it "doesn't accept invalid values" do
expect do
XPermittedCrossDomainPolicies.validate_config!("open")

Просмотреть файл

@ -1,9 +1,10 @@
require 'spec_helper'
# frozen_string_literal: true
require "spec_helper"
module SecureHeaders
describe XXssProtection do
specify { expect(XXssProtection.make_header).to eq([XXssProtection::HEADER_NAME, XXssProtection::DEFAULT_VALUE]) }
specify { expect(XXssProtection.make_header("1; mode=block; report=https://www.secure.com/reports")).to eq([XXssProtection::HEADER_NAME, '1; mode=block; report=https://www.secure.com/reports']) }
specify { expect(XXssProtection.make_header("1; mode=block; report=https://www.secure.com/reports")).to eq([XXssProtection::HEADER_NAME, "1; mode=block; report=https://www.secure.com/reports"]) }
context "with invalid configuration" do
it "should raise an error when providing a string that is not valid" do
@ -19,7 +20,7 @@ module SecureHeaders
context "when using a hash value" do
it "should allow string values ('1' or '0' are the only valid strings)" do
expect do
XXssProtection.validate_config!('1')
XXssProtection.validate_config!("1")
end.not_to raise_error
end

Просмотреть файл

@ -1,3 +1,4 @@
# frozen_string_literal: true
require "spec_helper"
module SecureHeaders
@ -19,8 +20,8 @@ module SecureHeaders
config.hpkp = {
max_age: 10000000,
pins: [
{sha256: 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c'},
{sha256: '73a2c64f9545172c1195efb6616ca5f7afd1df6f245407cafb90de3998a1c97f'}
{sha256: "b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"},
{sha256: "73a2c64f9545172c1195efb6616ca5f7afd1df6f245407cafb90de3998a1c97f"}
],
report_uri: "https://#{report_host}/example-hpkp"
}
@ -62,7 +63,7 @@ module SecureHeaders
end
request = Rack::Request.new("HTTPS" => "on")
_, env = cookie_middleware.call request.env
expect(env['Set-Cookie']).to eq("foo=bar; secure")
expect(env["Set-Cookie"]).to eq("foo=bar; secure")
end
end
@ -73,7 +74,7 @@ module SecureHeaders
end
request = Rack::Request.new("HTTPS" => "on")
_, env = cookie_middleware.call request.env
expect(env['Set-Cookie']).to eq("foo=bar")
expect(env["Set-Cookie"]).to eq("foo=bar")
end
end
end
@ -84,7 +85,7 @@ module SecureHeaders
request = Rack::Request.new("HTTPS" => "on")
_, env = cookie_middleware.call request.env
expect(env['Set-Cookie']).to eq("foo=bar; secure; HttpOnly")
expect(env["Set-Cookie"]).to eq("foo=bar; secure; HttpOnly")
end
it "flags cookies with a combination of SameSite configurations" do
@ -94,8 +95,8 @@ module SecureHeaders
request = Rack::Request.new("HTTPS" => "on")
_, env = cookie_middleware.call request.env
expect(env['Set-Cookie']).to match("_session=foobar; SameSite=Strict")
expect(env['Set-Cookie']).to match("_guest=true; SameSite=Lax")
expect(env["Set-Cookie"]).to match("_session=foobar; SameSite=Strict")
expect(env["Set-Cookie"]).to match("_guest=true; SameSite=Lax")
end
it "disables secure cookies for non-https requests" do
@ -103,7 +104,7 @@ module SecureHeaders
request = Rack::Request.new("HTTPS" => "off")
_, env = cookie_middleware.call request.env
expect(env['Set-Cookie']).to eq("foo=bar")
expect(env["Set-Cookie"]).to eq("foo=bar")
end
it "sets the secure cookie flag correctly on interleaved http/https requests" do
@ -111,11 +112,11 @@ module SecureHeaders
request = Rack::Request.new("HTTPS" => "off")
_, env = cookie_middleware.call request.env
expect(env['Set-Cookie']).to eq("foo=bar")
expect(env["Set-Cookie"]).to eq("foo=bar")
request = Rack::Request.new("HTTPS" => "on")
_, env = cookie_middleware.call request.env
expect(env['Set-Cookie']).to eq("foo=bar; secure")
expect(env["Set-Cookie"]).to eq("foo=bar; secure")
end
end
end

Просмотреть файл

@ -1,3 +1,4 @@
# frozen_string_literal: true
require "spec_helper"
require "erb"
@ -58,7 +59,7 @@ TEMPLATE
end
if options.is_a?(Hash)
options = options.map {|k,v| " #{k}=#{v}"}
options = options.map {|k, v| " #{k}=#{v}"}
end
"<#{type}#{options}>#{content}</#{type}>"
end
@ -82,9 +83,9 @@ module SecureHeaders
before(:all) do
Configuration.default do |config|
config.csp = {
:default_src => %w('self'),
:script_src => %w('self'),
:style_src => %w('self')
default_src: %w('self'),
script_src: %w('self'),
style_src: %w('self')
}
end
end

Просмотреть файл

@ -1,4 +1,5 @@
require 'spec_helper'
# frozen_string_literal: true
require "spec_helper"
module SecureHeaders
describe SecureHeaders do
@ -37,9 +38,9 @@ module SecureHeaders
SecureHeaders.opt_out_of_header(request, ContentSecurityPolicyReportOnlyConfig::CONFIG_KEY)
SecureHeaders.opt_out_of_header(request, XContentTypeOptions::CONFIG_KEY)
hash = SecureHeaders.header_hash_for(request)
expect(hash['Content-Security-Policy-Report-Only']).to be_nil
expect(hash['Content-Security-Policy']).to be_nil
expect(hash['X-Content-Type-Options']).to be_nil
expect(hash["Content-Security-Policy-Report-Only"]).to be_nil
expect(hash["Content-Security-Policy"]).to be_nil
expect(hash["X-Content-Type-Options"]).to be_nil
end
it "Carries options over when using overrides" do
@ -54,15 +55,15 @@ module SecureHeaders
SecureHeaders.use_secure_headers_override(request, :api)
hash = SecureHeaders.header_hash_for(request)
expect(hash['X-Download-Options']).to be_nil
expect(hash['X-Permitted-Cross-Domain-Policies']).to be_nil
expect(hash['X-Frame-Options']).to be_nil
expect(hash["X-Download-Options"]).to be_nil
expect(hash["X-Permitted-Cross-Domain-Policies"]).to be_nil
expect(hash["X-Frame-Options"]).to be_nil
end
it "allows you to opt out entirely" do
# configure the disabled-by-default headers to ensure they also do not get set
Configuration.default do |config|
config.csp = { :default_src => ["example.com"] }
config.csp = { default_src: ["example.com"] }
config.csp_report_only = config.csp
config.hpkp = {
report_only: false,
@ -144,10 +145,10 @@ module SecureHeaders
config.hpkp = {
max_age: 1_000_000,
include_subdomains: true,
report_uri: '//example.com/uri-directive',
report_uri: "//example.com/uri-directive",
pins: [
{ sha256: 'abc' },
{ sha256: '123' }
{ sha256: "abc" },
{ sha256: "123" }
]
}
end
@ -301,7 +302,7 @@ module SecureHeaders
SecureHeaders.content_security_policy_script_nonce(chrome_request)
hash = SecureHeaders.header_hash_for(chrome_request)
expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src mycdn.com 'nonce-#{nonce}'; style-src 'self'")
expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src mycdn.com 'nonce-#{nonce}'; style-src 'self'")
end
it "uses a nonce for safari 10+" do
@ -315,7 +316,7 @@ module SecureHeaders
safari_request = Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:safari10]))
nonce = SecureHeaders.content_security_policy_script_nonce(safari_request)
hash = SecureHeaders.header_hash_for(safari_request)
expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src mycdn.com 'nonce-#{nonce}'")
expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src mycdn.com 'nonce-#{nonce}'")
end
it "supports the deprecated `report_only: true` format" do
@ -368,8 +369,8 @@ module SecureHeaders
end
hash = SecureHeaders.header_hash_for(request)
expect(hash['Content-Security-Policy']).to eq("default-src 'self'")
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'")
expect(hash["Content-Security-Policy"]).to eq("default-src 'self'")
expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'")
end
it "sets different headers when the configs are different" do
@ -381,8 +382,8 @@ module SecureHeaders
end
hash = SecureHeaders.header_hash_for(request)
expect(hash['Content-Security-Policy']).to eq("default-src 'self'")
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; script-src 'self'")
expect(hash["Content-Security-Policy"]).to eq("default-src 'self'")
expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'; script-src 'self'")
end
it "allows you to opt-out of enforced CSP" do
@ -394,8 +395,8 @@ module SecureHeaders
end
hash = SecureHeaders.header_hash_for(request)
expect(hash['Content-Security-Policy']).to be_nil
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'")
expect(hash["Content-Security-Policy"]).to be_nil
expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'")
end
it "opts-out of enforced CSP when only csp_report_only is set" do
@ -407,8 +408,8 @@ module SecureHeaders
end
hash = SecureHeaders.header_hash_for(request)
expect(hash['Content-Security-Policy']).to be_nil
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'")
expect(hash["Content-Security-Policy"]).to be_nil
expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'")
end
it "allows you to set csp_report_only before csp" do
@ -421,50 +422,50 @@ module SecureHeaders
end
hash = SecureHeaders.header_hash_for(request)
expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src 'self' 'unsafe-inline'")
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'")
expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src 'self' 'unsafe-inline'")
expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'")
end
it "allows appending to the enforced policy" do
SecureHeaders.append_content_security_policy_directives(request, {script_src: %w(anothercdn.com)}, :enforced)
hash = SecureHeaders.header_hash_for(request)
expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src 'self' anothercdn.com")
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'")
expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src 'self' anothercdn.com")
expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'")
end
it "allows appending to the report only policy" do
SecureHeaders.append_content_security_policy_directives(request, {script_src: %w(anothercdn.com)}, :report_only)
hash = SecureHeaders.header_hash_for(request)
expect(hash['Content-Security-Policy']).to eq("default-src 'self'")
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; script-src 'self' anothercdn.com")
expect(hash["Content-Security-Policy"]).to eq("default-src 'self'")
expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'; script-src 'self' anothercdn.com")
end
it "allows appending to both policies" do
SecureHeaders.append_content_security_policy_directives(request, {script_src: %w(anothercdn.com)}, :both)
hash = SecureHeaders.header_hash_for(request)
expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src 'self' anothercdn.com")
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; script-src 'self' anothercdn.com")
expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src 'self' anothercdn.com")
expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'; script-src 'self' anothercdn.com")
end
it "allows overriding the enforced policy" do
SecureHeaders.override_content_security_policy_directives(request, {script_src: %w(anothercdn.com)}, :enforced)
hash = SecureHeaders.header_hash_for(request)
expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src anothercdn.com")
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'")
expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src anothercdn.com")
expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'")
end
it "allows overriding the report only policy" do
SecureHeaders.override_content_security_policy_directives(request, {script_src: %w(anothercdn.com)}, :report_only)
hash = SecureHeaders.header_hash_for(request)
expect(hash['Content-Security-Policy']).to eq("default-src 'self'")
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; script-src anothercdn.com")
expect(hash["Content-Security-Policy"]).to eq("default-src 'self'")
expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'; script-src anothercdn.com")
end
it "allows overriding both policies" do
SecureHeaders.override_content_security_policy_directives(request, {script_src: %w(anothercdn.com)}, :both)
hash = SecureHeaders.header_hash_for(request)
expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src anothercdn.com")
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; script-src anothercdn.com")
expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src anothercdn.com")
expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'; script-src anothercdn.com")
end
context "when inferring which config to modify" do
@ -477,8 +478,8 @@ module SecureHeaders
SecureHeaders.append_content_security_policy_directives(request, {script_src: %w(anothercdn.com)})
hash = SecureHeaders.header_hash_for(request)
expect(hash['Content-Security-Policy']).to eq("default-src 'self'; script-src 'self' anothercdn.com")
expect(hash['Content-Security-Policy-Report-Only']).to be_nil
expect(hash["Content-Security-Policy"]).to eq("default-src 'self'; script-src 'self' anothercdn.com")
expect(hash["Content-Security-Policy-Report-Only"]).to be_nil
end
it "updates the report only header when configured" do
@ -491,8 +492,8 @@ module SecureHeaders
SecureHeaders.append_content_security_policy_directives(request, {script_src: %w(anothercdn.com)})
hash = SecureHeaders.header_hash_for(request)
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; script-src 'self' anothercdn.com")
expect(hash['Content-Security-Policy']).to be_nil
expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src 'self'; script-src 'self' anothercdn.com")
expect(hash["Content-Security-Policy"]).to be_nil
end
it "updates both headers if both are configured" do
@ -507,8 +508,8 @@ module SecureHeaders
SecureHeaders.append_content_security_policy_directives(request, {script_src: %w(anothercdn.com)})
hash = SecureHeaders.header_hash_for(request)
expect(hash['Content-Security-Policy']).to eq("default-src enforced.com; script-src enforced.com anothercdn.com")
expect(hash['Content-Security-Policy-Report-Only']).to eq("default-src reportonly.com; script-src reportonly.com anothercdn.com")
expect(hash["Content-Security-Policy"]).to eq("default-src enforced.com; script-src enforced.com anothercdn.com")
expect(hash["Content-Security-Policy-Report-Only"]).to eq("default-src reportonly.com; script-src reportonly.com anothercdn.com")
end
end
@ -520,7 +521,7 @@ module SecureHeaders
it "validates your hsts config upon configuration" do
expect do
Configuration.default do |config|
config.hsts = 'lol'
config.hsts = "lol"
end
end.to raise_error(STSConfigError)
end
@ -528,7 +529,7 @@ module SecureHeaders
it "validates your csp config upon configuration" do
expect do
Configuration.default do |config|
config.csp = { ContentSecurityPolicy::DEFAULT_SRC => '123456' }
config.csp = { ContentSecurityPolicy::DEFAULT_SRC => "123456" }
end
end.to raise_error(ContentSecurityPolicyConfigError)
end
@ -536,7 +537,7 @@ module SecureHeaders
it "raises errors for unknown directives" do
expect do
Configuration.default do |config|
config.csp = { made_up_directive: '123456' }
config.csp = { made_up_directive: "123456" }
end
end.to raise_error(ContentSecurityPolicyConfigError)
end

Просмотреть файл

@ -1,12 +1,13 @@
require 'rubygems'
require 'rspec'
require 'rack'
require 'pry-nav'
# frozen_string_literal: true
require "rubygems"
require "rspec"
require "rack"
require "pry-nav"
require 'coveralls'
require "coveralls"
Coveralls.wear!
require File.join(File.dirname(__FILE__), '..', 'lib', 'secure_headers')
require File.join(File.dirname(__FILE__), "..", "lib", "secure_headers")
@ -14,9 +15,9 @@ USER_AGENTS = {
edge: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246",
firefox: "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1",
firefox46: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:46.0) Gecko/20100101 Firefox/46.0",
chrome: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5',
ie: 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/5.0)',
opera: 'Opera/9.80 (Windows NT 6.1; U; es-ES) Presto/2.9.181 Version/12.00',
chrome: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5",
ie: "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/5.0)",
opera: "Opera/9.80 (Windows NT 6.1; U; es-ES) Presto/2.9.181 Version/12.00",
ios5: "Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3",
ios6: "Mozilla/5.0 (iPhone; CPU iPhone OS 614 like Mac OS X) AppleWebKit/536.26 (KHTML like Gecko) Version/6.0 Mobile/10B350 Safari/8536.25",
safari5: "Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko ) Version/5.1 Mobile/9B176 Safari/7534.48.3",