Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

switch to allowlist and denylist #203

Merged
merged 1 commit into from
Sep 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## master

- [#203](https://github.com/castle/castle-ruby/pull/203) switch to denylist and allowlist

## 4.3.0 (2020-05-22)

- [#197](https://github.com/castle/castle-ruby/pull/197) add `trusted_proxy_depth` and `trust_proxy_chain` configuration options
Expand Down
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,26 +65,26 @@ Castle.configure do |config|
# Castle::RequestError is raised when timing out in milliseconds (default: 500 milliseconds)
config.request_timeout = 2000

# Whitelisted and Blacklisted headers are case insensitive and allow to use _ and - as a separator, http prefixes are removed
# Whitelisted headers
# Allowlisted and Denylisted headers are case insensitive and allow to use _ and - as a separator, http prefixes are removed
# Allowlisted headers
# By default, the SDK sends all HTTP headers, except for Cookie and Authorization.
# If you decide to use a whitelist, the SDK will:
# If you decide to use a allowlist, the SDK will:
# - always send the User-Agent header
# - send scrubbed values of non-whitelisted headers
# - send proper values of whitelisted headers.
# - send scrubbed values of non-allowlisted headers
# - send proper values of allowlisted headers.
# @example
# config.whitelisted = ['X_HEADER']
# config.allowlisted = ['X_HEADER']
# # will send { 'User-Agent' => 'Chrome', 'X_HEADER' => 'proper value', 'Any-Other-Header' => true }
#
# We highly suggest using blacklist instead of whitelist, so that Castle can use as many data points
# as possible to secure your users. If you want to use the whitelist, this is the minimal
# We highly suggest using denylist instead of allowlist, so that Castle can use as many data points
# as possible to secure your users. If you want to use the allowlist, this is the minimal
# amount of headers we recommend:
config.whitelisted = Castle::Configuration::DEFAULT_WHITELIST
config.allowlisted = Castle::Configuration::DEFAULT_ALLOWLIST

# Blacklisted headers take precedence over whitelisted elements
# We always blacklist Cookie and Authentication headers. If you use any other headers that
# might contain sensitive information, you should blacklist them.
config.blacklisted = ['HTTP-X-header']
# Denylisted headers take precedence over allowlisted elements
# We always denylist Cookie and Authentication headers. If you use any other headers that
# might contain sensitive information, you should denylist them.
config.denylisted = ['HTTP-X-header']

# Castle needs the original IP of the client, not the IP of your proxy or load balancer.
# The SDK will only trust the proxy chain as defined in the configuration.
Expand Down
18 changes: 9 additions & 9 deletions lib/castle/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ class Configuration
\Aunix:
/ix].freeze

# @note this value is not assigned as we don't recommend using a whitelist. If you need to use
# @note this value is not assigned as we don't recommend using a allowlist. If you need to use
# one, this constant is provided as a good default.
DEFAULT_WHITELIST = %w[
DEFAULT_ALLOWLIST = %w[
Accept
Accept-Charset
Accept-Datetime
Expand All @@ -45,7 +45,7 @@ class Configuration
].freeze

attr_accessor :host, :port, :request_timeout, :url_prefix, :trust_proxy_chain
attr_reader :api_secret, :whitelisted, :blacklisted, :failover_strategy, :ip_headers,
attr_reader :api_secret, :allowlisted, :denylisted, :failover_strategy, :ip_headers,
:trusted_proxies, :trusted_proxy_depth

def initialize
Expand All @@ -59,8 +59,8 @@ def reset
self.host = HOST
self.port = PORT
self.url_prefix = URL_PREFIX
self.whitelisted = [].freeze
self.blacklisted = [].freeze
self.allowlisted = [].freeze
self.denylisted = [].freeze
self.api_secret = ENV.fetch('CASTLE_API_SECRET', '')
self.ip_headers = [].freeze
self.trusted_proxies = [].freeze
Expand All @@ -72,12 +72,12 @@ def api_secret=(value)
@api_secret = value.to_s
end

def whitelisted=(value)
@whitelisted = (value ? value.map { |header| @formatter.call(header) } : []).freeze
def allowlisted=(value)
@allowlisted = (value ? value.map { |header| @formatter.call(header) } : []).freeze
end

def blacklisted=(value)
@blacklisted = (value ? value.map { |header| @formatter.call(header) } : []).freeze
def denylisted=(value)
@denylisted = (value ? value.map { |header| @formatter.call(header) } : []).freeze
end

# sets ip headers
Expand Down
20 changes: 10 additions & 10 deletions lib/castle/extractors/headers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@ module Castle
module Extractors
# used for extraction of cookies and headers from the request
class Headers
# Headers that we will never scrub, even if they land on the configuration blacklist.
ALWAYS_WHITELISTED = %w[User-Agent].freeze
# Headers that we will never scrub, even if they land on the configuration denylist.
ALWAYS_ALLOWLISTED = %w[User-Agent].freeze

# Headers that will always be scrubbed, even if whitelisted.
ALWAYS_BLACKLISTED = %w[Cookie Authorization].freeze
# Headers that will always be scrubbed, even if allowlisted.
ALWAYS_DENYLISTED = %w[Cookie Authorization].freeze

private_constant :ALWAYS_WHITELISTED, :ALWAYS_BLACKLISTED
private_constant :ALWAYS_ALLOWLISTED, :ALWAYS_DENYLISTED

# @param headers [Hash]
def initialize(headers)
@headers = headers
@no_whitelist = Castle.config.whitelisted.empty?
@no_allowlist = Castle.config.allowlisted.empty?
end

# Serialize HTTP headers
Expand All @@ -33,10 +33,10 @@ def call
# @param value [String]
# @return [TrueClass | FalseClass | String]
def header_value(name, value)
return true if ALWAYS_BLACKLISTED.include?(name)
return value if ALWAYS_WHITELISTED.include?(name)
return true if Castle.config.blacklisted.include?(name)
return value if @no_whitelist || Castle.config.whitelisted.include?(name)
return true if ALWAYS_DENYLISTED.include?(name)
return value if ALWAYS_ALLOWLISTED.include?(name)
return true if Castle.config.denylisted.include?(name)
return value if @no_allowlist || Castle.config.allowlisted.include?(name)

true
end
Expand Down
16 changes: 8 additions & 8 deletions spec/lib/castle/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,34 +89,34 @@
end
end

describe 'whitelisted' do
describe 'allowlisted' do
it do
expect(config.whitelisted.size).to be_eql(0)
expect(config.allowlisted.size).to be_eql(0)
end

context 'with setter' do
before do
config.whitelisted = ['header']
config.allowlisted = ['header']
end

it do
expect(config.whitelisted).to be_eql(['Header'])
expect(config.allowlisted).to be_eql(['Header'])
end
end
end

describe 'blacklisted' do
describe 'denylisted' do
it do
expect(config.blacklisted.size).to be_eql(0)
expect(config.denylisted.size).to be_eql(0)
end

context 'with setter' do
before do
config.blacklisted = ['header']
config.denylisted = ['header']
end

it do
expect(config.blacklisted).to be_eql(['Header'])
expect(config.denylisted).to be_eql(['Header'])
end
end
end
Expand Down
22 changes: 11 additions & 11 deletions spec/lib/castle/extractors/headers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
end

after do
Castle.config.whitelisted = %w[]
Castle.config.blacklisted = %w[]
Castle.config.allowlisted = %w[]
Castle.config.denylisted = %w[]
end

context 'when whitelist is not set in the configuration' do
context 'when allowlist is not set in the configuration' do
let(:result) do
{
'Accept' => 'application/json',
Expand All @@ -36,8 +36,8 @@
it { expect(headers).to eq(result) }
end

context 'when whitelist is set in the configuration' do
before { Castle.config.whitelisted = %w[Accept OK] }
context 'when allowlist is set in the configuration' do
before { Castle.config.allowlisted = %w[Accept OK] }

let(:result) do
{
Expand All @@ -54,7 +54,7 @@
it { expect(headers).to eq(result) }
end

context 'when blacklist is set in the configuration' do
context 'when denylist is set in the configuration' do
context 'with a User-Agent' do
let(:result) do
{
Expand All @@ -68,7 +68,7 @@
}
end

before { Castle.config.blacklisted = %w[User-Agent] }
before { Castle.config.denylisted = %w[User-Agent] }

it { expect(headers).to eq(result) }
end
Expand All @@ -86,16 +86,16 @@
}
end

before { Castle.config.blacklisted = %w[Accept] }
before { Castle.config.denylisted = %w[Accept] }

it { expect(headers).to eq(result) }
end
end

context 'when a header is both whitelisted and blacklisted' do
context 'when a header is both allowlisted and denylisted' do
before do
Castle.config.whitelisted = %w[Accept]
Castle.config.blacklisted = %w[Accept]
Castle.config.allowlisted = %w[Accept]
Castle.config.denylisted = %w[Accept]
end

it do
Expand Down