lhc 14.0.0 → 15.0.0

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: 7379ccaa5a569acd2eca8b6ea938996477391648efe913b45ff07e478f81e471
4
- data.tar.gz: a277d00a29c59e3da5c9fa4fa5b130a201d253ad65ac26a75e1bc348eacf479f
3
+ metadata.gz: 3a34aeee4fa910b8099acfdd73813103beb630400b92b449dbfbf4d87de4e041
4
+ data.tar.gz: 846b8cc9e24b9b0b9189b24d38c3e7b485096b4afd970cfc6fe48c8ee3a76d7e
5
5
  SHA512:
6
- metadata.gz: 76713bd28a5e9ce252bfef62f77d5b04cf3820a290089d7331f2d1445a4e04ff4ebcfa6180b50d75f0ce4b0b62454f3a4bbece2f7a41f37fa2ea8158e755a196
7
- data.tar.gz: e33b20e2c2d8f12b68e5ef76842bc94c61cd761be66d4d61b22b0c2501fd6dfc4041c7a9e32bff1d964e34c154d40ed6acf865cfeadb01c7318729597c96b25c
6
+ metadata.gz: 067d2e56385e931f00caa829411888881705ec19a702979332ad5074bf1a6515f0893e09111314b825fa7dd6d559518c997ac309f17e06eee245faffe93fcebd
7
+ data.tar.gz: 5284f04c83ca525e774775c41bd88d9f2dc8feeaf8f1fb47d3b9bde5677eeaad9bec6be6213597c4d1f34a2a22f8703b9f8e7fef0a4a09a58f24a6f72b2bf66b
data/README.md CHANGED
@@ -51,6 +51,7 @@ use it like:
51
51
  * [Configuration](#configuration)
52
52
  * [Configuring endpoints](#configuring-endpoints)
53
53
  * [Configuring placeholders](#configuring-placeholders)
54
+ * [Configuring scrubs](#configuring-scrubs)
54
55
  * [Interceptors](#interceptors)
55
56
  * [Quick start: Configure/Enable Interceptors](#quick-start-configureenable-interceptors)
56
57
  * [Interceptors on local request level](#interceptors-on-local-request-level)
@@ -491,6 +492,50 @@ You can configure global placeholders, that are used when generating urls from u
491
492
  LHC.get(:feedbacks) # http://datastore/v2/feedbacks
492
493
  ```
493
494
 
495
+ ### Configuring scrubs
496
+
497
+ You can filter out sensitive request data from your log files and rollbar by appending them to `LHS.config.scrubs`. These values will be marked `[FILTERED]` in the log and on rollbar. Also nested parameters are being filtered.
498
+ The scrubbing configuration affects all request done by LHC independent of the endpoint. You can scrub any attribute within `:params`, `:headers` or `:body`. For `:auth` you either can choose `:bearer` or `:basic` (default is both).
499
+
500
+ LHS scrubs per default:
501
+ - Bearer Token within the Request Header
502
+ - Basic Auth `username` and `password` within the Request Header
503
+ - `password` and `password_confirmation` within the Request Body
504
+
505
+ Enhance the default scrubbing by pushing the name of the parameter, which should be scrubbed, as string to the existing configuration.
506
+ You can also add multiple parameters at once by pushing multiple strings.
507
+
508
+ Example:
509
+ ```ruby
510
+ LHC.configure do |c|
511
+ c.scrubs[:params] << 'api_key'
512
+ c.scrubs[:body].push('user_token', 'secret_key')
513
+ end
514
+ ```
515
+
516
+ For disabling scrubbing, add following configuration:
517
+ ```ruby
518
+ LHC.configure do |c|
519
+ c.scrubs = {}
520
+ end
521
+ ```
522
+
523
+ If you want to turn off `:bearer` or `:basic` scrubbing, then just overwrite the `:auth` configuration.
524
+
525
+ Example:
526
+ ```ruby
527
+ LHC.configure do |c|
528
+ c.scrubs[:auth] = [:bearer]
529
+ end
530
+ ```
531
+
532
+ If your app has a different authentication strategy than Bearer Authentication or Basic Authentication then you can filter the data by scrubbing the whole header:
533
+ ```ruby
534
+ LHC.configure do |c|
535
+ c.scrubs[:headers] << 'Authorization'
536
+ end
537
+ ```
538
+
494
539
  ## Interceptors
495
540
 
496
541
  To monitor and manipulate the HTTP communication done with LHC, you can define interceptors that follow the (Inteceptor Pattern)[https://en.wikipedia.org/wiki/Interceptor_pattern].
data/lib/lhc.rb CHANGED
@@ -113,6 +113,17 @@ module LHC
113
113
  autoload :UnknownError,
114
114
  'lhc/errors/unknown_error'
115
115
 
116
+ autoload :Scrubber,
117
+ 'lhc/scrubber'
118
+ autoload :AuthScrubber,
119
+ 'lhc/scrubbers/auth_scrubber'
120
+ autoload :BodyScrubber,
121
+ 'lhc/scrubbers/body_scrubber'
122
+ autoload :HeadersScrubber,
123
+ 'lhc/scrubbers/headers_scrubber'
124
+ autoload :ParamsScrubber,
125
+ 'lhc/scrubbers/params_scrubber'
126
+
116
127
  autoload :Interceptor,
117
128
  'lhc/interceptor'
118
129
  autoload :Interceptors,
data/lib/lhc/config.rb CHANGED
@@ -5,9 +5,12 @@ require 'singleton'
5
5
  class LHC::Config
6
6
  include Singleton
7
7
 
8
+ attr_accessor :scrubs
9
+
8
10
  def initialize
9
11
  @endpoints = {}
10
12
  @placeholders = {}
13
+ @scrubs = default_scrubs
11
14
  end
12
15
 
13
16
  def endpoint(name, url, options = {})
@@ -42,9 +45,19 @@ class LHC::Config
42
45
  @interceptors = interceptors
43
46
  end
44
47
 
48
+ def default_scrubs
49
+ {
50
+ auth: [:bearer, :basic],
51
+ params: [],
52
+ headers: [],
53
+ body: ['password', 'password_confirmation']
54
+ }
55
+ end
56
+
45
57
  def reset
46
58
  @endpoints = {}
47
59
  @placeholders = {}
48
60
  @interceptors = nil
61
+ @scrubs = default_scrubs
49
62
  end
50
63
  end
data/lib/lhc/error.rb CHANGED
@@ -72,13 +72,12 @@ class LHC::Error < StandardError
72
72
 
73
73
  debug = []
74
74
  debug << [request.method, request.url].map { |str| self.class.fix_invalid_encoding(str) }.join(' ')
75
- debug << "Options: #{request.options}"
76
- debug << "Headers: #{request.headers}"
75
+ debug << "Options: #{request.scrubbed_options}"
76
+ debug << "Headers: #{request.scrubbed_headers}"
77
77
  debug << "Response Code: #{response.code} (#{response.options[:return_code]})"
78
78
  debug << "Response Options: #{response.options}"
79
79
  debug << response.body
80
80
  debug << _message
81
-
82
81
  debug.map { |str| self.class.fix_invalid_encoding(str) }.join("\n")
83
82
  end
84
83
  end
@@ -30,7 +30,7 @@ class LHC::Auth < LHC::Interceptor
30
30
  def basic_authentication!
31
31
  auth = auth_options[:basic]
32
32
  credentials = "#{auth[:username]}:#{auth[:password]}"
33
- set_authorization_header("Basic #{Base64.strict_encode64(credentials).chomp}")
33
+ set_basic_authorization_header(Base64.strict_encode64(credentials).chomp)
34
34
  end
35
35
 
36
36
  def bearer_authentication!
@@ -44,7 +44,13 @@ class LHC::Auth < LHC::Interceptor
44
44
  request.headers['Authorization'] = value
45
45
  end
46
46
 
47
+ def set_basic_authorization_header(base_64_encoded_credentials)
48
+ request.options[:auth][:basic].merge!(base_64_encoded_credentials: base_64_encoded_credentials)
49
+ set_authorization_header("Basic #{base_64_encoded_credentials}")
50
+ end
51
+
47
52
  def set_bearer_authorization_header(token)
53
+ request.options[:auth].merge!(bearer_token: token)
48
54
  set_authorization_header("Bearer #{token}")
49
55
  end
50
56
  # rubocop:enable Style/AccessorMethodName
@@ -14,8 +14,8 @@ class LHC::Logging < LHC::Interceptor
14
14
  "<#{request.object_id}>",
15
15
  request.method.upcase,
16
16
  "#{request.url} at #{Time.now.iso8601}",
17
- "Params=#{request.params}",
18
- "Headers=#{request.headers}",
17
+ "Params=#{request.scrubbed_params}",
18
+ "Headers=#{request.scrubbed_headers}",
19
19
  request.source ? "\nCalled from #{request.source}" : nil
20
20
  ].compact.join(' ')
21
21
  )
@@ -23,8 +23,8 @@ class LHC::Rollbar < LHC::Interceptor
23
23
  request: {
24
24
  url: request.url,
25
25
  method: request.method,
26
- headers: request.headers,
27
- params: request.params
26
+ headers: request.scrubbed_headers,
27
+ params: request.scrubbed_params
28
28
  }
29
29
  }.merge additional_params
30
30
  begin
data/lib/lhc/request.rb CHANGED
@@ -12,7 +12,13 @@ class LHC::Request
12
12
 
13
13
  TYPHOEUS_OPTIONS ||= [:params, :method, :body, :headers, :follow_location, :params_encoding]
14
14
 
15
- attr_accessor :response, :options, :raw, :format, :error_handler, :errors_ignored, :source
15
+ attr_accessor :response,
16
+ :options,
17
+ :raw,
18
+ :format,
19
+ :error_handler,
20
+ :errors_ignored,
21
+ :source
16
22
 
17
23
  def initialize(options, self_executing = true)
18
24
  self.errors_ignored = (options.fetch(:ignore, []) || []).to_a.compact
@@ -56,6 +62,23 @@ class LHC::Request
56
62
  raw.run
57
63
  end
58
64
 
65
+ def scrubbed_params
66
+ LHC::ParamsScrubber.new(params.deep_dup).scrubbed
67
+ end
68
+
69
+ def scrubbed_headers
70
+ LHC::HeadersScrubber.new(headers.deep_dup, options[:auth]).scrubbed
71
+ end
72
+
73
+ def scrubbed_options
74
+ scrubbed_options = options.deep_dup
75
+ scrubbed_options[:params] = LHC::ParamsScrubber.new(scrubbed_options[:params]).scrubbed
76
+ scrubbed_options[:headers] = LHC::HeadersScrubber.new(scrubbed_options[:headers], scrubbed_options[:auth]).scrubbed
77
+ scrubbed_options[:auth] = LHC::AuthScrubber.new(scrubbed_options[:auth]).scrubbed
78
+ scrubbed_options[:body] = LHC::BodyScrubber.new(scrubbed_options[:body]).scrubbed
79
+ scrubbed_options
80
+ end
81
+
59
82
  private
60
83
 
61
84
  attr_accessor :interceptors
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LHC::Scrubber
4
+ attr_accessor :scrubbed
5
+
6
+ SCRUB_DISPLAY = '[FILTERED]'
7
+
8
+ def initialize(data)
9
+ @scrubbed = data
10
+ end
11
+
12
+ private
13
+
14
+ def scrub_auth_elements
15
+ LHC.config.scrubs.dig(:auth)
16
+ end
17
+
18
+ def scrub!
19
+ return if scrub_elements.blank?
20
+ return if scrubbed.blank?
21
+
22
+ LHC::Scrubber.scrub_hash!(scrub_elements, scrubbed) if scrubbed.is_a?(Hash)
23
+ LHC::Scrubber.scrub_array!(scrub_elements, scrubbed) if scrubbed.is_a?(Array)
24
+ end
25
+
26
+ def self.scrub_array!(scrub_elements, scrubbed)
27
+ scrubbed.each do |scrubbed_hash|
28
+ LHC::Scrubber.scrub_hash!(scrub_elements, scrubbed_hash)
29
+ end
30
+ end
31
+
32
+ def self.scrub_hash!(scrub_elements, scrubbed)
33
+ scrub_elements.each do |scrub_element|
34
+ if scrubbed.key?(scrub_element.to_s)
35
+ key = scrub_element.to_s
36
+ elsif scrubbed.key?(scrub_element.to_sym)
37
+ key = scrub_element.to_sym
38
+ end
39
+ next if key.blank? || scrubbed[key].blank?
40
+
41
+ scrubbed[key] = SCRUB_DISPLAY
42
+ end
43
+ scrubbed.values.each { |v| LHC::Scrubber.scrub_hash!(scrub_elements, v) if v.instance_of?(Hash) }
44
+ end
45
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LHC::AuthScrubber < LHC::Scrubber
4
+ def initialize(data)
5
+ super(data)
6
+ scrub_auth_options!
7
+ end
8
+
9
+ private
10
+
11
+ def scrub_auth_options!
12
+ return if scrubbed.blank?
13
+ return if scrub_auth_elements.blank?
14
+
15
+ scrub_basic_auth_options! if scrub_auth_elements.include?(:basic)
16
+ scrub_bearer_auth_options! if scrub_auth_elements.include?(:bearer)
17
+ end
18
+
19
+ def scrub_basic_auth_options!
20
+ return if scrubbed[:basic].blank?
21
+
22
+ scrubbed[:basic][:username] = SCRUB_DISPLAY
23
+ scrubbed[:basic][:password] = SCRUB_DISPLAY
24
+ scrubbed[:basic][:base_64_encoded_credentials] = SCRUB_DISPLAY
25
+ end
26
+
27
+ def scrub_bearer_auth_options!
28
+ return if scrubbed[:bearer].blank?
29
+
30
+ scrubbed[:bearer] = SCRUB_DISPLAY if scrubbed[:bearer].is_a?(String)
31
+ scrubbed[:bearer_token] = SCRUB_DISPLAY
32
+ end
33
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LHC::BodyScrubber < LHC::Scrubber
4
+ def initialize(data)
5
+ super(data)
6
+ parse!
7
+ scrub!
8
+ end
9
+
10
+ private
11
+
12
+ def scrub_elements
13
+ LHC.config.scrubs[:body]
14
+ end
15
+
16
+ def parse!
17
+ return if scrubbed.nil? || scrubbed.is_a?(Hash) || scrubbed.is_a?(Array)
18
+
19
+ if scrubbed.is_a?(String)
20
+ json = scrubbed
21
+ else
22
+ json = scrubbed.to_json
23
+ end
24
+
25
+ parsed = JSON.parse(json)
26
+ self.scrubbed = parsed if parsed.is_a?(Hash) || parsed.is_a?(Array)
27
+ end
28
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LHC::HeadersScrubber < LHC::Scrubber
4
+ def initialize(data, auth_options)
5
+ super(data)
6
+ @auth_options = auth_options
7
+ scrub!
8
+ scrub_auth_headers!
9
+ end
10
+
11
+ private
12
+
13
+ attr_reader :auth_options
14
+
15
+ def scrub_elements
16
+ LHC.config.scrubs[:headers]
17
+ end
18
+
19
+ def scrub_auth_headers!
20
+ return if scrub_auth_elements.blank?
21
+ return if auth_options.blank?
22
+
23
+ scrub_basic_authentication_headers! if scrub_auth_elements.include?(:basic)
24
+ scrub_bearer_authentication_headers! if scrub_auth_elements.include?(:bearer)
25
+ end
26
+
27
+ def scrub_basic_authentication_headers!
28
+ return if auth_options[:basic].blank? || scrubbed['Authorization'].blank?
29
+
30
+ scrubbed['Authorization'].gsub!(auth_options[:basic][:base_64_encoded_credentials], SCRUB_DISPLAY)
31
+ end
32
+
33
+ def scrub_bearer_authentication_headers!
34
+ return if auth_options[:bearer].blank? || scrubbed['Authorization'].blank?
35
+
36
+ scrubbed['Authorization'].gsub!(auth_options[:bearer_token], SCRUB_DISPLAY)
37
+ end
38
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LHC::ParamsScrubber < LHC::Scrubber
4
+ def initialize(data)
5
+ super(data)
6
+ scrub!
7
+ end
8
+
9
+ private
10
+
11
+ def scrub_elements
12
+ LHC.config.scrubs[:params]
13
+ end
14
+ end
data/lib/lhc/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LHC
4
- VERSION ||= '14.0.0'
4
+ VERSION ||= '15.0.0'
5
5
  end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHC do
6
+ it 'has a default value for scrubs' do
7
+ expect(LHC.config.scrubs[:auth]).to eq [:bearer, :basic]
8
+ expect(LHC.config.scrubs[:params]).to eq []
9
+ expect(LHC.config.scrubs[:headers]).to eq []
10
+ expect(LHC.config.scrubs[:body]).to eq ['password', 'password_confirmation']
11
+ end
12
+
13
+ describe 'auth' do
14
+ context 'when only bearer auth should get scrubbed' do
15
+ before(:each) do
16
+ LHC.configure do |c|
17
+ c.scrubs[:auth] = [:bearer]
18
+ end
19
+ end
20
+
21
+ it 'has only bearer auth in scrubs' do
22
+ expect(LHC.config.scrubs[:auth]).to eq([:bearer])
23
+ expect(LHC.config.scrubs[:params]).to eq []
24
+ expect(LHC.config.scrubs[:headers]).to eq []
25
+ expect(LHC.config.scrubs[:body]).to eq ['password', 'password_confirmation']
26
+ end
27
+ end
28
+ end
29
+
30
+ context 'params' do
31
+ context 'when additional param "api_key" should be scrubbed' do
32
+ before(:each) do
33
+ LHC.configure do |c|
34
+ c.scrubs[:params] << 'api_key'
35
+ end
36
+ end
37
+
38
+ it 'has "api_key" in scrubs' do
39
+ expect(LHC.config.scrubs[:auth]).to eq [:bearer, :basic]
40
+ expect(LHC.config.scrubs[:params]).to eq ['api_key']
41
+ expect(LHC.config.scrubs[:headers]).to eq []
42
+ expect(LHC.config.scrubs[:body]).to eq ['password', 'password_confirmation']
43
+ end
44
+ end
45
+ end
46
+
47
+ context 'headers' do
48
+ context 'when additional header "private_key" should be scrubbed' do
49
+ before(:each) do
50
+ LHC.configure do |c|
51
+ c.scrubs[:headers] << 'private_key'
52
+ end
53
+ end
54
+
55
+ it 'has "private_key" in scrubs' do
56
+ expect(LHC.config.scrubs[:auth]).to eq [:bearer, :basic]
57
+ expect(LHC.config.scrubs[:params]).to eq []
58
+ expect(LHC.config.scrubs[:headers]).to eq ['private_key']
59
+ expect(LHC.config.scrubs[:body]).to eq ['password', 'password_confirmation']
60
+ end
61
+ end
62
+ end
63
+
64
+ context 'body' do
65
+ context 'when only password should get scrubbed' do
66
+ before(:each) do
67
+ LHC.configure do |c|
68
+ c.scrubs[:body] = ['password']
69
+ end
70
+ end
71
+
72
+ it 'has password in scrubs' do
73
+ expect(LHC.config.scrubs[:auth]).to eq [:bearer, :basic]
74
+ expect(LHC.config.scrubs[:params]).to eq []
75
+ expect(LHC.config.scrubs[:headers]).to eq []
76
+ expect(LHC.config.scrubs[:body]).to eq(['password'])
77
+ end
78
+ end
79
+
80
+ context 'when "user_token" should be scrubbed' do
81
+ before(:each) do
82
+ LHC.configure do |c|
83
+ c.scrubs[:body] << 'user_token'
84
+ end
85
+ end
86
+
87
+ it 'has user_token in scrubs' do
88
+ expect(LHC.config.scrubs[:auth]).to eq [:bearer, :basic]
89
+ expect(LHC.config.scrubs[:params]).to eq []
90
+ expect(LHC.config.scrubs[:headers]).to eq []
91
+ expect(LHC.config.scrubs[:body]).to eq(['password', 'password_confirmation', 'user_token'])
92
+ end
93
+ end
94
+ end
95
+
96
+ context 'when nothing should be scrubbed' do
97
+ before(:each) do
98
+ LHC.configure do |c|
99
+ c.scrubs = {}
100
+ end
101
+ end
102
+
103
+ it 'does not have scrubs' do
104
+ expect(LHC.config.scrubs.blank?).to be true
105
+ expect(LHC.config.scrubs[:auth]).to be nil
106
+ end
107
+ end
108
+ end
@@ -48,10 +48,10 @@ describe LHC::Error do
48
48
  double('LHC::Request',
49
49
  method: 'GET',
50
50
  url: 'http://example.com/sessions',
51
- headers: { 'Bearer Token' => "aaaaaaaa-bbbb-cccc-dddd-eeee" },
52
- options: { followlocation: true,
53
- auth: { bearer: "aaaaaaaa-bbbb-cccc-dddd-eeee" },
54
- params: { limit: 20 }, url: "http://example.com/sessions" })
51
+ scrubbed_headers: { 'Bearer Token' => LHC::Scrubber::SCRUB_DISPLAY },
52
+ scrubbed_options: { followlocation: true,
53
+ auth: { bearer: LHC::Scrubber::SCRUB_DISPLAY },
54
+ params: { limit: 20 }, url: "http://example.com/sessions" })
55
55
  end
56
56
 
57
57
  let(:response) do
@@ -72,8 +72,8 @@ describe LHC::Error do
72
72
  it 'produces correct debug output' do
73
73
  expect(subject.to_s.split("\n")).to eq(<<-MSG.strip_heredoc.split("\n"))
74
74
  GET http://example.com/sessions
75
- Options: {:followlocation=>true, :auth=>{:bearer=>"aaaaaaaa-bbbb-cccc-dddd-eeee"}, :params=>{:limit=>20}, :url=>"http://example.com/sessions"}
76
- Headers: {"Bearer Token"=>"aaaaaaaa-bbbb-cccc-dddd-eeee"}
75
+ Options: {:followlocation=>true, :auth=>{:bearer=>"#{LHC::Scrubber::SCRUB_DISPLAY}"}, :params=>{:limit=>20}, :url=>"http://example.com/sessions"}
76
+ Headers: {"Bearer Token"=>"#{LHC::Scrubber::SCRUB_DISPLAY}"}
77
77
  Response Code: 500 (internal_error)
78
78
  Response Options: {:return_code=>:internal_error, :response_headers=>""}
79
79
  {"status":500,"message":"undefined"}
@@ -8,7 +8,7 @@ describe LHC::Logging do
8
8
  before(:each) do
9
9
  LHC.config.interceptors = [LHC::Logging]
10
10
  LHC::Logging.logger = logger
11
- stub_request(:get, 'http://local.ch').to_return(status: 200)
11
+ stub_request(:get, /http:\/\/local.ch.*/).to_return(status: 200)
12
12
  end
13
13
 
14
14
  it 'does log information before and after every request made with LHC' do
@@ -34,4 +34,24 @@ describe LHC::Logging do
34
34
  )
35
35
  end
36
36
  end
37
+
38
+ context 'sensitive data' do
39
+ before :each do
40
+ LHC.config.scrubs[:params] << 'api_key'
41
+ LHC.config.scrubs[:headers] << 'private_key'
42
+ LHC.get('http://local.ch', params: { api_key: '123-abc' }, headers: { private_key: 'abc-123' })
43
+ end
44
+
45
+ it 'does not log sensitive params information' do
46
+ expect(logger).to have_received(:info).once.with(
47
+ a_string_including("Params={:api_key=>\"#{LHC::Scrubber::SCRUB_DISPLAY}\"}")
48
+ )
49
+ end
50
+
51
+ it 'does not log sensitive header information' do
52
+ expect(logger).to have_received(:info).once.with(
53
+ a_string_including(":private_key=>\"#{LHC::Scrubber::SCRUB_DISPLAY}\"")
54
+ )
55
+ end
56
+ end
37
57
  end
@@ -36,22 +36,34 @@ describe LHC::Rollbar do
36
36
  )
37
37
  end
38
38
 
39
- context 'additional params' do
40
- it 'does report errors to rollbar with additional data' do
41
- stub_request(:get, 'http://local.ch')
42
- .to_return(status: 400)
43
- expect(-> { LHC.get('http://local.ch', rollbar: { additional: 'data' }) })
44
- .to raise_error LHC::BadRequest
45
- expect(::Rollbar).to have_received(:warning)
46
- .with(
47
- 'Status: 400 URL: http://local.ch',
48
- hash_including(
49
- response: anything,
50
- request: anything,
51
- additional: 'data'
52
- )
39
+ it 'does report errors to rollbar with additional data' do
40
+ stub_request(:get, 'http://local.ch')
41
+ .to_return(status: 400)
42
+ expect(-> { LHC.get('http://local.ch', rollbar: { additional: 'data' }) })
43
+ .to raise_error LHC::BadRequest
44
+ expect(::Rollbar).to have_received(:warning)
45
+ .with(
46
+ 'Status: 400 URL: http://local.ch',
47
+ hash_including(
48
+ response: anything,
49
+ request: anything,
50
+ additional: 'data'
53
51
  )
54
- end
52
+ )
53
+ end
54
+
55
+ it 'scrubs sensitive data' do
56
+ LHC.config.scrubs[:params] << 'api_key'
57
+ LHC.config.scrubs[:headers] << 'private_key'
58
+ stub_request(:get, 'http://local.ch?api_key=123-abc').to_return(status: 400)
59
+ expect(-> { LHC.get('http://local.ch', params: { api_key: '123-abc' }, headers: { private_key: 'abc-123' }) })
60
+ .to raise_error LHC::BadRequest
61
+ expect(::Rollbar).to have_received(:warning)
62
+ .with(
63
+ 'Status: 400 URL: http://local.ch',
64
+ response: hash_including(body: anything, code: anything, headers: anything, time: anything, timeout?: anything),
65
+ request: hash_including(url: anything, method: anything, headers: hash_including(private_key: LHC::Scrubber::SCRUB_DISPLAY), params: { api_key: LHC::Scrubber::SCRUB_DISPLAY })
66
+ )
55
67
  end
56
68
  end
57
69
  end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHC::Request do
6
+ let(:headers) { { private_key: 'xyz-123' } }
7
+ let(:response) { LHC.get(:local, headers: headers) }
8
+ let(:auth) { {} }
9
+
10
+ before :each do
11
+ LHC.config.endpoint(:local, 'http://local.ch', auth: auth)
12
+ stub_request(:get, 'http://local.ch').with(headers: headers)
13
+ end
14
+
15
+ it 'scrubs "private_key"' do
16
+ LHC.config.scrubs[:headers] << 'private_key'
17
+ expect(response.request.scrubbed_headers).to include(private_key: LHC::Scrubber::SCRUB_DISPLAY)
18
+ end
19
+
20
+ it 'does not add a new attribute when a non existing header should be scrubbed' do
21
+ LHC.config.scrubs[:headers] << 'anything'
22
+ expect(response.request.scrubbed_headers).not_to include('anything' => LHC::Scrubber::SCRUB_DISPLAY)
23
+ end
24
+
25
+ context 'when strings instead of symbols are provided' do
26
+ let(:headers) { { 'private_key' => 'xyz-123' } }
27
+
28
+ it 'scrubs "private_key"' do
29
+ LHC.config.scrubs[:headers] << 'private_key'
30
+ expect(response.request.scrubbed_headers).to include('private_key' => LHC::Scrubber::SCRUB_DISPLAY)
31
+ end
32
+ end
33
+
34
+ context 'other authentication strategy' do
35
+ let(:api_key) { '123456' }
36
+ let(:authorization_header) { { 'Authorization' => "Apikey #{api_key}" } }
37
+ let(:headers) { authorization_header }
38
+
39
+ it 'provides srubbed Authorization header' do
40
+ LHC.config.scrubs[:headers] << 'Authorization'
41
+ expect(response.request.scrubbed_headers).to include('Authorization' => LHC::Scrubber::SCRUB_DISPLAY)
42
+ expect(response.request.headers).to include(authorization_header)
43
+ end
44
+ end
45
+
46
+ describe 'auth' do
47
+ before :each do
48
+ LHC.config.interceptors = [LHC::Auth]
49
+ stub_request(:get, 'http://local.ch').with(headers: authorization_header)
50
+ end
51
+
52
+ let(:request) do
53
+ response = LHC.get(:local)
54
+ response.request
55
+ end
56
+
57
+ context 'bearer authentication' do
58
+ let(:bearer_token) { '123456' }
59
+ let(:authorization_header) { { 'Authorization' => "Bearer #{bearer_token}" } }
60
+ let(:auth) { { bearer: -> { bearer_token } } }
61
+
62
+ it 'provides srubbed request headers' do
63
+ expect(request.scrubbed_headers).to include('Authorization' => "Bearer #{LHC::Scrubber::SCRUB_DISPLAY}")
64
+ expect(request.headers).to include(authorization_header)
65
+ end
66
+
67
+ context 'when nothing should get scrubbed' do
68
+ before :each do
69
+ LHC.config.scrubs = {}
70
+ end
71
+
72
+ it 'does not filter beaerer auth' do
73
+ expect(request.scrubbed_headers).to include(authorization_header)
74
+ end
75
+ end
76
+ end
77
+
78
+ context 'basic authentication' do
79
+ let(:username) { 'steve' }
80
+ let(:password) { 'abcdefg' }
81
+ let(:credentials_base_64_codiert) { Base64.strict_encode64("#{username}:#{password}").chomp }
82
+ let(:authorization_header) { { 'Authorization' => "Basic #{credentials_base_64_codiert}" } }
83
+ let(:auth) { { basic: { username: username, password: password } } }
84
+
85
+ it 'provides srubbed request headers' do
86
+ expect(request.scrubbed_headers).to include('Authorization' => "Basic #{LHC::Scrubber::SCRUB_DISPLAY}")
87
+ expect(request.headers).to include(authorization_header)
88
+ end
89
+
90
+ context 'when nothing should get scrubbed' do
91
+ before :each do
92
+ LHC.config.scrubs = {}
93
+ end
94
+
95
+ it 'does not filter basic auth' do
96
+ expect(request.scrubbed_headers).to include(authorization_header)
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHC::Request do
6
+ before :each do
7
+ LHC.config.interceptors = [LHC::Auth]
8
+ LHC.config.endpoint(:local, 'http://local.ch', auth: auth)
9
+ LHC.config.scrubs[:params] << 'api_key'
10
+ LHC.config.scrubs[:headers] << 'private_key'
11
+ LHC.config.scrubs[:body] << 'user_token'
12
+ stub_request(:post, "http://local.ch?#{params.to_query}").with(headers: authorization_header.merge(headers), body: body.to_json)
13
+ end
14
+
15
+ let(:bearer_token) { '123456' }
16
+ let(:authorization_header) { { 'Authorization' => "Bearer #{bearer_token}" } }
17
+ let(:auth) { { bearer: -> { bearer_token } } }
18
+ let(:params) { { api_key: 'api-key-params' } }
19
+ let(:headers) { { private_key: 'private-key-header' } }
20
+ let(:body) { { user_token: 'user-token-body' } }
21
+
22
+ let(:request) do
23
+ response = LHC.post(:local, params: params, headers: headers, body: body)
24
+ response.request
25
+ end
26
+
27
+ it 'provides srubbed request options' do
28
+ expect(request.scrubbed_options[:params]).to include(api_key: LHC::Scrubber::SCRUB_DISPLAY)
29
+ expect(request.scrubbed_options[:headers]).to include(private_key: LHC::Scrubber::SCRUB_DISPLAY)
30
+ expect(request.scrubbed_options[:body]).to include(user_token: LHC::Scrubber::SCRUB_DISPLAY)
31
+ expect(request.scrubbed_options[:auth][:bearer_token]).to eq(LHC::Scrubber::SCRUB_DISPLAY)
32
+ expect(request.scrubbed_options[:auth][:basic]).to be nil
33
+ end
34
+
35
+ context 'when bearer auth is not a proc' do
36
+ let(:auth) { { bearer: bearer_token } }
37
+
38
+ it 'also scrubbes the bearer' do
39
+ expect(request.scrubbed_options[:auth][:bearer]).to eq(LHC::Scrubber::SCRUB_DISPLAY)
40
+ expect(request.scrubbed_options[:auth][:bearer_token]).to eq(LHC::Scrubber::SCRUB_DISPLAY)
41
+ end
42
+ end
43
+
44
+ context 'when options do not have auth' do
45
+ let(:authorization_header) { {} }
46
+ let(:auth) { nil }
47
+
48
+ it 'provides srubbed request options' do
49
+ expect(request.scrubbed_options[:params]).to include(api_key: LHC::Scrubber::SCRUB_DISPLAY)
50
+ expect(request.scrubbed_options[:headers]).to include(private_key: LHC::Scrubber::SCRUB_DISPLAY)
51
+ expect(request.scrubbed_options[:body]).to include(user_token: LHC::Scrubber::SCRUB_DISPLAY)
52
+ expect(request.scrubbed_options[:auth]).to be nil
53
+ end
54
+ end
55
+
56
+ context 'when body data is nested' do
57
+ let(:body) do
58
+ {
59
+ data: {
60
+ attributes: {
61
+ employee: {
62
+ name: 'Muster',
63
+ surname: 'Hans',
64
+ password: 'test-1234',
65
+ password_confirmation: 'test-1234'
66
+ }
67
+ }
68
+ }
69
+ }
70
+ end
71
+
72
+ it 'srubbes nested attributes' do
73
+ expect(request.scrubbed_options[:params]).to include(api_key: LHC::Scrubber::SCRUB_DISPLAY)
74
+ expect(request.scrubbed_options[:headers]).to include(private_key: LHC::Scrubber::SCRUB_DISPLAY)
75
+ expect(request.scrubbed_options[:body][:data][:attributes][:employee]).to include(password: LHC::Scrubber::SCRUB_DISPLAY)
76
+ expect(request.scrubbed_options[:body][:data][:attributes][:employee]).to include(password_confirmation: LHC::Scrubber::SCRUB_DISPLAY)
77
+ expect(request.scrubbed_options[:auth][:bearer_token]).to eq(LHC::Scrubber::SCRUB_DISPLAY)
78
+ expect(request.scrubbed_options[:auth][:basic]).to be nil
79
+ end
80
+ end
81
+
82
+ context 'basic authentication' do
83
+ let(:username) { 'steve' }
84
+ let(:password) { 'abcdefg' }
85
+ let(:credentials_base_64_codiert) { Base64.strict_encode64("#{username}:#{password}").chomp }
86
+ let(:authorization_header) { { 'Authorization' => "Basic #{credentials_base_64_codiert}" } }
87
+ let(:auth) { { basic: { username: username, password: password } } }
88
+
89
+ it 'provides srubbed request headers' do
90
+ expect(request.scrubbed_options[:auth][:basic][:username]).to eq(LHC::Scrubber::SCRUB_DISPLAY)
91
+ expect(request.scrubbed_options[:auth][:basic][:password]).to eq(LHC::Scrubber::SCRUB_DISPLAY)
92
+ expect(request.scrubbed_options[:auth][:basic][:base_64_encoded_credentials]).to eq(LHC::Scrubber::SCRUB_DISPLAY)
93
+ expect(request.scrubbed_options[:auth][:bearer]).to be nil
94
+ end
95
+ end
96
+
97
+ context 'when nothing should get scrubbed' do
98
+ before :each do
99
+ LHC.config.scrubs = {}
100
+ end
101
+
102
+ it 'does not filter anything' do
103
+ expect(request.scrubbed_options[:params]).not_to include(api_key: LHC::Scrubber::SCRUB_DISPLAY)
104
+ expect(request.scrubbed_options[:headers]).not_to include(private_key: LHC::Scrubber::SCRUB_DISPLAY)
105
+ expect(request.scrubbed_options[:body]).not_to include(user_token: LHC::Scrubber::SCRUB_DISPLAY)
106
+ expect(request.scrubbed_options[:auth][:bearer_token]).not_to eq(LHC::Scrubber::SCRUB_DISPLAY)
107
+ end
108
+ end
109
+
110
+ context 'custom data structures that respond to as_json (like LHS data or record)' do
111
+ before do
112
+ class CustomStructure
113
+
114
+ def initialize(data)
115
+ @data = data
116
+ end
117
+
118
+ def as_json
119
+ @data.as_json
120
+ end
121
+
122
+ def to_json
123
+ as_json.to_json
124
+ end
125
+ end
126
+
127
+ stub_request(:post, 'http://local.ch').with(body: custom_structure.to_json)
128
+ end
129
+
130
+ let(:custom_structure) do
131
+ CustomStructure.new(user_token: '12345')
132
+ end
133
+
134
+ let(:request) do
135
+ response = LHC.post(:local, body: custom_structure)
136
+ response.request
137
+ end
138
+
139
+ it 'provides srubbed request options' do
140
+ expect(request.scrubbed_options[:body]).to include('user_token' => LHC::Scrubber::SCRUB_DISPLAY)
141
+ end
142
+ end
143
+
144
+ context 'encoded data hash' do
145
+ let(:body) { { user_token: 'user-token-body' } }
146
+
147
+ let(:request) do
148
+ response = LHC.post(:local, body: body.to_json)
149
+ response.request
150
+ end
151
+
152
+ before :each do
153
+ stub_request(:post, 'http://local.ch').with(body: body.to_json)
154
+ end
155
+
156
+ it 'provides srubbed request options' do
157
+ expect(request.scrubbed_options[:body]).to include('user_token' => LHC::Scrubber::SCRUB_DISPLAY)
158
+ end
159
+ end
160
+
161
+ context 'array' do
162
+ let(:body) { [{ user_token: 'user-token-body' }] }
163
+
164
+ let(:request) do
165
+ response = LHC.post(:local, body: body)
166
+ response.request
167
+ end
168
+
169
+ before :each do
170
+ stub_request(:post, 'http://local.ch').with(body: body.to_json)
171
+ end
172
+
173
+ it 'provides srubbed request options' do
174
+ expect(request.scrubbed_options[:body]).to eq([user_token: LHC::Scrubber::SCRUB_DISPLAY])
175
+ end
176
+ end
177
+
178
+ context 'encoded array' do
179
+ let(:body) { [{ user_token: 'user-token-body' }] }
180
+
181
+ let(:request) do
182
+ response = LHC.post(:local, body: body.to_json)
183
+ response.request
184
+ end
185
+
186
+ before :each do
187
+ stub_request(:post, 'http://local.ch').with(body: body.to_json)
188
+ end
189
+
190
+ it 'provides srubbed request options' do
191
+ expect(request.scrubbed_options[:body]).to eq(['user_token' => LHC::Scrubber::SCRUB_DISPLAY])
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe LHC::Request do
6
+ let(:params) { { api_key: 'xyz-123', secret_key: '123-xyz' } }
7
+ let(:response) { LHC.get(:local, params: params) }
8
+
9
+ before :each do
10
+ LHC.config.endpoint(:local, 'http://local.ch')
11
+ stub_request(:get, "http://local.ch?#{params.to_query}")
12
+ end
13
+
14
+ it 'scrubs "api_key"' do
15
+ LHC.config.scrubs[:params] << 'api_key'
16
+ expect(response.request.scrubbed_params).to include(api_key: LHC::Scrubber::SCRUB_DISPLAY)
17
+ expect(response.request.scrubbed_params).to include(secret_key: '123-xyz')
18
+ end
19
+
20
+ it 'scrubs "api_key" and "secret_key"' do
21
+ LHC.config.scrubs[:params].push('api_key', 'secret_key')
22
+ expect(response.request.scrubbed_params).to include(api_key: LHC::Scrubber::SCRUB_DISPLAY)
23
+ expect(response.request.scrubbed_params).to include(secret_key: LHC::Scrubber::SCRUB_DISPLAY)
24
+ end
25
+
26
+ context 'when value is empty' do
27
+ let(:params) { { api_key: nil, secret_key: '' } }
28
+
29
+ it 'does not filter the value' do
30
+ LHC.config.scrubs[:params].push('api_key', 'secret_key')
31
+ expect(response.request.scrubbed_params).to include(api_key: nil)
32
+ expect(response.request.scrubbed_params).to include(secret_key: '')
33
+ end
34
+ end
35
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lhc
3
3
  version: !ruby/object:Gem::Version
4
- version: 14.0.0
4
+ version: 15.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - https://github.com/local-ch/lhc/contributors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-11 00:00:00.000000000 Z
11
+ date: 2021-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -270,6 +270,11 @@ files:
270
270
  - lib/lhc/response/data/collection.rb
271
271
  - lib/lhc/response/data/item.rb
272
272
  - lib/lhc/rspec.rb
273
+ - lib/lhc/scrubber.rb
274
+ - lib/lhc/scrubbers/auth_scrubber.rb
275
+ - lib/lhc/scrubbers/body_scrubber.rb
276
+ - lib/lhc/scrubbers/headers_scrubber.rb
277
+ - lib/lhc/scrubbers/params_scrubber.rb
273
278
  - lib/lhc/test/cache_helper.rb
274
279
  - lib/lhc/version.rb
275
280
  - script/ci/build.sh
@@ -281,6 +286,7 @@ files:
281
286
  - spec/basic_methods/request_without_rails_spec.rb
282
287
  - spec/config/endpoints_spec.rb
283
288
  - spec/config/placeholders_spec.rb
289
+ - spec/config/scrubs_spec.rb
284
290
  - spec/core_ext/hash/deep_transform_values_spec.rb
285
291
  - spec/dummy/README.rdoc
286
292
  - spec/dummy/Rakefile
@@ -382,6 +388,9 @@ files:
382
388
  - spec/request/parallel_requests_spec.rb
383
389
  - spec/request/params_encoding_spec.rb
384
390
  - spec/request/request_without_rails_spec.rb
391
+ - spec/request/scrubbed_headers_spec.rb
392
+ - spec/request/scrubbed_options_spec.rb
393
+ - spec/request/scrubbed_params_spec.rb
385
394
  - spec/request/url_patterns_spec.rb
386
395
  - spec/request/user_agent_spec.rb
387
396
  - spec/request/user_agent_without_rails_spec.rb
@@ -436,6 +445,7 @@ test_files:
436
445
  - spec/basic_methods/request_without_rails_spec.rb
437
446
  - spec/config/endpoints_spec.rb
438
447
  - spec/config/placeholders_spec.rb
448
+ - spec/config/scrubs_spec.rb
439
449
  - spec/core_ext/hash/deep_transform_values_spec.rb
440
450
  - spec/dummy/README.rdoc
441
451
  - spec/dummy/Rakefile
@@ -537,6 +547,9 @@ test_files:
537
547
  - spec/request/parallel_requests_spec.rb
538
548
  - spec/request/params_encoding_spec.rb
539
549
  - spec/request/request_without_rails_spec.rb
550
+ - spec/request/scrubbed_headers_spec.rb
551
+ - spec/request/scrubbed_options_spec.rb
552
+ - spec/request/scrubbed_params_spec.rb
540
553
  - spec/request/url_patterns_spec.rb
541
554
  - spec/request/user_agent_spec.rb
542
555
  - spec/request/user_agent_without_rails_spec.rb