stratagem 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/Manifest +16 -6
  2. data/Rakefile +8 -1
  3. data/lib/generators/stratagem/install/install_base.rb +13 -3
  4. data/lib/generators/stratagem/install/install_generator.rb +1 -1
  5. data/lib/stratagem.rb +42 -18
  6. data/lib/stratagem/authentication.rb +2 -5
  7. data/lib/stratagem/auto_mock.rb +1 -0
  8. data/lib/stratagem/auto_mock/aquifer.rb +49 -26
  9. data/lib/stratagem/auto_mock/factory.rb +1 -6
  10. data/lib/stratagem/auto_mock/user_loader.rb +38 -0
  11. data/lib/stratagem/client.rb +15 -4
  12. data/lib/stratagem/configuration/auth_auth.rb +19 -0
  13. data/lib/stratagem/configuration/core.rb +20 -0
  14. data/lib/stratagem/crawler/authentication.rb +17 -12
  15. data/lib/stratagem/crawler/authentication/automated.rb +40 -0
  16. data/lib/stratagem/crawler/authentication/base.rb +140 -0
  17. data/lib/stratagem/crawler/authentication/configured.rb +27 -0
  18. data/lib/stratagem/crawler/parameter_resolver.rb +12 -8
  19. data/lib/stratagem/crawler/route_invoker.rb +10 -13
  20. data/lib/stratagem/crawler/session.rb +14 -2
  21. data/lib/stratagem/crawler/site_model.rb +4 -173
  22. data/lib/stratagem/crawler/site_model/edge.rb +20 -0
  23. data/lib/stratagem/crawler/site_model/page.rb +121 -0
  24. data/lib/stratagem/crawler/site_model/page_set.rb +58 -0
  25. data/lib/stratagem/instrumentation/models.rb +3 -14
  26. data/lib/stratagem/instrumentation/models/annotations.rb +39 -5
  27. data/lib/stratagem/instrumentation/models/authentication.rb +0 -1
  28. data/lib/stratagem/instrumentation/models/authentication/authlogic/detect.rb +1 -0
  29. data/lib/stratagem/instrumentation/models/authentication/devise/detect.rb +1 -1
  30. data/lib/stratagem/instrumentation/models/authentication/devise/instrumentation.rb +0 -4
  31. data/lib/stratagem/instrumentation/models/metadata.rb +23 -1
  32. data/lib/stratagem/instrumentation/models/persistence.rb +3 -4
  33. data/lib/stratagem/instrumentation/models/persistence/active_record/metadata.rb +2 -2
  34. data/lib/stratagem/interface/browser.rb +9 -3
  35. data/lib/stratagem/interface/public/javascripts/stratagem.js +14 -12
  36. data/lib/stratagem/interface/views/index.haml +3 -3
  37. data/lib/stratagem/logger.rb +28 -2
  38. data/lib/stratagem/model.rb +6 -0
  39. data/lib/stratagem/model/application.rb +21 -134
  40. data/lib/stratagem/model/components/base.rb +1 -4
  41. data/lib/stratagem/model/components/controller.rb +1 -2
  42. data/lib/stratagem/model/components/model.rb +15 -15
  43. data/lib/stratagem/model/components/route.rb +3 -2
  44. data/lib/stratagem/model/components/view.rb +0 -1
  45. data/lib/stratagem/model/containers/base.rb +60 -0
  46. data/lib/stratagem/model/containers/gem.rb +25 -0
  47. data/lib/stratagem/model/containers/plugin.rb +11 -0
  48. data/lib/stratagem/model/containers/route.rb +19 -0
  49. data/lib/stratagem/model/parse_util.rb +3 -3
  50. data/lib/stratagem/model_builder.rb +1 -4
  51. data/lib/stratagem/rack_hack.rb +15 -0
  52. data/lib/stratagem/site_crawler.rb +5 -4
  53. data/lib/stratagem/snapshot.rb +5 -7
  54. data/spec/stratagem/configuration_spec.rb +32 -0
  55. data/stratagem.gemspec +5 -8
  56. data/templates/install/environments/stratagem.rb.erb +31 -2
  57. data/templates/install/script/stratagem +16 -0
  58. data/templates/install/tasks/stratagem.rake +2 -2
  59. metadata +36 -65
  60. data/bin/stratagem +0 -58
  61. data/lib/stratagem/scan.rb +0 -19
  62. data/lib/stratagem/scan/checks/email_address.rb +0 -15
  63. data/lib/stratagem/scan/checks/error_pages.rb +0 -25
  64. data/lib/stratagem/scan/result.rb +0 -45
  65. data/lib/stratagem/scanner.rb +0 -32
@@ -1,25 +1,14 @@
1
1
  module Stratagem::Instrumentation::Models; end
2
2
  module Stratagem::Instrumentation::Models::Adapters; end
3
3
 
4
+ require 'stratagem/instrumentation/models/association'
5
+
4
6
  require 'stratagem/instrumentation/models/mocking'
5
7
  require 'stratagem/instrumentation/models/metadata'
6
8
  require 'stratagem/instrumentation/models/tracing'
7
9
  require 'stratagem/instrumentation/models/annotations'
8
10
  require 'stratagem/instrumentation/models/detect'
9
11
 
10
- require 'stratagem/instrumentation/models/persistence'
11
12
  require 'stratagem/instrumentation/models/authentication'
13
+ require 'stratagem/instrumentation/models/persistence'
12
14
  require 'stratagem/instrumentation/models/support_libraries'
13
-
14
- # base = File.join(File.dirname(__FILE__), 'models', 'persistence', 'util')
15
- # Dir.entries(base).select {|s| s =~ /\.rb$/}.each {|helper|
16
- # require File.join(base, helper.gsub(/\.rb/, ''))
17
- # }
18
- #
19
- # base = File.join(File.dirname(__FILE__), 'models', 'persistence')
20
- # Dir.entries(base).select {|s| s !~ /^\./ && s != 'util' }.each {|adapter_dir|
21
- # require File.join(base, adapter_dir, 'detect')
22
- # require File.join(base, adapter_dir, 'tracing')
23
- # require File.join(base, adapter_dir, 'metadata')
24
- # require File.join(base, adapter_dir, 'extensions')
25
- # }
@@ -12,6 +12,36 @@ module Stratagem::Instrumentation::Models
12
12
  def method_missing(method, *args, &block)
13
13
  @object.class.stratagem.send(method, *args, &block)
14
14
  end
15
+
16
+ # objects that are related to this object
17
+ def related_objects(collection_size_limit=5000)
18
+ traverse_objects(collection_size_limit)
19
+ end
20
+
21
+ def traverse_objects(collection_size_limit, collection=[], class_chain=[])
22
+ return if collection.size >= collection_size_limit
23
+
24
+ unless collection.include?(@object)
25
+ collection << @object
26
+ relations(:has_many).each do |relation|
27
+ puts "relation #{relation.name} - #{collection.size} - #{relation.klass}"
28
+ if (relation.klass && !class_chain.include?(relation.klass))
29
+ class_chain << relation.klass
30
+ begin
31
+ related = @object.send(relation.name)
32
+ if (related.kind_of?(Array))
33
+ related.each {|r| r.stratagem.traverse_objects(collection_size_limit, collection, class_chain) }
34
+ elsif (!related.nil?)
35
+ related.stratagem.traverse_objects(collection_size_limit, collection, class_chain)
36
+ end
37
+ rescue
38
+ Stratagem.logger.error($!)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ collection
44
+ end
15
45
  end
16
46
 
17
47
  class Annotations
@@ -43,8 +73,12 @@ module Stratagem::Instrumentation::Models
43
73
  # connect the adapters
44
74
  detect_adapters(model).each {|adapter|
45
75
  if adapter.detector.supports?(model)
46
- puts "#{model.name} supports #{adapter.tracing.name}"
47
- model.send(:include, adapter.tracing)
76
+ begin
77
+ puts "#{model.name} supports #{adapter.tracing.name}"
78
+ model.send(:include, adapter.tracing)
79
+ rescue
80
+ Rails.logger.error($!)
81
+ end
48
82
  end
49
83
  }
50
84
  end
@@ -75,14 +109,14 @@ module Stratagem::Instrumentation::Models
75
109
  end
76
110
 
77
111
  def initialize(model)
78
- puts "initializing stratagem for #{model.name} - #{self.class.name}"
112
+ puts "initializing stratagem for #{model.name}"
79
113
  @model = model
80
114
  self.class.detect_adapters(model).each do |adapter|
81
115
  if adapter.detector.supports?(model)
82
116
  # puts "\t#{model.name} supports #{adapter.detector.name}"
83
117
  instrument_model(adapter)
84
- # else
85
- # puts "#{model.name} does not support #{adapter.detector.name}"
118
+ else
119
+ # puts "#{model.name} does not support #{adapter.detector.name}"
86
120
  end
87
121
  end
88
122
  rescue
@@ -4,4 +4,3 @@ pattern = File.join(File.dirname(__FILE__), 'authentication', '**', '*.rb')
4
4
  Dir.glob(pattern).each {|filename|
5
5
  require filename
6
6
  }
7
-
@@ -2,6 +2,7 @@ module Stratagem::Instrumentation::Models::Authentication::Authlogic
2
2
  class Detect < Stratagem::Instrumentation::Models::Detect
3
3
  def self.supports?(model)
4
4
  begin
5
+ # puts "AUTHLOGIC MODEL #{model.name} - #{model.ancestors.include?(::Authlogic::ActsAsAuthentic::MagicColumns::Methods)}"
5
6
  model.ancestors.include?(::Authlogic::ActsAsAuthentic::MagicColumns::Methods)
6
7
  rescue
7
8
  false
@@ -2,7 +2,7 @@ module Stratagem::Instrumentation::Models::Authentication::Devise
2
2
  class Detect < Stratagem::Instrumentation::Models::Detect
3
3
  def self.supports?(model)
4
4
  begin
5
- model.ancestors.find {|a| a.name.include? 'Devise::Models' } != nil
5
+ model.ancestors.find {|a| a.name && a.name.include?('Devise::Models') } != nil
6
6
  rescue
7
7
  false
8
8
  end
@@ -8,10 +8,6 @@ module Stratagem::Instrumentation::Models::Authentication::Devise
8
8
  record.active = true
9
9
  end
10
10
  }
11
-
12
- after_save {|record|
13
- p record
14
- }
15
11
  end
16
12
  end
17
13
  end
@@ -25,6 +25,10 @@ module Stratagem::Instrumentation::Models
25
25
 
26
26
  # Convenience methods
27
27
 
28
+ def connected_classes
29
+ traverse_graph(model) - [model]
30
+ end
31
+
28
32
  def subclasses?
29
33
  model.sg_subclasses().size > 0
30
34
  end
@@ -61,7 +65,9 @@ module Stratagem::Instrumentation::Models
61
65
  end
62
66
 
63
67
  def callbacks
64
- adapters.select {|a| a.detector.supports?(model) }.map {|a| a.metadata }
68
+ adapters.select {|a|
69
+ a.detector.supports?(model)
70
+ }.map {|a| a.metadata }
65
71
  end
66
72
 
67
73
  def validators
@@ -71,6 +77,7 @@ module Stratagem::Instrumentation::Models
71
77
  private
72
78
 
73
79
  def run_callbacks(method, *args)
80
+ puts "#{self.class.name} model" if (method == :attribute_names)
74
81
  results = callbacks.inject([]) {|memory,callback|
75
82
  begin
76
83
  memory << callback.send(method, *args) if callback.methods_include?(method)
@@ -82,5 +89,20 @@ module Stratagem::Instrumentation::Models
82
89
  results.flatten.compact.uniq
83
90
  end
84
91
 
92
+
93
+ private
94
+
95
+ # see connected_classes
96
+ def traverse_graph(application_class, class_chain=[])
97
+ class_chain << application_class
98
+ application_class.stratagem.relations.each do |relation|
99
+ traverse_graph(relation.from_class, class_chain) unless class_chain.include?(relation.from_class)
100
+ traverse_graph(relation.klass, class_chain) unless class_chain.include?(relation.klass)
101
+ end
102
+ puts class_chain.map {|c| c.name }.join(" -> ")
103
+ class_chain
104
+ end
105
+
106
+
85
107
  end
86
108
  end
@@ -2,8 +2,7 @@ module Stratagem::Instrumentation::Models::Persistence; end
2
2
 
3
3
  base = File.join(File.dirname(__FILE__), 'persistence')
4
4
  Dir.entries(base).select {|s| s !~ /^\./ && s != 'util' }.each {|adapter_dir|
5
- require File.join(base, adapter_dir, 'detect')
6
- require File.join(base, adapter_dir, 'tracing')
7
- require File.join(base, adapter_dir, 'metadata')
8
- require File.join(base, adapter_dir, 'extensions')
5
+ ['detect', 'tracing', 'metadata', 'extensions'].each do |extension|
6
+ require File.join(base, adapter_dir, extension) if File.exists?(File.join(base, adapter_dir, extension+'.rb'))
7
+ end
9
8
  }
@@ -7,7 +7,7 @@ module Stratagem::Instrumentation::Models::Persistence::ActiveRecord
7
7
  begin
8
8
  @instance = @model.new unless (@model == ActiveRecord::Base)
9
9
  rescue
10
- puts "ERROR: #{@model.name} could not be instantiated: #{$!.message}"
10
+ puts "ERROR: #{@model} could not be instantiated: #{$!.message}"
11
11
  end
12
12
  end
13
13
 
@@ -23,7 +23,7 @@ module Stratagem::Instrumentation::Models::Persistence::ActiveRecord
23
23
  begin
24
24
  Stratagem::Instrumentation::Models::Association.new(model, a.name.to_sym, a.association_foreign_key.to_sym, klass, a.macro, a.options)
25
25
  rescue
26
- puts "ERROR: #{$!.message}"
26
+ puts "METADATA ERROR: #{$!.message}"
27
27
  end
28
28
  }.compact
29
29
  end
@@ -18,7 +18,7 @@ get '/' do
18
18
  end
19
19
 
20
20
  get '/credentials' do
21
- Stratagem::Authentication.instance.store_credentials(params[:account], params[:api_key], params[:project])
21
+ Stratagem::Authentication.instance.store_credentials(params[:api_key], params[:project])
22
22
  redirect '/'
23
23
  end
24
24
 
@@ -27,9 +27,15 @@ get '/logs' do
27
27
  logger = Stratagem.logger
28
28
  logs = [logger.pop]
29
29
  logs << logger.pop until logger.empty?
30
- logs.to_json
30
+ logs.map {|log|
31
+ {
32
+ :phase => log.phase,
33
+ :details => log.details,
34
+ :level => log.level
35
+ }
36
+ }.to_json
31
37
  else
32
- ""
38
+ null
33
39
  end
34
40
  end
35
41
 
@@ -2,24 +2,26 @@ $(document).ready(function() {
2
2
  var currentPhase = null,
3
3
  $phases = $("li");
4
4
 
5
+ function changePhase(name) {
6
+ $phases.removeClass("active");
7
+ $("#"+name).addClass("active");
8
+ if (name == "complete") {
9
+ window.setTimeout(function() { window.location = $("#completeUrl").attr("href"); }, 1000);
10
+ }
11
+ };
12
+
5
13
  window.setInterval(function() {
6
14
  $.getJSON('/logs', function(data) {
7
15
  var logs = $("#logs .modeling_application ul");
8
- if (data == null)
9
- window.location = $("#completeUrl").attr("href");
16
+ if (data == null) {
17
+ changePhase("complete");
18
+ }
10
19
 
11
20
  $.each(data, function(idx, val) {
12
- var phase = val[0];
13
- var ts = val[1];
14
-
21
+ var phase = val['phase'];
22
+
15
23
  if (phase != currentPhase) {
16
- $phases.removeClass("active");
17
- $("#"+phase).addClass("active");
18
-
19
- if (phase == "complete")
20
- window.setTimeout(function() {
21
- window.location = $("#completeUrl").attr("href");
22
- }, 1000);
24
+ changePhase(phase);
23
25
  }
24
26
  });
25
27
  });
@@ -1,4 +1,4 @@
1
- - phases = ['modeling application', 'mocking models', 'traversing site', 'vulnerability scanning']
1
+ - phases = ['modeling application', 'mocking models', 'traversing site', 'exporting']
2
2
 
3
3
  %html
4
4
  %head
@@ -26,9 +26,9 @@
26
26
  %li{:id => "#{phases[2].gsub(/\s/, '_')}"}
27
27
  crawling your website
28
28
  %li{:id => "#{phases[3].gsub(/\s/, '_')}"}
29
- scanning model for vulnerabilities
29
+ exporting security snapshot
30
30
  %li#complete
31
- analysis complete
31
+ complete
32
32
  %li{:style => "display:none"}
33
33
  %a{:id => "completeUrl", :href => Stratagem::Authentication.instance.project_url}
34
34
 
@@ -7,9 +7,15 @@ module Stratagem
7
7
 
8
8
  MESSAGE_QUEUE = []
9
9
 
10
- Message = Struct.new(:phase, :timestamp, :details)
10
+ Message = Struct.new(:phase, :timestamp, :details, :level)
11
11
  @@blocker = Blocker.new()
12
12
 
13
+ attr_reader :errors
14
+
15
+ def initialize
16
+ @errors = []
17
+ end
18
+
13
19
  def phase(phase)
14
20
  @phase = phase
15
21
  end
@@ -18,8 +24,17 @@ module Stratagem
18
24
  add(Message.new(@phase, Time.now, message))
19
25
  end
20
26
 
27
+ def error(exception)
28
+ puts exception.message
29
+ puts exception.backtrace
30
+ @errors << create_error(exception)
31
+ end
32
+
21
33
  def fatal(exception)
22
-
34
+ puts exception.message
35
+ puts exception.backtrace
36
+ @errors << create_error(exception)
37
+ # add(Message.new(@phase, Time.now, $!.message))
23
38
  end
24
39
 
25
40
  def pop
@@ -33,6 +48,17 @@ module Stratagem
33
48
 
34
49
  private
35
50
 
51
+ def create_error(exception)
52
+ if (exception.kind_of?(Exception))
53
+ { :message => exception.message, :backtrace => exception.backtrace.slice(0,50) }
54
+ elsif (e.kind_of?(String))
55
+ { :message => exception, :backtrace => [] }
56
+ else
57
+ puts "ERROR: unknown error type #{e.class.name}"
58
+ nil
59
+ end
60
+ end
61
+
36
62
  def add(obj)
37
63
  puts obj.details
38
64
  MESSAGE_QUEUE << obj
@@ -3,6 +3,12 @@ end
3
3
 
4
4
  require 'stratagem/model/application'
5
5
  require 'stratagem/model/parse_util'
6
+
7
+ require 'stratagem/model/containers/base'
8
+ require 'stratagem/model/containers/gem'
9
+ require 'stratagem/model/containers/route'
10
+ require 'stratagem/model/containers/plugin'
11
+
6
12
  require 'stratagem/model/components/base'
7
13
  require 'stratagem/model/components/reference'
8
14
  require 'stratagem/model/components/model'
@@ -16,13 +16,13 @@ module Stratagem::Model
16
16
 
17
17
  def initialize
18
18
  log "initializing application model"
19
- @models = ComponentContainer.new self
20
- @controllers = ComponentContainer.new self
21
- @routes = RouteContainer.new self
22
- @views = ComponentContainer.new self
23
- @static_files = ComponentContainer.new self
24
- @gems = GemContainer.new self
25
- @plugins = PluginContainer.new self
19
+ @models = Stratagem::Model::Containers::Base.new self
20
+ @controllers = Stratagem::Model::Containers::Base.new self
21
+ @routes = Stratagem::Model::Containers::Route.new self
22
+ @views = Stratagem::Model::Containers::Base.new self
23
+ @static_files = Stratagem::Model::Containers::Base.new self
24
+ @gems = Stratagem::Model::Containers::Gem.new self
25
+ @plugins = Stratagem::Model::Containers::Plugin.new self
26
26
  end
27
27
 
28
28
  def log(msg)
@@ -31,7 +31,7 @@ module Stratagem::Model
31
31
 
32
32
  def export
33
33
  puts "exporting site model"
34
- puts "references:"
34
+ puts "\tmapping #{Stratagem::Instrumentation::Models::Tracing.invocations_audit.size} references"
35
35
  references = []
36
36
  begin
37
37
  references = Stratagem::Instrumentation::Models::Tracing.invocations_audit.uniq.map {|ia| ia.to_reference.export }.uniq
@@ -39,28 +39,30 @@ module Stratagem::Model
39
39
  puts $!.message
40
40
  puts $!.backtrace
41
41
  end
42
- puts "exporting hash"
42
+
43
+ puts "\tmapping #{Stratagem.logger.errors.size} errors"
44
+ errors = Stratagem.logger.errors.compact.uniq
43
45
  begin
44
- # references = @controllers.map {|c| c.references }.flatten.map {|r| r.export }.uniq
45
46
  h = {
46
47
  :rails_version => rails_version,
47
48
  :rails_environment => Rails.env,
48
49
  :rails_root => Rails.root.to_s,
49
- :models => @models.export,
50
- :controllers => @controllers.export,
51
- :routes => @routes.export,
52
- :views => @views.export,
53
- :gems => @gems.export,
54
50
  :plugins => @plugins.export,
55
- :site_model => crawler ? crawler.export : nil,
56
- :references => references
51
+ :scanning_exceptions_attributes => errors,
52
+
53
+ :models_attributes => @models.export,
54
+ :controllers_attributes => @controllers.export,
55
+ :routes_attributes => @routes.export,
56
+ :views_attributes => @views.export,
57
+ :gems => @gems.export,
58
+ :references_attributes => references,
59
+ :site_attributes => crawler ? crawler.export : {},
57
60
  }
58
61
  rescue
59
62
  puts $!.message
60
63
  puts $!.backtrace
61
64
  end
62
- puts "hash generated"
63
- puts h.to_json
65
+ puts "Sending snapshot of #{h.to_json.size} bytes"
64
66
  h
65
67
  end
66
68
 
@@ -69,120 +71,5 @@ module Stratagem::Model
69
71
  end
70
72
  end
71
73
 
72
- protected
73
-
74
- class GemContainer
75
- include Enumerable
76
-
77
- def initialize(app_model)
78
- @app_model = app_model
79
- @gems = Gem.loaded_specs
80
- end
81
-
82
- def names
83
- @gems.map {|g| g[0] }
84
- end
85
-
86
- def export(options=nil)
87
- @gems.map {|g|
88
- name, spec = g
89
- [name, {:version => spec.version.version}]
90
- }
91
- end
92
-
93
- def each
94
- @gems.each {|spec| yield spec }
95
- end
96
- end
97
-
98
- class ComponentContainer
99
- include Enumerable
100
-
101
- attr_reader :invalid, :missing, :parse_trees, :components, :errors
102
-
103
- def initialize(app_model)
104
- @app_model = app_model
105
- @components = Set.new()
106
- @parse_trees = {}
107
- @invalid = []
108
- @missing = {}
109
- end
110
-
111
- def export(options=nil)
112
- {
113
- :components => @components.to_a.map {|c| c.export }.compact,
114
- :invalid => @invalid.map {|c| c.export }.compact
115
- }
116
- end
117
-
118
- def find
119
- @components.find{|component| yield component }
120
- end
121
-
122
- def clear
123
- @components.clear
124
- end
125
-
126
- def size
127
- @components.size
128
- end
129
-
130
- def -(other)
131
- @components-other
132
- end
133
-
134
- def each
135
- @components.each {|e| yield e }
136
- end
137
-
138
- def map
139
- @components.map {|e| yield e }
140
- end
141
-
142
- def << (component)
143
- if (component.kind_of?(Array))
144
- component.each {|e|
145
- @components << e
146
- e.app_model = @app_model if e.methods_include?(:app_model=)
147
- }
148
- elsif (component.kind_of?(Exception))
149
- errors << component
150
- else
151
- @components << component
152
- component.app_model = @app_model if component.methods_include?(:app_model=)
153
- end
154
- end
155
- end
156
-
157
- class RouteContainer < ComponentContainer
158
- def recognize(page, method = :get)
159
- path = nil
160
- if (page.kind_of?(Stratagem::Crawler::Page))
161
- method = page.method
162
- path = page.path
163
- else
164
- path = page
165
- end
166
-
167
- unless path.nil?
168
- # path = path.gsub('http://www.example.com', '')
169
- route = self.find {|r| r.responds_to?(path, method) }
170
- puts "route: #{route.path}" if route
171
- route
172
- else
173
- nil
174
- end
175
- end
176
- end
177
-
178
- class PluginContainer < ComponentContainer
179
- def names
180
- components.map {|plugin| plugin.name }
181
- end
182
-
183
- def export(options=nil)
184
- names
185
- end
186
- end
187
74
  end
188
75