castle-rb 3.6.0 → 3.6.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 10971e0d6aaf51fd108b6a4316e938fd223146eb65bf1d1216c585816a0d8e2c
4
- data.tar.gz: 52bae4bd484b9ccc79d6665f2a80c3839532442f4850c9fa4b1a0a7768fc3426
3
+ metadata.gz: 4b34bb0722019eb7b8249772c5f05a0e51de7e230cb00cb12a12a1ccd5c7cb1f
4
+ data.tar.gz: 97c7084f179397163ccbf652f46651892f47fbfe17622bd8a2ed3c5388c93f05
5
5
  SHA512:
6
- metadata.gz: fa31103ea67d58adb41aad80a1e925173892d73c9cad31b1028bf38194044128c9d3bedfaf399050603749e0f84ae87e48edb922124c99b9cd99b063a65734dd
7
- data.tar.gz: c203a28c0bb5bde14212282e3565123d41797ea3af151cc5e7405666bea63f58b98decb0807d7fb95de262e066f1f154d3448b1d9714545964842a78fefc0e75
6
+ metadata.gz: c2187bb33e94733bb99b49e33a999fadbc5bc42c18afb84ba1117880350e020e0415000ac0fa819ca3fe356171648e970a413479bb04558b1e9cdb588a68012e
7
+ data.tar.gz: 41edcf28a794675f760d3be4322fc920ae7458047907407074626fb97a1698c14b88bf2b15a0dc7d784f537ff1ada225c421cd3c38e6017bf5b4a07a09a90a14
data/README.md CHANGED
@@ -67,15 +67,24 @@ Castle.configure do |config|
67
67
 
68
68
  # Whitelisted and Blacklisted headers are case insensitive and allow to use _ and - as a separator, http prefixes are removed
69
69
  # Whitelisted headers
70
- # @note In case of the whitelist, we won't send the values of other headers but we will send their names
71
- config.whitelisted = ['X_HEADER']
72
- # or append to default
73
- config.whitelisted += ['http-x-header']
74
-
75
- # Blacklisted headers take advantage over whitelisted elements
70
+ # By default, the SDK sends all HTTP headers, except for Cookie and Authorization.
71
+ # If you decide to use a whitelist, the SDK will:
72
+ # - always send the User-Agent header
73
+ # - send scrubbed values of non-whitelisted headers
74
+ # - send proper values of whitelisted headers.
75
+ # @example
76
+ # config.whitelisted = ['X_HEADER']
77
+ # # will send { 'User-Agent' => 'Chrome', 'X_HEADER' => 'proper value', 'Any-Other-Header' => true }
78
+ #
79
+ # We highly suggest using blacklist instead of whitelist, so that Castle can use as many data points
80
+ # as possible to secure your users. If you want to use the whitelist, this is the minimal
81
+ # amount of headers we recommend:
82
+ config.whitelisted = Castle::Configuration::DEFAULT_WHITELIST
83
+
84
+ # Blacklisted headers take precedence over whitelisted elements
85
+ # We always blacklist Cookie and Authentication headers. If you use any other headers that
86
+ # might contain sensitive information, you should blacklist them.
76
87
  config.blacklisted = ['HTTP-X-header']
77
- # or append to default
78
- config.blacklisted += ['X_HEADER']
79
88
  end
80
89
  ```
81
90
 
@@ -9,22 +9,27 @@ module Castle
9
9
  FAILOVER_STRATEGY = :allow
10
10
  REQUEST_TIMEOUT = 500 # in milliseconds
11
11
  FAILOVER_STRATEGIES = %i[allow deny challenge throw].freeze
12
- WHITELISTED = [
13
- 'User-Agent',
14
- 'Accept-Language',
15
- 'Accept-Encoding',
16
- 'Accept-Charset',
17
- 'Accept',
18
- 'Accept-Datetime',
19
- 'X-Forwarded-For',
20
- 'Forwarded',
21
- 'X-Forwarded',
22
- 'X-Real-IP',
23
- 'REMOTE_ADDR',
24
- 'X-Forwarded-For',
25
- 'CF_CONNECTING_IP'
12
+
13
+ # @note this value is not assigned as we don't recommend using a whitelist. If you need to use
14
+ # one, this constant is provided as a good default.
15
+ DEFAULT_WHITELIST = %w[
16
+ Accept
17
+ Accept-Charset
18
+ Accept-Datetime
19
+ Accept-Encoding
20
+ Accept-Language
21
+ Cache-Control
22
+ Connection
23
+ Content-Length
24
+ Content-Type
25
+ Host
26
+ Origin
27
+ Pragma
28
+ Referer
29
+ TE
30
+ Upgrade-Insecure-Requests
31
+ X-Castle-Client-Id
26
32
  ].freeze
27
- BLACKLISTED = ['HTTP_COOKIE'].freeze
28
33
 
29
34
  attr_accessor :host, :port, :request_timeout, :url_prefix
30
35
  attr_reader :api_secret, :whitelisted, :blacklisted, :failover_strategy
@@ -36,8 +41,8 @@ module Castle
36
41
  self.host = HOST
37
42
  self.port = PORT
38
43
  self.url_prefix = URL_PREFIX
39
- self.whitelisted = WHITELISTED
40
- self.blacklisted = BLACKLISTED
44
+ self.whitelisted = [].freeze
45
+ self.blacklisted = [].freeze
41
46
  self.api_secret = ''
42
47
  end
43
48
 
@@ -4,23 +4,45 @@ module Castle
4
4
  module Extractors
5
5
  # used for extraction of cookies and headers from the request
6
6
  class Headers
7
+ # Headers that we will never scrub, even if they land on the configuration blacklist.
8
+ ALWAYS_INCLUDED_HEADERS = %w[User-Agent].freeze
9
+
10
+ # Headers that will always be scrubbed, even if whitelisted.
11
+ ALWAYS_SCRUBBED_HEADERS = %w[Cookie Authorization].freeze
12
+
13
+ # Rack does not add the HTTP_ prefix to Content-Length for some reason
14
+ CONTENT_LENGTH = 'CONTENT_LENGTH'
15
+
16
+ # Prefix that Rack adds for HTTP headers
17
+ HTTP_HEADER_PREFIX = 'HTTP_'
18
+
19
+ private_constant :ALWAYS_INCLUDED_HEADERS, :ALWAYS_SCRUBBED_HEADERS,
20
+ :CONTENT_LENGTH, :HTTP_HEADER_PREFIX
21
+
22
+ # @param request [Rack::Request]
7
23
  def initialize(request)
8
- @request = request
9
- @request_env = @request.env
24
+ @request_env = request.env
10
25
  @formatter = HeaderFormatter.new
11
26
  end
12
27
 
13
28
  # Serialize HTTP headers
29
+ # @return [Hash]
14
30
  def call
15
- @request_env.keys.each_with_object({}) do |header, acc|
16
- name = @formatter.call(header)
31
+ @request_env.keys.each_with_object({}) do |env_header, acc|
32
+ next unless env_header.start_with?(HTTP_HEADER_PREFIX) || env_header == CONTENT_LENGTH
33
+
34
+ header = @formatter.call(env_header)
17
35
 
18
- if Castle.config.whitelisted.include?(name) && !Castle.config.blacklisted.include?(name)
19
- acc[name] = @request_env[header]
36
+ if ALWAYS_SCRUBBED_HEADERS.include?(header)
37
+ acc[header] = true
38
+ elsif ALWAYS_INCLUDED_HEADERS.include?(header)
39
+ acc[header] = @request_env[env_header]
40
+ elsif Castle.config.blacklisted.include?(header)
41
+ acc[header] = true
42
+ elsif Castle.config.whitelisted.empty? || Castle.config.whitelisted.include?(header)
43
+ acc[header] = @request_env[env_header]
20
44
  else
21
- # When a header is not whitelisted or blacklisted, we're not suppose to send
22
- # it's value but we should send it's name to indicate it's presence
23
- acc[name] = true
45
+ acc[header] = true
24
46
  end
25
47
  end
26
48
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Castle
4
- VERSION = '3.6.0'
4
+ VERSION = '3.6.1'
5
5
  end
@@ -22,13 +22,7 @@ describe Castle::Client do
22
22
 
23
23
  let(:headers) do
24
24
  {
25
- 'Rack.version': true, 'Rack.input': true, 'Rack.errors': true,
26
- 'Rack.multithread': true, 'Rack.multiprocess': true, 'Rack.run-Once': true,
27
- 'Request-Method': true, 'Server-Name': true, 'Server-Port': true,
28
- 'Query-String': true, 'Path-Info': true, 'Rack.url-Scheme': true,
29
- 'Https': true, 'Script-Name': true, 'Content-Length': true,
30
- 'User-Agent': ua, 'X-Forwarded-For': ip.to_s, 'Rack.request.cookie-Hash': true,
31
- 'Rack.request.cookie-String': true, 'Cookie': true
25
+ 'Content-Length': '0', 'User-Agent': ua, 'X-Forwarded-For': ip.to_s, 'Cookie': true
32
26
  }
33
27
  end
34
28
  let(:context) do
@@ -77,7 +77,7 @@ describe Castle::Configuration do
77
77
 
78
78
  describe 'whitelisted' do
79
79
  it do
80
- expect(config.whitelisted.size).to be_eql(13)
80
+ expect(config.whitelisted.size).to be_eql(0)
81
81
  end
82
82
 
83
83
  context 'with setter' do
@@ -88,19 +88,11 @@ describe Castle::Configuration do
88
88
  expect(config.whitelisted).to be_eql(['Header'])
89
89
  end
90
90
  end
91
-
92
- context 'when appending' do
93
- before do
94
- config.whitelisted += ['header']
95
- end
96
- it { expect(config.whitelisted).to be_include('Header') }
97
- it { expect(config.whitelisted.size).to be_eql(14) }
98
- end
99
91
  end
100
92
 
101
93
  describe 'blacklisted' do
102
94
  it do
103
- expect(config.blacklisted.size).to be_eql(1)
95
+ expect(config.blacklisted.size).to be_eql(0)
104
96
  end
105
97
 
106
98
  context 'with setter' do
@@ -111,14 +103,6 @@ describe Castle::Configuration do
111
103
  expect(config.blacklisted).to be_eql(['Header'])
112
104
  end
113
105
  end
114
-
115
- context 'when appending' do
116
- before do
117
- config.blacklisted += ['header']
118
- end
119
- it { expect(config.blacklisted).to be_include('Header') }
120
- it { expect(config.blacklisted.size).to be_eql(2) }
121
- end
122
106
  end
123
107
 
124
108
  describe 'failover_strategy' do
@@ -9,8 +9,8 @@ describe Castle::Context::Default do
9
9
  let(:env) do
10
10
  Rack::MockRequest.env_for('/',
11
11
  'HTTP_X_FORWARDED_FOR' => ip,
12
- 'HTTP-Accept-Language' => 'en',
13
- 'HTTP-User-Agent' => 'test',
12
+ 'HTTP_ACCEPT_LANGUAGE' => 'en',
13
+ 'HTTP_USER_AGENT' => 'test',
14
14
  'HTTP_COOKIE' => "__cid=#{cookie_id};other=efgh")
15
15
  end
16
16
  let(:request) { Rack::Request.new(env) }
@@ -23,18 +23,17 @@ describe Castle::Context::Default do
23
23
 
24
24
  it { expect(default_context[:active]).to be_eql(true) }
25
25
  it { expect(default_context[:origin]).to be_eql('web') }
26
- it {
26
+
27
+ it do
27
28
  expect(default_context[:headers]).to be_eql(
28
- 'Rack.version' => true, 'Rack.input' => true, 'Rack.errors' => true,
29
- 'Rack.multithread' => true, 'Rack.multiprocess' => true, 'Rack.run-Once' => true,
30
- 'Request-Method' => true, 'Server-Name' => true, 'Server-Port' => true,
31
- 'Query-String' => true, 'Path-Info' => true, 'Rack.url-Scheme' => true,
32
- 'Https' => true, 'Script-Name' => true, 'Content-Length' => true,
33
- 'X-Forwarded-For' => '1.2.3.4', 'Accept-Language' => 'en', 'User-Agent' => 'test',
34
- 'Rack.request.cookie-Hash' => true, 'Rack.request.cookie-String' => true,
29
+ 'X-Forwarded-For' => '1.2.3.4',
30
+ 'Accept-Language' => 'en',
31
+ 'User-Agent' => 'test',
32
+ 'Content-Length' => '0',
35
33
  'Cookie' => true
36
34
  )
37
- }
35
+ end
36
+
38
37
  it { expect(default_context[:ip]).to be_eql(ip) }
39
38
  it { expect(default_context[:library][:name]).to be_eql('castle-rb') }
40
39
  it { expect(default_context[:library][:version]).to be_eql(version) }
@@ -1,32 +1,88 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  describe Castle::Extractors::Headers do
4
- subject(:extractor) { described_class.new(request) }
4
+ subject(:headers) { described_class.new(request).call }
5
5
 
6
6
  let(:client_id) { 'abcd' }
7
7
  let(:env) do
8
- Rack::MockRequest.env_for('/',
9
- 'HTTP_X_FORWARDED_FOR' => '1.2.3.4',
10
- 'HTTP_OK' => 'OK',
11
- 'TEST' => '1',
12
- 'HTTP_COOKIE' => "__cid=#{client_id};other=efgh")
8
+ Rack::MockRequest.env_for(
9
+ '/',
10
+ 'Action-Dispatch.request.content-Type' => 'application/json',
11
+ 'HTTP_AUTHORIZATION' => 'Basic 123456',
12
+ 'HTTP_COOKIE' => "__cid=#{client_id};other=efgh",
13
+ 'HTTP_OK' => 'OK',
14
+ 'HTTP_ACCEPT' => 'application/json',
15
+ 'HTTP_X_FORWARDED_FOR' => '1.2.3.4',
16
+ 'HTTP_USER_AGENT' => 'Mozilla 1234',
17
+ 'TEST' => '1'
18
+ )
13
19
  end
14
20
  let(:request) { Rack::Request.new(env) }
15
21
 
16
- describe 'extract http headers with whitelisted and blacklisted support' do
22
+ context 'when whitelist is not set in the configuration' do
23
+ it do
24
+ is_expected.to eq('Accept' => 'application/json',
25
+ 'Authorization' => true,
26
+ 'Cookie' => true,
27
+ 'Content-Length' => '0',
28
+ 'Ok' => 'OK',
29
+ 'User-Agent' => 'Mozilla 1234',
30
+ 'X-Forwarded-For' => '1.2.3.4')
31
+ end
32
+ end
33
+
34
+ context 'when whitelist is set in the configuration' do
35
+ before { Castle.config.whitelisted = %w[Accept OK] }
36
+
37
+ it do
38
+ is_expected.to eq('Accept' => 'application/json',
39
+ 'Authorization' => true,
40
+ 'Cookie' => true,
41
+ 'Content-Length' => true,
42
+ 'Ok' => 'OK',
43
+ 'User-Agent' => 'Mozilla 1234',
44
+ 'X-Forwarded-For' => true)
45
+ end
46
+ end
47
+
48
+ context 'when blacklist is set in the configuration' do
49
+ context 'with a User-Agent' do
50
+ before { Castle.config.blacklisted = %w[User-Agent] }
51
+
52
+ it do
53
+ is_expected.to eq('Accept' => 'application/json',
54
+ 'Authorization' => true,
55
+ 'Cookie' => true,
56
+ 'Content-Length' => '0',
57
+ 'Ok' => 'OK',
58
+ 'User-Agent' => 'Mozilla 1234',
59
+ 'X-Forwarded-For' => '1.2.3.4')
60
+ end
61
+ end
62
+
63
+ context 'with a different header' do
64
+ before { Castle.config.blacklisted = %w[Accept] }
65
+
66
+ it do
67
+ is_expected.to eq('Accept' => true,
68
+ 'Authorization' => true,
69
+ 'Cookie' => true,
70
+ 'Content-Length' => '0',
71
+ 'Ok' => 'OK',
72
+ 'User-Agent' => 'Mozilla 1234',
73
+ 'X-Forwarded-For' => '1.2.3.4')
74
+ end
75
+ end
76
+ end
77
+
78
+ context 'when a header is both whitelisted and blacklisted' do
17
79
  before do
18
- Castle.config.whitelisted += ['TEST']
80
+ Castle.config.whitelisted = %w[Accept]
81
+ Castle.config.blacklisted = %w[Accept]
19
82
  end
83
+
20
84
  it do
21
- expect(extractor.call).to eql(
22
- 'Test' => '1', 'Ok' => true, 'Rack.version' => true,
23
- 'Rack.input' => true, 'Rack.errors' => true, 'Rack.multithread' => true,
24
- 'Rack.multiprocess' => true, 'Rack.run-Once' => true, 'Request-Method' => true,
25
- 'Server-Name' => true, 'Server-Port' => true, 'Query-String' => true,
26
- 'Path-Info' => true, 'Rack.url-Scheme' => true, 'Https' => true,
27
- 'Script-Name' => true, 'Content-Length' => true, 'X-Forwarded-For' => '1.2.3.4',
28
- 'Cookie' => true
29
- )
85
+ expect(headers['Accept']).to eq(true)
30
86
  end
31
87
  end
32
88
  end
@@ -12,11 +12,14 @@ Coveralls.wear!
12
12
 
13
13
  require 'castle'
14
14
 
15
- Castle.configure do |config|
16
- config.api_secret = 'secret'
17
- end
18
-
19
15
  WebMock.disable_net_connect!(allow_localhost: true)
20
16
 
21
17
  RSpec.configure do |config|
18
+ config.before do
19
+ Castle.instance_variable_set(:@configuration, Castle::Configuration.new)
20
+
21
+ Castle.configure do |cfg|
22
+ cfg.api_secret = 'secret'
23
+ end
24
+ end
22
25
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: castle-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.6.0
4
+ version: 3.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Johan Brissmyr
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-07 00:00:00.000000000 Z
11
+ date: 2020-01-16 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Castle protects your users from account compromise
14
14
  email: johan@castle.io