sqreen 1.18.3.beta1 → 1.18.3.beta2

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 (121) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +0 -5
  3. data/lib/sqreen/actions.rb +11 -337
  4. data/lib/sqreen/actions/base.rb +110 -0
  5. data/lib/sqreen/actions/block_ip.rb +32 -0
  6. data/lib/sqreen/actions/block_user.rb +44 -0
  7. data/lib/sqreen/actions/ip_range_indexed_action_class.rb +36 -0
  8. data/lib/sqreen/actions/ip_ranges_index.rb +36 -0
  9. data/lib/sqreen/actions/redirect_ip.rb +40 -0
  10. data/lib/sqreen/actions/redirect_user.rb +45 -0
  11. data/lib/sqreen/actions/repository.rb +24 -0
  12. data/lib/sqreen/actions/unknown_action_type.rb +16 -0
  13. data/lib/sqreen/actions/user_action_class.rb +41 -0
  14. data/lib/sqreen/agent.rb +4 -1
  15. data/lib/sqreen/attack_blocked.rb +17 -0
  16. data/lib/sqreen/binding_accessor.rb +9 -102
  17. data/lib/sqreen/binding_accessor/path_elem.rb +8 -0
  18. data/lib/sqreen/binding_accessor/transforms.rb +107 -0
  19. data/lib/sqreen/capped_queue.rb +2 -0
  20. data/lib/sqreen/{callbacks.rb → cb.rb} +1 -53
  21. data/lib/sqreen/{callback_tree.rb → cb_tree.rb} +2 -2
  22. data/lib/sqreen/condition_evaluator.rb +22 -5
  23. data/lib/sqreen/configuration.rb +3 -0
  24. data/lib/sqreen/default_cb.rb +20 -0
  25. data/lib/sqreen/deferred_logger.rb +63 -0
  26. data/lib/sqreen/deliveries.rb +10 -0
  27. data/lib/sqreen/deliveries/batch.rb +7 -1
  28. data/lib/sqreen/deliveries/simple.rb +5 -0
  29. data/lib/sqreen/dependency/rails.rb +4 -0
  30. data/lib/sqreen/dependency/sinatra.rb +4 -0
  31. data/lib/sqreen/error_handling_middleware.rb +30 -0
  32. data/lib/sqreen/event.rb +2 -0
  33. data/lib/sqreen/events/attack.rb +2 -0
  34. data/lib/sqreen/events/request_record.rb +11 -56
  35. data/lib/sqreen/exception.rb +9 -40
  36. data/lib/sqreen/formatter_with_tid.rb +45 -0
  37. data/lib/sqreen/framework_cb.rb +28 -0
  38. data/lib/sqreen/frameworks.rb +7 -0
  39. data/lib/sqreen/frameworks/generic.rb +5 -1
  40. data/lib/sqreen/frameworks/rails.rb +2 -0
  41. data/lib/sqreen/frameworks/request_recorder.rb +3 -0
  42. data/lib/sqreen/frameworks/sinatra.rb +2 -0
  43. data/lib/sqreen/frameworks/sqreen_test.rb +2 -0
  44. data/lib/sqreen/instrumentation.rb +5 -5
  45. data/lib/sqreen/invalid_signature_exception.rb +8 -0
  46. data/lib/{sqreen-alt.rb → sqreen/js.rb} +6 -1
  47. data/lib/sqreen/js/call_context.rb +10 -0
  48. data/lib/sqreen/js/context_pool.rb +60 -0
  49. data/lib/sqreen/js/exec_js_runnable.rb +20 -0
  50. data/lib/sqreen/js/execjs_adapter.rb +6 -47
  51. data/lib/sqreen/js/executable_js.rb +12 -0
  52. data/lib/sqreen/js/js_service.rb +2 -22
  53. data/lib/sqreen/js/js_service_adapter.rb +18 -0
  54. data/lib/sqreen/js/mini_racer_adapter.rb +6 -180
  55. data/lib/sqreen/js/mini_racer_executable_js.rb +142 -0
  56. data/lib/sqreen/js/thread_local_exec_js_runnable.rb +47 -0
  57. data/lib/sqreen/log.rb +8 -188
  58. data/lib/sqreen/logger.rb +83 -0
  59. data/lib/sqreen/metrics_store.rb +3 -11
  60. data/lib/sqreen/metrics_store/already_registered_metric.rb +11 -0
  61. data/lib/sqreen/metrics_store/unknown_metric.rb +11 -0
  62. data/lib/sqreen/metrics_store/unregistered_metric.rb +11 -0
  63. data/lib/sqreen/middleware.rb +0 -44
  64. data/lib/sqreen/mono_time.rb +2 -0
  65. data/lib/sqreen/node.rb +44 -0
  66. data/lib/sqreen/not_implemented_yet.rb +8 -0
  67. data/lib/sqreen/null_logger.rb +24 -0
  68. data/lib/sqreen/payload_creator.rb +2 -19
  69. data/lib/sqreen/payload_creator/header_section.rb +28 -0
  70. data/lib/sqreen/prefix.rb +33 -0
  71. data/lib/sqreen/rails_middleware.rb +14 -0
  72. data/lib/sqreen/remote_command.rb +1 -8
  73. data/lib/sqreen/remote_command/failure_output.rb +11 -0
  74. data/lib/sqreen/rules.rb +32 -2
  75. data/lib/sqreen/{rule_attributes.rb → rules/attrs.rb} +0 -0
  76. data/lib/sqreen/{rules_callbacks/sdk_auth_track.rb → rules/auth_track_cb.rb} +2 -2
  77. data/lib/sqreen/{rules_callbacks/binding_accessor_matcher.rb → rules/binding_accessor_matcher_cb.rb} +4 -8
  78. data/lib/sqreen/{rules_callbacks → rules}/binding_accessor_metrics.rb +1 -1
  79. data/lib/sqreen/{rules_callbacks/blacklist_ips.rb → rules/blacklist_ips_cb.rb} +3 -2
  80. data/lib/sqreen/{rules_callbacks → rules}/count_http_codes.rb +2 -2
  81. data/lib/sqreen/{rules_callbacks/crawler_user_agent_matches.rb → rules/crawler_user_agent_matches_cb.rb} +1 -1
  82. data/lib/sqreen/{rules_callbacks/crawler_user_agent_matches_metrics.rb → rules/crawler_user_agent_matches_metrics_cb.rb} +1 -1
  83. data/lib/sqreen/{rules_callbacks/custom_error.rb → rules/custom_error_cb.rb} +1 -1
  84. data/lib/sqreen/{rules_callbacks/devise_auth_track.rb → rules/devise_auth_track_cb.rb} +2 -2
  85. data/lib/sqreen/{rules_callbacks/devise_signup_track.rb → rules/devise_signup_track_cb.rb} +2 -2
  86. data/lib/sqreen/{rules_callbacks/execjs.rb → rules/execjs_cb.rb} +49 -50
  87. data/lib/sqreen/{rules_callbacks/headers_insert.rb → rules/headers_insert_cb.rb} +1 -1
  88. data/lib/sqreen/{rules_callbacks → rules}/matcher_rule.rb +2 -2
  89. data/lib/sqreen/{rules_callbacks/not_found.rb → rules/not_found_cb.rb} +2 -2
  90. data/lib/sqreen/{rules_callbacks/rails_parameters.rb → rules/rails_parameters_cb.rb} +1 -1
  91. data/lib/sqreen/{rules_callbacks → rules}/record_request_context.rb +1 -1
  92. data/lib/sqreen/{rules_callbacks/regexp_rule.rb → rules/regexp_rule_cb.rb} +1 -1
  93. data/lib/sqreen/{rule_callback.rb → rules/rule_cb.rb} +2 -2
  94. data/lib/sqreen/{rules_callbacks → rules}/run_req_start_actions.rb +4 -2
  95. data/lib/sqreen/{rules_callbacks → rules}/run_user_actions.rb +1 -1
  96. data/lib/sqreen/{rules_callbacks/shell_env.rb → rules/shell_env_cb.rb} +1 -1
  97. data/lib/sqreen/{rules_callbacks/sdk_signup_track.rb → rules/signup_track_cb.rb} +2 -2
  98. data/lib/sqreen/{rules_callbacks → rules}/update_request_context.rb +1 -1
  99. data/lib/sqreen/{rules_callbacks/url_matches.rb → rules/url_matches_cb.rb} +1 -1
  100. data/lib/sqreen/{rules_callbacks/user_agent_matches.rb → rules/user_agent_matches_cb.rb} +1 -1
  101. data/lib/sqreen/{rules_callbacks/waf.rb → rules/waf_cb.rb} +7 -3
  102. data/lib/sqreen/{rules_callbacks/reflected_xss.rb → rules/xss_cb.rb} +10 -7
  103. data/lib/sqreen/run_when_called_cb.rb +21 -0
  104. data/lib/sqreen/sensitive_data_redactor.rb +111 -0
  105. data/lib/sqreen/signature_verifier.rb +20 -0
  106. data/lib/sqreen/sinatra_middleware.rb +14 -0
  107. data/lib/sqreen/{rules_signature.rb → sqreen_signed_verifier.rb} +5 -17
  108. data/lib/sqreen/token_invalid_exception.rb +8 -0
  109. data/lib/sqreen/token_not_found_exception.rb +9 -0
  110. data/lib/sqreen/trie.rb +3 -64
  111. data/lib/sqreen/unauthorized.rb +8 -0
  112. data/lib/sqreen/util.rb +2 -0
  113. data/lib/sqreen/util/capped_array.rb +30 -0
  114. data/lib/sqreen/util/capped_hash.rb +36 -0
  115. data/lib/sqreen/util/capped_string.rb +22 -0
  116. data/lib/sqreen/util/capper.rb +57 -0
  117. data/lib/sqreen/version.rb +1 -1
  118. data/lib/sqreen/waf_error.rb +18 -0
  119. metadata +85 -36
  120. data/lib/sqreen/rules_callbacks.rb +0 -36
  121. data/lib/sqreen/rules_callbacks/inspect_rule.rb +0 -25
@@ -0,0 +1,47 @@
1
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
+ # Please refer to our terms for more information: https://www.sqreen.com/terms.html
3
+
4
+ # TODO: => Sqreen::JS:ExecJS
5
+
6
+ require 'sqreen/js/executable_js'
7
+
8
+ module Sqreen
9
+ module Js
10
+ class ThreadLocalExecJsRunnable < ExecutableJs
11
+ def initialize(code)
12
+ @code = code
13
+ @tl_key = "SQREEN_EXECJS_CONTEXT_#{object_id}".freeze
14
+ @runtimes = [] # place where to keep strong references
15
+ @runtimes_mutex = Mutex.new
16
+ end
17
+
18
+ def run_js_cb(cbname, _budget, arguments)
19
+ tl_exec_js_runnable.call(cbname, *arguments)
20
+ end
21
+
22
+ def with_runtimes_mutex
23
+ @runtimes_mutex.synchronize { yield }
24
+ end
25
+
26
+ private
27
+
28
+ def dispose_from_dead_threads
29
+ with_runtimes_mutex do
30
+ @runtimes.delete_if { |th, _runtime| !th.alive? }
31
+ end
32
+ end
33
+
34
+ def tl_exec_js_runnable
35
+ runnable = Thread.current[@tl_key]
36
+ return runnable if runnable && runnable.weakref_alive?
37
+
38
+ dispose_from_dead_threads
39
+ runtime = ExecJS.compile(@code)
40
+ with_runtimes_mutex do
41
+ @runtimes << [Thread.current, runtime]
42
+ end
43
+ Thread.current[@tl_key] = WeakRef.new(runtime)
44
+ end
45
+ end
46
+ end
47
+ end
data/lib/sqreen/log.rb CHANGED
@@ -1,207 +1,27 @@
1
1
  # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
2
  # Please refer to our terms for more information: https://www.sqreen.com/terms.html
3
3
 
4
- require 'logger'
5
- require 'singleton'
6
-
7
4
  require 'sqreen/performance_notifications/log'
8
5
  require 'sqreen/performance_notifications/log_performance'
9
6
  require 'sqreen/configuration'
10
7
 
8
+ require 'sqreen/logger'
9
+ require 'sqreen/deferred_logger'
10
+
11
+ # TODO: fold into Sqreen::Logger
12
+
11
13
  module Sqreen
12
14
  def self.log_init
13
- @logger = Logger.new(
15
+ @logger = Sqreen::Logger.new(
14
16
  Sqreen.config_get(:log_level).to_s.upcase,
15
17
  Sqreen.config_get(:log_location)
16
18
  )
17
- DeferredLogger.instance.flush_to(@logger.instance_eval { @logger })
19
+ Sqreen::DeferredLogger.instance.flush_to(@logger.instance_eval { @logger })
18
20
  rescue => e
19
21
  warn "Sqreen logger exception: #{e}"
20
22
  end
21
23
 
22
24
  def self::log
23
- @logger || DeferredLogger.instance
24
- end
25
-
26
- # Ruby default formatter modified to display current thread_id
27
- class FormatterWithTid
28
- Format = "%s, [%s#%d.%s] %5s -- %s: %s\n".freeze
29
- DatetimeFormat = '%Y-%m-%dT%H:%M:%S.%6N '.freeze
30
-
31
- attr_accessor :datetime_format
32
-
33
- def initialize
34
- @datetime_format = nil
35
- end
36
-
37
- def call(severity, time, progname, msg)
38
- format(Format,
39
- severity[0..0], format_datetime(time), $$,
40
- Thread.current.object_id.to_s(36),
41
- severity, progname, msg2str(msg)
42
- )
43
- end
44
-
45
- private
46
-
47
- def format_datetime(time)
48
- time.strftime(DatetimeFormat)
49
- end
50
-
51
- def msg2str(msg)
52
- case msg
53
- when ::String
54
- msg
55
- when ::Exception
56
- "#{msg.message} (#{msg.class})\n" << (msg.backtrace || []).join("\n")
57
- else
58
- msg.inspect
59
- end
60
- end
61
- end
62
-
63
- # Wrapper class for sqreen logging
64
- class Logger
65
- SEVERITY_TO_METHOD = ::Logger::Severity.constants.each_with_object({}) do |s, h|
66
- h[::Logger::Severity.const_get(s)] = s.downcase
67
- end
68
-
69
- def initialize(desired_level, log_location, force_logger = nil)
70
- if force_logger
71
- @logger = force_logger
72
- else
73
- init_logger_output(log_location)
74
- end
75
- init_log_level(desired_level)
76
- enforce_log_format(@logger)
77
- create_error_logger
78
- end
79
-
80
- def debug(msg = nil, &block)
81
- @logger.debug(msg, &block)
82
- end
83
-
84
- def info(msg = nil, &block)
85
- @logger.info(msg, &block)
86
- end
87
-
88
- def warn(msg = nil, &block)
89
- @logger.warn(msg, &block)
90
- end
91
-
92
- def error(msg = nil, &block)
93
- @error_logger.error(msg, &block)
94
- @logger.error(msg, &block)
95
- end
96
-
97
- def add(severity, msg = nil, &block)
98
- send(SEVERITY_TO_METHOD[severity], msg, &block)
99
- end
100
-
101
- protected
102
-
103
- def init_logger_output(path)
104
- path = File.expand_path(path)
105
- if File.writable?(path) || File.writable?(File.dirname(path))
106
- @logger = ::Logger.new(path)
107
- else
108
- @logger = ::Logger.new(STDOUT)
109
- @logger.info("Cannot access #{path} for writing. Defaulting to stdout")
110
- end
111
- rescue => e
112
- @logger = ::Logger.new(STDOUT)
113
- @logger.error('Got error while trying to setting logger up, '\
114
- "falling back to stdout #{e.inspect}")
115
- end
116
-
117
- def init_log_level(level)
118
- log_level = ::Logger.const_get(level)
119
- @logger.level = log_level
120
- Sqreen::PerformanceNotifications::Log.enable if level == 'DEBUG'
121
- return if level != 'DEBUG' && !Sqreen.config_get(:report_perf)
122
- Sqreen::PerformanceNotifications::LogPerformance.enable
123
- end
124
-
125
- def create_error_logger
126
- @error_logger = Kernel.const_defined?('MiniTest') ? NullLogger.instance : ::Logger.new(STDERR)
127
- enforce_log_format(@error_logger)
128
- end
129
-
130
- def enforce_log_format(logger)
131
- logger.formatter = FormatterWithTid.new
132
- end
133
- end
134
-
135
- class NullLogger
136
- include Singleton
137
-
138
- def debug(_msg = nil); end
139
-
140
- def info(_msg = nil); end
141
-
142
- def warn(_msg = nil); end
143
-
144
- def error(_msg = nil); end
145
-
146
- def fatal(_msg = nil); end
147
-
148
- def add(_severity, _msg = nil); end
149
-
150
- def formatter=(_); end
151
- end
152
-
153
- class DeferredLogger
154
- include Singleton
155
-
156
- def initialize
157
- @buffer = StringIO.new
158
- @logger = ::Logger.new(@buffer)
159
- end
160
-
161
- def debug(msg = nil, &block)
162
- @logger.debug(msg, &block)
163
- end
164
-
165
- def info(msg = nil, &block)
166
- @logger.info(msg, &block)
167
- end
168
-
169
- def warn(msg = nil, &block)
170
- @logger.warn(msg, &block)
171
- end
172
-
173
- def error(msg = nil, &block)
174
- @logger.error(msg, &block)
175
- end
176
-
177
- def fatal(msg = nil, &block)
178
- @logger.error(msg, &block)
179
- end
180
-
181
- def add(severity, msg = nil, &block)
182
- send(Sqreen::Logger::SEVERITY_TO_METHOD[severity], msg, &block)
183
- end
184
-
185
- def formatter=(value)
186
- @logger.formatter = value
187
- end
188
-
189
- def flush_to(logger)
190
- logger.instance_eval { @logdev }.write(read).tap { reset }
191
- end
192
-
193
- private
194
-
195
- def read
196
- @buffer.rewind
197
- @buffer.read
198
- end
199
-
200
- def reset
201
- buffer = StringIO.new
202
- logger = ::Logger.new(buffer)
203
- logger.formatter = @logger.formatter
204
- @buffer, @logger = buffer, logger
205
- end
25
+ @logger || Sqreen::DeferredLogger.instance
206
26
  end
207
27
  end
@@ -0,0 +1,83 @@
1
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
+ # Please refer to our terms for more information: https://www.sqreen.com/terms.html
3
+
4
+ require 'logger'
5
+ require 'sqreen/log'
6
+ require 'sqreen/formatter_with_tid'
7
+ require 'sqreen/null_logger'
8
+
9
+ # TODO: inherit from ::Logger
10
+
11
+ module Sqreen
12
+ # Wrapper class for sqreen logging
13
+ class Logger
14
+ SEVERITY_TO_METHOD = ::Logger::Severity.constants.each_with_object({}) do |s, h|
15
+ h[::Logger::Severity.const_get(s)] = s.downcase
16
+ end
17
+
18
+ def initialize(desired_level, log_location, force_logger = nil)
19
+ if force_logger
20
+ @logger = force_logger
21
+ else
22
+ init_logger_output(log_location)
23
+ end
24
+ init_log_level(desired_level)
25
+ enforce_log_format(@logger)
26
+ create_error_logger
27
+ end
28
+
29
+ def debug(msg = nil, &block)
30
+ @logger.debug(msg, &block)
31
+ end
32
+
33
+ def info(msg = nil, &block)
34
+ @logger.info(msg, &block)
35
+ end
36
+
37
+ def warn(msg = nil, &block)
38
+ @logger.warn(msg, &block)
39
+ end
40
+
41
+ def error(msg = nil, &block)
42
+ @error_logger.error(msg, &block)
43
+ @logger.error(msg, &block)
44
+ end
45
+
46
+ def add(severity, msg = nil, &block)
47
+ send(SEVERITY_TO_METHOD[severity], msg, &block)
48
+ end
49
+
50
+ protected
51
+
52
+ def init_logger_output(path)
53
+ path = File.expand_path(path)
54
+ if File.writable?(path) || File.writable?(File.dirname(path))
55
+ @logger = ::Logger.new(path)
56
+ else
57
+ @logger = ::Logger.new(STDOUT)
58
+ @logger.info("Cannot access #{path} for writing. Defaulting to stdout")
59
+ end
60
+ rescue StandardError => e
61
+ @logger = ::Logger.new(STDOUT)
62
+ @logger.error('Got error while trying to setting logger up, '\
63
+ "falling back to stdout #{e.inspect}")
64
+ end
65
+
66
+ def init_log_level(level)
67
+ log_level = ::Logger.const_get(level)
68
+ @logger.level = log_level
69
+ Sqreen::PerformanceNotifications::Log.enable if level == 'DEBUG'
70
+ return if level != 'DEBUG' && !Sqreen.config_get(:report_perf)
71
+ Sqreen::PerformanceNotifications::LogPerformance.enable
72
+ end
73
+
74
+ def create_error_logger
75
+ @error_logger = Kernel.const_defined?('MiniTest') ? NullLogger.instance : ::Logger.new(STDERR)
76
+ enforce_log_format(@error_logger)
77
+ end
78
+
79
+ def enforce_log_format(logger)
80
+ logger.formatter = Sqreen::FormatterWithTid.new
81
+ end
82
+ end
83
+ end
@@ -1,23 +1,15 @@
1
1
  # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
2
  # Please refer to our terms for more information: https://www.sqreen.com/terms.html
3
3
 
4
- require 'sqreen/exception'
5
4
  require 'sqreen/metrics'
6
5
  require 'sqreen/mono_time'
6
+ require 'sqreen/metrics_store/unknown_metric'
7
+ require 'sqreen/metrics_store/unregistered_metric'
8
+ require 'sqreen/metrics_store/already_registered_metric'
7
9
 
8
10
  module Sqreen
9
11
  # This store and register metrics
10
12
  class MetricsStore
11
- # When a metric is not yet created
12
- class UnregisteredMetric < Sqreen::Exception
13
- end
14
- # When the metric is unknown
15
- class UnknownMetric < Sqreen::Exception
16
- end
17
- # When this name as already been declared with another kind
18
- class AlreadyRegisteredMetric < Sqreen::Exception
19
- end
20
-
21
13
  # definition keys
22
14
  NAME_KEY = 'name'.freeze
23
15
  KIND_KEY = 'kind'.freeze
@@ -0,0 +1,11 @@
1
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
+ # Please refer to our terms for more information: https://www.sqreen.com/terms.html
3
+
4
+ require 'sqreen/exception'
5
+
6
+ module Sqreen
7
+ class MetricsStore
8
+ class AlreadyRegisteredMetric < Sqreen::Exception
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
+ # Please refer to our terms for more information: https://www.sqreen.com/terms.html
3
+
4
+ require 'sqreen/exception'
5
+
6
+ module Sqreen
7
+ class MetricsStore
8
+ class UnknownMetric < Sqreen::Exception
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
+ # Please refer to our terms for more information: https://www.sqreen.com/terms.html
3
+
4
+ require 'sqreen/exception'
5
+
6
+ module Sqreen
7
+ class MetricsStore
8
+ class UnregisteredMetric < Sqreen::Exception
9
+ end
10
+ end
11
+ end
@@ -11,48 +11,4 @@ module Sqreen
11
11
  @app.call(env)
12
12
  end
13
13
  end
14
-
15
- class ErrorHandlingMiddleware
16
- def initialize(app)
17
- @app = app
18
- end
19
-
20
- def call(env)
21
- @app.call(env)
22
- rescue => e
23
- sqreen_attack = nil
24
- if e.is_a?(Sqreen::AttackBlocked)
25
- sqreen_attack = e
26
- elsif e.respond_to?(:original_exception) &&
27
- e.original_exception.is_a?(Sqreen::AttackBlocked)
28
- sqreen_attack = e.original_exception
29
- end
30
-
31
- if sqreen_attack && sqreen_attack.redirect_url
32
- return [303, { 'Location' => sqreen_attack.redirect_url }, ['']]
33
- else
34
- raise
35
- end
36
- end
37
- end
38
-
39
- class RailsMiddleware
40
- def initialize(app)
41
- @app = app
42
- end
43
-
44
- def call(env)
45
- @app.call(env)
46
- end
47
- end
48
-
49
- class SinatraMiddleware
50
- def initialize(app)
51
- @app = app
52
- end
53
-
54
- def call(env)
55
- @app.call(env)
56
- end
57
- end
58
14
  end