tshield 0.8.0.0 → 0.9.0.0

Sign up to get free protection for your applications and to get access to all the features.
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