tshield 0.8.0.0 → 0.9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +3 -2
  3. data/README.md +147 -4
  4. data/Rakefile +13 -2
  5. data/bin/tshield +5 -5
  6. data/config/tshield.yml +9 -0
  7. data/lib/tshield/after_filter.rb +3 -2
  8. data/lib/tshield/before_filter.rb +3 -2
  9. data/lib/tshield/configuration.rb +57 -36
  10. data/lib/tshield/controller.rb +22 -10
  11. data/lib/tshield/controllers/requests.rb +20 -21
  12. data/lib/tshield/controllers/sessions.rb +2 -3
  13. data/lib/tshield/counter.rb +5 -5
  14. data/lib/tshield/logger.rb +10 -0
  15. data/lib/tshield/options.rb +61 -27
  16. data/lib/tshield/request.rb +25 -28
  17. data/lib/tshield/response.rb +2 -0
  18. data/lib/tshield/server.rb +24 -19
  19. data/lib/tshield/sessions.rb +6 -4
  20. data/lib/tshield/simple_tcp_server.rb +3 -2
  21. data/lib/tshield/version.rb +4 -2
  22. data/lib/tshield.rb +3 -2
  23. data/spec/spec_helper.rb +6 -6
  24. data/spec/tshield/after_filter_spec.rb +7 -0
  25. data/spec/tshield/configuration_spec.rb +57 -20
  26. data/spec/tshield/fixtures/config/tshield.yml +7 -1
  27. data/spec/tshield/fixtures/filters/example_filter.rb +9 -0
  28. data/spec/tshield/request_spec.rb +43 -2
  29. data/tshield.gemspec +28 -22
  30. metadata +139 -67
  31. data/lib/tshield/assets/favicon.ico +0 -0
  32. data/lib/tshield/assets/javascripts/application.js +0 -0
  33. data/lib/tshield/assets/javascripts/bootstrap.min.js +0 -7
  34. data/lib/tshield/assets/javascripts/jquery.min.js +0 -4
  35. data/lib/tshield/assets/stylesheets/application.css +0 -49
  36. data/lib/tshield/assets/stylesheets/bootstrap-theme.min.css +0 -6
  37. data/lib/tshield/assets/stylesheets/bootstrap.min.css +0 -6
  38. data/lib/tshield/controllers/admin/requests.rb +0 -62
  39. data/lib/tshield/controllers/admin/sessions.rb +0 -40
  40. data/lib/tshield/views/admin/requests/index.haml +0 -6
  41. data/lib/tshield/views/admin/requests/show.haml +0 -25
  42. data/lib/tshield/views/admin/sessions/index.haml +0 -6
  43. data/lib/tshield/views/layout/base.haml +0 -15
@@ -1,21 +1,25 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'optparse'
2
4
 
5
+ require 'tshield/logger'
3
6
  require 'tshield/version'
4
7
 
5
8
  module TShield
9
+ # Options for command line
6
10
  class Options
7
-
8
11
  attr_reader :debug
9
12
 
10
13
  def self.init
11
- @@instance = TShield::Options.new
14
+ @instance = TShield::Options.new
12
15
  end
13
16
 
14
17
  def self.instance
15
- @@instance
18
+ @instance || TShield::Options.new
16
19
  end
17
20
 
18
21
  def initialize
22
+ @options = {}
19
23
  parse
20
24
  end
21
25
 
@@ -23,42 +27,72 @@ module TShield
23
27
  check_breakpoint(args)
24
28
  end
25
29
 
30
+ def configuration_file
31
+ @options.fetch(:configuration_file, 'config/tshield.yml')
32
+ end
33
+
26
34
  private
35
+
27
36
  def check_breakpoint(args)
28
- check_breakpoint_moment(args)
37
+ check_breakpoint_moment(args)
29
38
  end
30
39
 
31
40
  def check_breakpoint_moment(args)
32
41
  @options["#{args[:moment]}_pattern".to_sym] =~ args[:path]
33
42
  end
34
43
 
44
+ def register_before_pattern(opts)
45
+ opts.on('-b', '--break-before-request [PATTERN]',
46
+ 'Breakpoint before request') do |pattern|
47
+ @options[:before_pattern] = Regexp.new(pattern)
48
+ end
49
+ end
50
+
51
+ def register_after_pattern(opts)
52
+ opts.on('-a', '--break-after-request [PATTERN]',
53
+ 'Breakpoint after request') do |pattern|
54
+ @options[:after_pattern] = Regexp.new(pattern)
55
+ end
56
+ end
57
+
58
+ def register_configuration(opts)
59
+ opts.on('-c', '--configuration [FILE]',
60
+ 'Configuration File') do |file|
61
+ @options[:configuration_file] = file
62
+ end
63
+ end
64
+
65
+ def register_patterns(opts)
66
+ register_before_pattern(opts)
67
+ register_after_pattern(opts)
68
+ end
69
+
70
+ def register_version(opts)
71
+ opts.on('-v', '--version', 'Show version') do
72
+ TShield.logger.info(TShield::Version.to_s)
73
+ exit
74
+ end
75
+ end
76
+
77
+ def register_help(opts)
78
+ opts.on_tail('-h', '--help', 'Show this message') do
79
+ puts opts
80
+ exit
81
+ end
82
+ end
83
+
35
84
  def parse
36
- @options = {}
37
85
  OptionParser.new do |opts|
38
- opts.banner = "Usage: tshield [options]"
39
-
40
- opts.on('-b', '--break-before-request [PATTERN]',
41
- 'Breakpoint before request') do |pattern|
42
- @options[:before_pattern] = Regexp.new(pattern)
43
- end
44
-
45
- opts.on('-a', '--break-after-request [PATTERN]',
46
- 'Breakpoint after request') do |pattern|
47
- @options[:after_pattern] = Regexp.new(pattern)
48
- end
49
-
50
- opts.on("-v", "--version", "Show version") do
51
- puts TShield::Version
52
- exit
53
- end
54
-
55
- opts.on_tail("-h", "--help", "Show this message") do
56
- puts opts
57
- exit
58
- end
86
+ opts.banner = 'Usage: tshield [options]'
87
+ register(opts)
59
88
  end.parse!
60
89
  end
61
90
 
91
+ def register(opts)
92
+ register_configuration(opts)
93
+ register_patterns(opts)
94
+ register_version(opts)
95
+ register_help(opts)
96
+ end
62
97
  end
63
98
  end
64
-
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'httparty'
2
4
  require 'json'
3
5
  require 'byebug'
@@ -10,29 +12,27 @@ require 'tshield/response'
10
12
  require 'tshield/sessions'
11
13
 
12
14
  module TShield
13
-
14
15
  class Request
15
-
16
16
  attr_reader :response
17
17
 
18
18
  def initialize(path, options = {})
19
19
  @path = path
20
- @options = options
20
+ @options = options
21
21
  @configuration = TShield::Configuration.singleton
22
- @options[:timeout] = @configuration.request['timeout']
23
- @options[:verify] = @configuration.request['verify_ssl']
22
+ @options[:timeout] = @configuration.request['timeout']
23
+ @options[:verify] = @configuration.request['verify_ssl']
24
24
  request
25
25
  end
26
26
 
27
27
  def request
28
- if not (@options[:raw_query].nil? or @options[:raw_query].empty?)
28
+ unless @options[:raw_query].nil? || @options[:raw_query].empty?
29
29
  @path = "#{@path}?#{@options[:raw_query]}"
30
30
  end
31
31
 
32
32
  @url = "#{domain}#{@path}"
33
33
 
34
34
  if exists
35
- @response = get_current_response
35
+ @response = get_current_response
36
36
  @response.original = false
37
37
  else
38
38
  @method = method
@@ -40,7 +40,7 @@ module TShield
40
40
  @method, @url, @options = filter.new.filter(@method, @url, @options)
41
41
  end
42
42
 
43
- raw = HTTParty.send("#{@method}", @url, @options)
43
+ raw = HTTParty.send(@method.to_s, @url, @options)
44
44
 
45
45
  @configuration.get_after_filters(domain).each do |filter|
46
46
  raw = filter.new.filter(raw)
@@ -56,6 +56,7 @@ module TShield
56
56
  end
57
57
 
58
58
  private
59
+
59
60
  def domain
60
61
  @domain ||= @configuration.get_domain_for(@path)
61
62
  end
@@ -70,14 +71,12 @@ module TShield
70
71
 
71
72
  def save(raw_response)
72
73
  headers = {}
73
- raw_response.headers.each do |k,v|
74
- if !@configuration.not_save_headers(domain).include? k
75
- headers[k] = v
76
- end
74
+ raw_response.headers.each do |k, v|
75
+ headers[k] = v unless @configuration.not_save_headers(domain).include? k
77
76
  end
78
77
 
79
78
  content = {
80
- body: raw_response.body,
79
+ body: raw_response.body,
81
80
  status: raw_response.code,
82
81
  headers: headers
83
82
  }
@@ -102,7 +101,7 @@ module TShield
102
101
  def file_exists
103
102
  session = current_session
104
103
  @content_idx = session ? session[:counter].current(@path, method) : 0
105
- File.exists?(destiny)
104
+ File.exist?(destiny)
106
105
  end
107
106
 
108
107
  def exists
@@ -110,7 +109,7 @@ module TShield
110
109
  end
111
110
 
112
111
  def get_current_response
113
- TShield::Response.new(content['body'], content['headers'] || [], content['status'] || 200)
112
+ TShield::Response.new(content['body'], content['headers'] || [], content['status'] || 200)
114
113
  end
115
114
 
116
115
  def key
@@ -119,22 +118,23 @@ module TShield
119
118
 
120
119
  def destiny(iscontent = false)
121
120
  request_path = File.join('requests')
122
- Dir.mkdir(request_path) unless File.exists?(request_path)
121
+ Dir.mkdir(request_path) unless File.exist?(request_path)
123
122
 
124
- if session = current_session
123
+ session = current_session
124
+ if session
125
125
  request_path = File.join(request_path, session[:name])
126
- Dir.mkdir(request_path) unless File.exists?(request_path)
126
+ Dir.mkdir(request_path) unless File.exist?(request_path)
127
127
  end
128
128
 
129
129
  name_path = File.join(request_path, name)
130
- Dir.mkdir(name_path) unless File.exists?(name_path)
130
+ Dir.mkdir(name_path) unless File.exist?(name_path)
131
131
 
132
132
  path_path = File.join(name_path, safe_dir(@path))
133
- Dir.mkdir(path_path) unless File.exists?(path_path)
133
+ Dir.mkdir(path_path) unless File.exist?(path_path)
134
134
 
135
135
  method_path = File.join(path_path, method)
136
- Dir.mkdir(method_path) unless File.exists?(method_path)
137
-
136
+ Dir.mkdir(method_path) unless File.exist?(method_path)
137
+
138
138
  destiny_name = iscontent ? "#{@content_idx}.content" : "#{@content_idx}.json"
139
139
  File.join(method_path, destiny_name)
140
140
  end
@@ -156,14 +156,11 @@ module TShield
156
156
  def safe_dir(url)
157
157
  if url.size > 225
158
158
  path = url.gsub(/(\?.*)/, '')
159
- params = Digest::SHA1.hexdigest $1
160
- "#{path.gsub(/\//, '-').gsub(/^-/, '')}?#{params}"
159
+ params = Digest::SHA1.hexdigest Regexp.last_match(1)
160
+ "#{path.gsub(%r{/}, '-').gsub(/^-/, '')}?#{params}"
161
161
  else
162
- url.gsub(/\//, '-').gsub(/^-/, '')
162
+ url.gsub(%r{/}, '-').gsub(/^-/, '')
163
163
  end
164
164
  end
165
-
166
165
  end
167
-
168
166
  end
169
-
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module TShield
2
4
  class Response
3
5
  attr_accessor :body, :headers, :status, :original
@@ -1,22 +1,35 @@
1
+ # frozen_string_literal: false
2
+
1
3
  require 'sinatra'
2
4
  require 'haml'
3
5
 
4
6
  require 'tshield/controllers/requests'
5
7
  require 'tshield/controllers/sessions'
6
8
 
7
- require 'tshield/controllers/admin/requests'
8
- require 'tshield/controllers/admin/sessions'
9
-
10
9
  module TShield
10
+ # Base of TShield Server
11
11
  class Server < Sinatra::Base
12
-
13
12
  include TShield::Controllers::Requests::Helpers
14
- include TShield::Controllers::Admin::Sessions::Helpers
15
- include TShield::Controllers::Admin::Requests::Helpers
16
13
 
17
- if File.exists?('controllers')
14
+ set :protection, except: [:json_csrf]
15
+ set :public_dir, File.join(File.dirname(__FILE__), 'assets')
16
+ set :views, File.join(File.dirname(__FILE__), 'views')
17
+ set :bind, '0.0.0.0'
18
+
19
+ def self.register_resources
20
+ load_controllers
21
+ register TShield::Controllers::Sessions
22
+ register TShield::Controllers::Requests
23
+ end
24
+
25
+ def self.load_controllers
26
+ return unless File.exist?('controllers')
27
+
18
28
  Dir.entries('controllers').each do |entry|
29
+ require 'byebug'
30
+ debugger
19
31
  next if entry =~ /^\.\.?$/
32
+
20
33
  entry.gsub!('.rb', '')
21
34
  require File.join('.', 'controllers', entry)
22
35
  controller_name = entry.split('_').collect(&:capitalize).join
@@ -25,17 +38,9 @@ module TShield
25
38
  end
26
39
  end
27
40
 
28
- set :protection, :except => [:json_csrf]
29
- set :public_dir, File.join(File.dirname(__FILE__), 'assets')
30
- set :views, File.join(File.dirname(__FILE__), 'views')
31
- set :bind, '0.0.0.0'
32
-
33
- register TShield::Controllers::Admin::Sessions
34
- register TShield::Controllers::Admin::Requests
35
-
36
- register TShield::Controllers::Sessions
37
- register TShield::Controllers::Requests
38
-
41
+ def self.run!
42
+ register_resources
43
+ super.run!
44
+ end
39
45
  end
40
46
  end
41
-
@@ -1,10 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'byebug'
2
4
  require 'tshield/counter'
3
5
 
4
6
  module TShield
7
+ # Manage sessions
8
+ #
9
+ # Start and stop session for ip
5
10
  module Sessions
6
11
  def self.start(ip, name)
7
- sessions[normalize_ip(ip)] = {name: name, counter: TShield::Counter.new}
12
+ sessions[normalize_ip(ip)] = { name: name, counter: TShield::Counter.new }
8
13
  end
9
14
 
10
15
  def self.stop(ip)
@@ -15,7 +20,6 @@ module TShield
15
20
  sessions[normalize_ip(ip)]
16
21
  end
17
22
 
18
- protected
19
23
  def self.sessions
20
24
  @sessions ||= {}
21
25
  end
@@ -23,7 +27,5 @@ module TShield
23
27
  def self.normalize_ip(ip)
24
28
  ip == '::1' ? '127.0.0.1' : ip
25
29
  end
26
-
27
30
  end
28
31
  end
29
-
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'socket'
2
4
 
3
5
  module TShield
@@ -6,7 +8,7 @@ module TShield
6
8
  @running = true
7
9
  end
8
10
 
9
- def on_connect(client)
11
+ def on_connect(_client)
10
12
  raise 'should implement method on_connect'
11
13
  end
12
14
 
@@ -24,4 +26,3 @@ module TShield
24
26
  end
25
27
  end
26
28
  end
27
-
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module TShield
4
+ # Control version of gem
2
5
  class Version
3
6
  MAJOR = 0
4
- MINOR = 8
7
+ MINOR = 9
5
8
  PATCH = 0
6
9
  PRE = 0
7
10
 
@@ -12,4 +15,3 @@ module TShield
12
15
  end
13
16
  end
14
17
  end
15
-
data/lib/tshield.rb CHANGED
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tshield/options'
1
4
  require 'tshield/simple_tcp_server'
2
5
  require 'tshield/server'
3
- require 'tshield/options'
4
6
 
5
7
  module TShield
6
8
  end
7
-
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: false
2
+
1
3
  require 'bundler/setup'
2
4
  Bundler.setup
3
5
 
@@ -5,14 +7,12 @@ require 'simplecov'
5
7
  SimpleCov.start
6
8
 
7
9
  require 'httparty'
8
- require 'tshield'
9
-
10
10
  require 'webmock/rspec'
11
11
 
12
12
  RSpec.configure do |config|
13
-
14
- config.before(:each) do
13
+ config.before(:each) do
14
+ allow(File).to receive(:join).and_return(
15
+ 'spec/tshield/fixtures/config/tshield.yml'
16
+ )
15
17
  end
16
-
17
18
  end
18
-
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tshield/after_filter'
4
+ require 'spec_helper'
5
+
6
+ describe TShield::AfterFilter do
7
+ end
@@ -1,32 +1,69 @@
1
+ # frozen_string_literal: false
2
+
3
+ require 'tshield/configuration'
1
4
  require 'spec_helper'
2
5
 
3
6
  describe TShield::Configuration do
4
- before :each do
5
- allow(File).to(
6
- receive(:join).and_return('spec/tshield/fixtures/config/tshield.yml'))
7
- @configuration = TShield::Configuration.singleton
8
- end
9
-
10
- describe 'load configurations from yaml' do
11
- it 'recover domains' do
12
- expect(@configuration.domains['example.org']['paths']).to(
13
- include('/api/one', '/api/two'))
7
+ context 'on config exist' do
8
+ before :each do
9
+ options_instance = double
10
+ allow(options_instance).to receive(:configuration_file)
11
+ .and_return('spec/tshield/fixtures/config/tshield.yml')
12
+ allow(TShield::Options).to receive(:instance).and_return(options_instance)
13
+ allow(File).to receive(:join).and_return(
14
+ './spec/tshield/fixtures/filters/example_filter.rb'
15
+ )
16
+ allow(File).to receive(:exist?) do
17
+ true
18
+ end
19
+ allow(Dir).to receive(:entries) do
20
+ ['.', '..', 'example_filter.rb']
21
+ end
22
+ @configuration = TShield::Configuration.singleton
14
23
  end
15
- end
16
24
 
17
- describe 'get_domain_for' do
18
- it 'return domain for example.org' do
19
- expect(@configuration.get_domain_for('/api/two')).to eq('example.org')
20
- end
25
+ context 'load configurations from yaml' do
26
+ it 'recover domains' do
27
+ expect(@configuration.domains['example.org']['paths']).to(
28
+ include('/api/one', '/api/two')
29
+ )
30
+ end
21
31
 
22
- it 'return domain for example.com' do
23
- expect(@configuration.get_domain_for('/api/three')).to eq('example.com')
32
+ context 'on load filters' do
33
+ it 'recover filters for a domain' do
34
+ expect(@configuration.get_filters('example.org')).to eq([ExampleFilter])
35
+ end
36
+ it 'return empty array if domain not have filters' do
37
+ expect(@configuration.get_filters('example.com')).to eq([])
38
+ end
39
+ end
24
40
  end
25
41
 
26
- it 'return nil if domain not found' do
27
- expect(@configuration.get_domain_for('/api/four')).to be_nil
42
+ describe 'get_domain_for' do
43
+ it 'return domain for example.org' do
44
+ expect(@configuration.get_domain_for('/api/two')).to eq('example.org')
45
+ end
46
+
47
+ it 'return domain for example.com' do
48
+ expect(@configuration.get_domain_for('/api/three')).to eq('example.com')
49
+ end
50
+
51
+ it 'return nil if domain not found' do
52
+ expect(@configuration.get_domain_for('/api/four')).to be_nil
53
+ end
28
54
  end
29
55
  end
56
+ context 'on config not exist' do
57
+ before :each do
58
+ options_instance = double
59
+ allow(options_instance).to receive(:configuration_file)
60
+ .and_return('not_found/config/tshield.yml')
61
+ allow(TShield::Options).to receive(:instance).and_return(options_instance)
62
+ TShield::Configuration.clear
63
+ end
30
64
 
65
+ it 'exit with error status' do
66
+ expect { TShield::Configuration.singleton }.to raise_error RuntimeError
67
+ end
68
+ end
31
69
  end
32
-
@@ -1,9 +1,15 @@
1
+ ---
2
+ request:
3
+ timeout: 0
1
4
  domains:
2
5
  'example.org':
6
+ name: 'example.org'
7
+ filters:
8
+ - 'ExampleFilter'
3
9
  paths:
4
10
  - '/api/one'
5
11
  - '/api/two'
6
12
  'example.com':
13
+ name: 'example.com'
7
14
  paths:
8
15
  - '/api/three'
9
-
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tshield/after_filter'
4
+
5
+ class ExampleFilter < TShield::AfterFilter
6
+ def filter(response)
7
+ response
8
+ end
9
+ end
@@ -1,11 +1,52 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
5
+ require 'tshield/request'
6
+
3
7
  describe TShield::Request do
8
+ before :each do
9
+ configuration = double
10
+ allow(TShield::Configuration)
11
+ .to receive(:singleton).and_return(configuration)
12
+ allow(configuration).to receive(:get_before_filters).and_return([])
13
+ allow(configuration).to receive(:get_after_filters).and_return([])
14
+ allow(configuration).to receive(:request).and_return('timeout' => 10)
15
+ allow(configuration).to receive(:get_domain_for).and_return('example.org')
16
+ allow(TShield::Options).to receive_message_chain(:instance, :break?)
17
+ end
4
18
 
5
19
  describe 'when save response' do
6
- it '' do
20
+ it 'should write response body, request status and headers' do
21
+ allow_any_instance_of(TShield::Request).to receive(:exists)
22
+ .and_return(false)
23
+ allow_any_instance_of(TShield::Request).to receive(:destiny)
24
+ allow(HTTParty).to receive(:send).and_return(RawResponse.new)
25
+
26
+ write_spy = double
27
+ allow(File).to receive(:open).and_return(write_spy)
28
+
29
+ expect(write_spy).to receive(:write).ordered.with('this is the body')
30
+ expect(write_spy).to receive(:write)
31
+ .ordered
32
+ .with("{\n \"status\": 200,\n \"headers\": {\n }\n}")
33
+ allow(write_spy).to receive(:close)
34
+
35
+ TShield::Request.new '/', method: 'GET'
7
36
  end
8
37
  end
9
38
 
10
- end
39
+ class RawResponse
40
+ def headers
41
+ []
42
+ end
43
+
44
+ def body
45
+ 'this is the body'
46
+ end
11
47
 
48
+ def code
49
+ 200
50
+ end
51
+ end
52
+ end