sqreen 1.18.6-java → 1.20.0-java

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 (190) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/lib/sqreen/actions.rb +2 -0
  4. data/lib/sqreen/actions/actions_index.rb +16 -0
  5. data/lib/sqreen/actions/base.rb +4 -10
  6. data/lib/sqreen/actions/block_ip.rb +2 -0
  7. data/lib/sqreen/actions/block_user.rb +2 -0
  8. data/lib/sqreen/actions/ip_range_indexed_action_class.rb +4 -24
  9. data/lib/sqreen/actions/ip_ranges_index.rb +32 -11
  10. data/lib/sqreen/actions/redirect_ip.rb +2 -0
  11. data/lib/sqreen/actions/redirect_user.rb +2 -0
  12. data/lib/sqreen/actions/repository.rb +27 -8
  13. data/lib/sqreen/actions/unknown_action_type.rb +4 -0
  14. data/lib/sqreen/actions/user_action_class.rb +5 -30
  15. data/lib/sqreen/actions/users_index.rb +35 -0
  16. data/lib/sqreen/agent.rb +2 -1
  17. data/lib/sqreen/aggregated_metric.rb +25 -0
  18. data/lib/sqreen/attack_blocked.rb +2 -0
  19. data/lib/sqreen/binding_accessor.rb +2 -0
  20. data/lib/sqreen/binding_accessor/path_elem.rb +2 -0
  21. data/lib/sqreen/binding_accessor/transforms.rb +8 -1
  22. data/lib/sqreen/call_countable.rb +2 -0
  23. data/lib/sqreen/capped_queue.rb +2 -0
  24. data/lib/sqreen/cb.rb +2 -0
  25. data/lib/sqreen/cb_tree.rb +2 -0
  26. data/lib/sqreen/condition_evaluator.rb +2 -0
  27. data/lib/sqreen/conditionable.rb +2 -0
  28. data/lib/sqreen/configuration.rb +19 -1
  29. data/lib/sqreen/context.rb +2 -0
  30. data/lib/sqreen/default_cb.rb +2 -0
  31. data/lib/sqreen/deferred_logger.rb +2 -0
  32. data/lib/sqreen/deliveries.rb +2 -0
  33. data/lib/sqreen/deliveries/batch.rb +6 -1
  34. data/lib/sqreen/deliveries/simple.rb +6 -0
  35. data/lib/sqreen/dependency.rb +3 -1
  36. data/lib/sqreen/dependency/detector.rb +22 -14
  37. data/lib/sqreen/dependency/libsqreen.rb +4 -0
  38. data/lib/sqreen/dependency/new_relic.rb +2 -0
  39. data/lib/sqreen/dependency/rack.rb +10 -5
  40. data/lib/sqreen/dependency/rails.rb +4 -0
  41. data/lib/sqreen/dependency/sentry.rb +2 -0
  42. data/lib/sqreen/dependency/sinatra.rb +12 -1
  43. data/lib/sqreen/encoding_sanitizer.rb +2 -0
  44. data/lib/sqreen/error_handling_middleware.rb +2 -0
  45. data/lib/sqreen/event.rb +9 -5
  46. data/lib/sqreen/events/attack.rb +25 -18
  47. data/lib/sqreen/events/remote_exception.rb +2 -22
  48. data/lib/sqreen/events/request_record.rb +17 -70
  49. data/lib/sqreen/exception.rb +2 -0
  50. data/lib/sqreen/formatter_with_tid.rb +2 -0
  51. data/lib/sqreen/framework_cb.rb +2 -0
  52. data/lib/sqreen/frameworks.rb +2 -0
  53. data/lib/sqreen/frameworks/generic.rb +2 -0
  54. data/lib/sqreen/frameworks/rails.rb +1 -0
  55. data/lib/sqreen/frameworks/rails3.rb +2 -0
  56. data/lib/sqreen/frameworks/request_recorder.rb +15 -2
  57. data/lib/sqreen/frameworks/sinatra.rb +2 -0
  58. data/lib/sqreen/frameworks/sqreen_test.rb +2 -0
  59. data/lib/sqreen/graft.rb +12 -0
  60. data/lib/sqreen/graft/call.rb +150 -0
  61. data/lib/sqreen/{dependency → graft}/callback.rb +12 -4
  62. data/lib/sqreen/graft/hook.rb +316 -0
  63. data/lib/sqreen/{dependency → graft}/hook_point.rb +152 -33
  64. data/lib/sqreen/graft/hook_point_error.rb +10 -0
  65. data/lib/sqreen/invalid_signature_exception.rb +2 -0
  66. data/lib/sqreen/js.rb +2 -0
  67. data/lib/sqreen/js/call_context.rb +2 -0
  68. data/lib/sqreen/js/context_pool.rb +2 -0
  69. data/lib/sqreen/js/exec_js_runnable.rb +2 -0
  70. data/lib/sqreen/js/execjs_adapter.rb +2 -0
  71. data/lib/sqreen/js/executable_js.rb +2 -0
  72. data/lib/sqreen/js/js_service.rb +2 -0
  73. data/lib/sqreen/js/js_service_adapter.rb +2 -0
  74. data/lib/sqreen/js/mini_racer_adapter.rb +2 -0
  75. data/lib/sqreen/js/mini_racer_executable_js.rb +2 -0
  76. data/lib/sqreen/js/thread_local_exec_js_runnable.rb +2 -0
  77. data/lib/sqreen/kit/signals/specialized/aggregated_metric.rb +72 -0
  78. data/lib/sqreen/kit/signals/specialized/attack.rb +57 -0
  79. data/lib/sqreen/kit/signals/specialized/binning_metric.rb +76 -0
  80. data/lib/sqreen/kit/signals/specialized/http_trace.rb +26 -0
  81. data/lib/sqreen/kit/signals/specialized/sdk_track_call.rb +50 -0
  82. data/lib/sqreen/kit/signals/specialized/sqreen_exception.rb +57 -0
  83. data/lib/sqreen/{backport.rb → legacy.rb} +3 -2
  84. data/lib/sqreen/{instrumentation.rb → legacy/instrumentation.rb} +31 -2
  85. data/lib/sqreen/legacy/old_event_submission_strategy.rb +221 -0
  86. data/lib/sqreen/legacy/waf_redactions.rb +49 -0
  87. data/lib/sqreen/log.rb +2 -0
  88. data/lib/sqreen/log/loggable.rb +28 -0
  89. data/lib/sqreen/logger.rb +2 -0
  90. data/lib/sqreen/metrics.rb +2 -0
  91. data/lib/sqreen/metrics/average.rb +2 -0
  92. data/lib/sqreen/metrics/base.rb +5 -0
  93. data/lib/sqreen/metrics/binning.rb +2 -0
  94. data/lib/sqreen/metrics/collect.rb +2 -0
  95. data/lib/sqreen/metrics/sum.rb +2 -0
  96. data/lib/sqreen/metrics_store.rb +24 -12
  97. data/lib/sqreen/metrics_store/already_registered_metric.rb +2 -0
  98. data/lib/sqreen/metrics_store/unknown_metric.rb +2 -0
  99. data/lib/sqreen/metrics_store/unregistered_metric.rb +2 -0
  100. data/lib/sqreen/middleware.rb +2 -0
  101. data/lib/sqreen/mono_time.rb +2 -0
  102. data/lib/sqreen/node.rb +2 -0
  103. data/lib/sqreen/not_implemented_yet.rb +2 -0
  104. data/lib/sqreen/null_logger.rb +2 -0
  105. data/lib/sqreen/payload_creator.rb +2 -0
  106. data/lib/sqreen/payload_creator/header_section.rb +2 -0
  107. data/lib/sqreen/performance_notifications.rb +2 -0
  108. data/lib/sqreen/performance_notifications/binned_metrics.rb +10 -2
  109. data/lib/sqreen/performance_notifications/log.rb +2 -0
  110. data/lib/sqreen/performance_notifications/log_performance.rb +2 -0
  111. data/lib/sqreen/performance_notifications/metrics.rb +2 -0
  112. data/lib/sqreen/performance_notifications/newrelic.rb +2 -0
  113. data/lib/sqreen/prefix.rb +2 -0
  114. data/lib/sqreen/rails_middleware.rb +2 -0
  115. data/lib/sqreen/remote_command.rb +2 -0
  116. data/lib/sqreen/remote_command/failure_output.rb +5 -0
  117. data/lib/sqreen/rules.rb +6 -2
  118. data/lib/sqreen/rules/attrs.rb +2 -0
  119. data/lib/sqreen/rules/auth_track_cb.rb +2 -0
  120. data/lib/sqreen/rules/binding_accessor_matcher_cb.rb +2 -0
  121. data/lib/sqreen/rules/binding_accessor_metrics.rb +2 -0
  122. data/lib/sqreen/rules/blacklist_ips_cb.rb +2 -0
  123. data/lib/sqreen/rules/count_http_codes.rb +2 -0
  124. data/lib/sqreen/rules/crawler_user_agent_matches_cb.rb +2 -0
  125. data/lib/sqreen/rules/crawler_user_agent_matches_metrics_cb.rb +2 -0
  126. data/lib/sqreen/rules/custom_error_cb.rb +2 -0
  127. data/lib/sqreen/rules/devise_auth_track_cb.rb +2 -0
  128. data/lib/sqreen/rules/devise_signup_track_cb.rb +2 -0
  129. data/lib/sqreen/rules/execjs_cb.rb +2 -0
  130. data/lib/sqreen/rules/headers_insert_cb.rb +7 -0
  131. data/lib/sqreen/rules/matcher_rule.rb +2 -0
  132. data/lib/sqreen/rules/not_found_cb.rb +7 -0
  133. data/lib/sqreen/rules/rails_parameters_cb.rb +2 -0
  134. data/lib/sqreen/rules/record_request_context.rb +2 -0
  135. data/lib/sqreen/rules/regexp_rule_cb.rb +2 -0
  136. data/lib/sqreen/rules/rule_cb.rb +4 -0
  137. data/lib/sqreen/rules/run_req_start_actions.rb +3 -1
  138. data/lib/sqreen/rules/run_user_actions.rb +3 -1
  139. data/lib/sqreen/rules/shell_env_cb.rb +2 -0
  140. data/lib/sqreen/rules/signup_track_cb.rb +2 -0
  141. data/lib/sqreen/rules/update_request_context.rb +2 -0
  142. data/lib/sqreen/rules/url_matches_cb.rb +2 -0
  143. data/lib/sqreen/rules/user_agent_matches_cb.rb +2 -0
  144. data/lib/sqreen/rules/waf_cb.rb +41 -16
  145. data/lib/sqreen/rules/xss_cb.rb +2 -0
  146. data/lib/sqreen/run_when_called_cb.rb +2 -0
  147. data/lib/sqreen/runner.rb +68 -12
  148. data/lib/sqreen/runtime_infos.rb +2 -0
  149. data/lib/sqreen/safe_json.rb +2 -0
  150. data/lib/sqreen/sdk.rb +4 -0
  151. data/lib/sqreen/sensitive_data_redactor.rb +21 -31
  152. data/lib/sqreen/serializer.rb +2 -0
  153. data/lib/sqreen/session.rb +41 -37
  154. data/lib/sqreen/shared_storage.rb +2 -0
  155. data/lib/sqreen/shared_storage23.rb +2 -0
  156. data/lib/sqreen/shrink_wrap.rb +16 -0
  157. data/lib/sqreen/signals/conversions.rb +283 -0
  158. data/lib/sqreen/signals/http_trace_redaction.rb +111 -0
  159. data/lib/sqreen/signals/signals_submission_strategy.rb +78 -0
  160. data/lib/sqreen/signature_verifier.rb +2 -0
  161. data/lib/sqreen/sinatra_middleware.rb +2 -0
  162. data/lib/sqreen/sqreen_signed_verifier.rb +2 -0
  163. data/lib/sqreen/token_invalid_exception.rb +2 -0
  164. data/lib/sqreen/token_not_found_exception.rb +2 -0
  165. data/lib/sqreen/trie.rb +2 -0
  166. data/lib/sqreen/unauthorized.rb +2 -0
  167. data/lib/sqreen/util.rb +5 -0
  168. data/lib/sqreen/util/capped_array.rb +2 -0
  169. data/lib/sqreen/util/capped_hash.rb +2 -0
  170. data/lib/sqreen/util/capped_string.rb +2 -0
  171. data/lib/sqreen/util/capper.rb +2 -0
  172. data/lib/sqreen/version.rb +3 -1
  173. data/lib/sqreen/waf_error.rb +2 -0
  174. data/lib/sqreen/weave.rb +12 -0
  175. data/lib/sqreen/weave/hardcoded.rb +19 -0
  176. data/lib/sqreen/weave/instrumentor.rb +48 -0
  177. data/lib/sqreen/weave/legacy.rb +12 -0
  178. data/lib/sqreen/weave/legacy/instrumentation.rb +406 -0
  179. data/lib/sqreen/web_server.rb +2 -0
  180. data/lib/sqreen/web_server/generic.rb +2 -0
  181. data/lib/sqreen/web_server/passenger.rb +2 -0
  182. data/lib/sqreen/web_server/puma.rb +2 -0
  183. data/lib/sqreen/web_server/rainbows.rb +2 -0
  184. data/lib/sqreen/web_server/thin.rb +2 -0
  185. data/lib/sqreen/web_server/unicorn.rb +2 -0
  186. data/lib/sqreen/web_server/webrick.rb +2 -0
  187. data/lib/sqreen/worker.rb +2 -0
  188. metadata +65 -9
  189. data/lib/sqreen/backport/original_name.rb +0 -86
  190. data/lib/sqreen/dependency/hook.rb +0 -102
@@ -0,0 +1,316 @@
1
+ # typed: false
2
+
3
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
4
+ # Please refer to our terms for more information: https://www.sqreen.com/terms.html
5
+
6
+ require 'sqreen/graft'
7
+ require 'sqreen/graft/call'
8
+ require 'sqreen/graft/callback'
9
+ require 'sqreen/graft/hook_point'
10
+
11
+ module Sqreen
12
+ module Graft
13
+ class Hook
14
+ @hooks = {}
15
+
16
+ def self.[](hook_point, strategy = :chain)
17
+ @hooks[hook_point] ||= new(hook_point, nil, strategy)
18
+ end
19
+
20
+ def self.add(hook_point, strategy = :chain, &block)
21
+ self[hook_point, strategy].add(&block)
22
+ end
23
+
24
+ attr_reader :point
25
+
26
+ def initialize(hook_point, dependency_test = nil, strategy = :chain)
27
+ @disabled = false
28
+ @point = hook_point.is_a?(HookPoint) ? hook_point : HookPoint.new(hook_point, strategy)
29
+ @before = []
30
+ @after = []
31
+ @raised = []
32
+ @ensured = []
33
+ @dependency_test = dependency_test || Proc.new { point.exist? }
34
+ end
35
+
36
+ def dependency?
37
+ @dependency_test.call if @dependency_test
38
+ end
39
+
40
+ def add(&block)
41
+ tap { instance_eval(&block) }
42
+ end
43
+
44
+ def callback_name(whence, tag = nil)
45
+ "#{point}@#{whence}" << (tag ? ":#{tag}" : "")
46
+ end
47
+
48
+ def before(tag = nil, opts = {}, &block)
49
+ return @before.sort_by(&:rank) if block.nil?
50
+
51
+ @before << Callback.new(callback_name(:before, tag), opts, &block)
52
+ end
53
+
54
+ def after(tag = nil, opts = {}, &block)
55
+ return @after.sort_by(&:rank) if block.nil?
56
+
57
+ @after << Callback.new(callback_name(:after, tag), opts, &block)
58
+ end
59
+
60
+ def raised(tag = nil, opts = {}, &block)
61
+ return @raised.sort_by(&:rank) if block.nil?
62
+
63
+ @raised << Callback.new(callback_name(:raised, tag), opts, &block)
64
+ end
65
+
66
+ def ensured(tag = nil, opts = {}, &block)
67
+ return @ensured.sort_by(&:rank) if block.nil?
68
+
69
+ @ensured << Callback.new(callback_name(:ensured, tag), opts, &block)
70
+ end
71
+
72
+ def depends_on(&block)
73
+ @dependency_test = block
74
+ end
75
+
76
+ def enable
77
+ @disabled = false
78
+ end
79
+
80
+ def disable
81
+ @disabled = true
82
+ end
83
+
84
+ def disabled?
85
+ @disabled
86
+ end
87
+
88
+ def install
89
+ unless point.exist?
90
+ Sqreen::Graft.logger.debug { "[#{Process.pid}] #{point} not found" }
91
+ return
92
+ end
93
+ Sqreen::Graft.logger.debug { "[#{Process.pid}] Hook #{point}: installing" }
94
+
95
+ point.install('sqreen_hook', &Sqreen::Graft::Hook.wrapper(self))
96
+ end
97
+
98
+ def uninstall
99
+ unless point.exist?
100
+ Sqreen::Graft.logger.debug { "[#{Process.pid}] #{point} not found" }
101
+ return
102
+ end
103
+ Sqreen::Graft.logger.debug { "[#{Process.pid}] Hook #{point}: uninstalling" }
104
+
105
+ point.uninstall('sqreen_hook', &Sqreen::Graft::Hook.wrapper(self))
106
+ end
107
+
108
+ def clear
109
+ @before = []
110
+ @after = []
111
+ @raised = []
112
+ end
113
+
114
+ def self.wrapper(hook)
115
+ Proc.new do |*args, &block|
116
+ if Thread.current[:sqreen_hook_entered] || Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:time_budget_expended]
117
+ if hook.point.super?
118
+ return super(*args, &block)
119
+ else
120
+ return hook.point.apply(self, 'sqreen_hook', *args, &block)
121
+ end
122
+ end
123
+
124
+ Timer.new(hook.point) do |t|
125
+ Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:timed_hooks] << t
126
+ end.measure do |chrono|
127
+ Sqreen::Graft.logger.debug { "[#{Process.pid}] Hook #{hook.point} disabled:#{hook.disabled?} caller:#{Kernel.caller[2].inspect}" }
128
+
129
+ budget = Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:time_budget]
130
+ timer = Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:timer] if budget
131
+ hooked_call = HookedCall.new(self, args)
132
+
133
+ begin
134
+ timer.start if timer
135
+ Thread.current[:sqreen_hook_entered] = true
136
+
137
+ # TODO: make Call have #ball to throw by cb
138
+ # TODO: can Call be the ball? r = catch(Call.new, &c)
139
+ # TODO: is catch return value a Call? a #dispatch?
140
+ # TODO: make before/after/raised return a CallbackCollection << Array (or extend with module)
141
+ # TODO: add CallbackCollection#each_with_call(instance, args) { |call| ... } ?
142
+ # TODO: HookCall x CallbackCollection#each_with_call x Flow
143
+ # TODO: TimedHookCall TimedCallbackCall
144
+ # TODO: TimeBoundHookCall TimeBoundCallbackCall TimeBoundFlow?
145
+
146
+ Timer.new("#{hook.point}@before") do |t|
147
+ Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:timed_hooks_before] << t
148
+ end.measure do |before_chrono|
149
+ hook.before.each do |c|
150
+ next if c.ignore && c.ignore.call
151
+
152
+ if timer && !c.mandatory
153
+ remaining = budget - timer.elapsed
154
+ unless remaining > 0
155
+ Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:skipped_callbacks] << c && Thread.current[:sqreen_http_request][:time_budget_expended] = true
156
+ next
157
+ end
158
+ end
159
+
160
+ flow = catch(Ball.new) do |ball|
161
+ Timer.new(c.name) do |t|
162
+ Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:timed_callbacks] << t
163
+ end.measure do
164
+ before_chrono.ignore do
165
+ c.call(CallbackCall.new(c, remaining, hooked_call.instance, hooked_call.args_passed), ball)
166
+ end
167
+ end
168
+ end
169
+
170
+ next unless c.flow && flow.is_a?(Flow)
171
+ hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
172
+ hooked_call.args_pass = flow.args and hooked_call.args_passing = true if flow.args?
173
+ hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
174
+ break if flow.break?
175
+ end unless hook.disabled?
176
+ end
177
+
178
+ raise hooked_call.raise if hooked_call.raising
179
+ return hooked_call.return if hooked_call.returning
180
+ ensure
181
+ Thread.current[:sqreen_hook_entered] = false
182
+ timer.stop if timer
183
+ end
184
+
185
+ begin
186
+ chrono.ignore do
187
+ if hook.point.super?
188
+ hooked_call.returned = super(*(hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed), &block)
189
+ else
190
+ hooked_call.returned = hook.point.apply(hooked_call.instance, 'sqreen_hook', *(hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed), &block)
191
+ end
192
+ end
193
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
194
+ timer.start if timer
195
+ Thread.current[:sqreen_hook_entered] = true
196
+ hooked_call.raised = e
197
+
198
+ Sqreen::Graft.logger.debug { "[#{Process.pid}] Hook #{hook.point} disabled:#{hook.disabled?} exception:#{e}" }
199
+ raise if hook.raised.empty?
200
+
201
+ Timer.new("#{hook.point}@raised") do |t|
202
+ Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:timed_hooks_raised] << t
203
+ end.measure do |raised_chrono|
204
+ hook.raised.each do |c|
205
+ next if c.ignore && c.ignore.call
206
+
207
+ if timer && !c.mandatory
208
+ remaining = budget - timer.elapsed
209
+ unless remaining > 0
210
+ Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:skipped_callbacks] << c && Thread.current[:sqreen_http_request][:time_budget_expended] = true
211
+ next
212
+ end
213
+ end
214
+
215
+ flow = catch(Ball.new) do |ball|
216
+ Timer.new(c.name) do |t|
217
+ Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:timed_callbacks] << t
218
+ end.measure do
219
+ raised_chrono.ignore do
220
+ c.call(CallbackCall.new(c, remaining, hooked_call.instance, hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed, hooked_call.raised), ball)
221
+ end
222
+ end
223
+ end
224
+
225
+ next unless c.flow && flow.is_a?(Flow)
226
+ hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
227
+ hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
228
+ hooked_call.retrying = true if flow.retry?
229
+ break if flow.break?
230
+ end unless hook.disabled?
231
+ end
232
+
233
+ retry if hooked_call.retrying
234
+ raise hooked_call.raise if hooked_call.raising
235
+ return hooked_call.return if hooked_call.returning
236
+ raise
237
+ else
238
+ timer.start if timer
239
+ Thread.current[:sqreen_hook_entered] = true
240
+
241
+ Timer.new("#{hook.point}@after") do |t|
242
+ Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:timed_hooks_after] << t
243
+ end.measure do |after_chrono|
244
+ hook.after.each do |c|
245
+ next if c.ignore && c.ignore.call
246
+
247
+ if timer && !c.mandatory
248
+ remaining = budget - timer.elapsed
249
+ unless remaining > 0
250
+ Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:skipped_callbacks] << c && Thread.current[:sqreen_http_request][:time_budget_expended] = true
251
+ next
252
+ end
253
+ end
254
+
255
+ flow = catch(Ball.new) do |ball|
256
+ Timer.new(c.name) do |t|
257
+ Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:timed_callbacks] << t
258
+ end.measure do
259
+ after_chrono.ignore do
260
+ c.call(CallbackCall.new(c, remaining, hooked_call.instance, hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed, nil, hooked_call.returned), ball)
261
+ end
262
+ end
263
+ end
264
+
265
+ next unless c.flow && flow.is_a?(Flow)
266
+ hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
267
+ hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
268
+ break if flow.break?
269
+ end unless hook.disabled?
270
+ end
271
+
272
+ raise hooked_call.raise if hooked_call.raising
273
+ return hooked_call.returning ? hooked_call.return : hooked_call.returned
274
+ ensure
275
+ # TODO: timer.start if someone has thrown?
276
+
277
+ Timer.new("#{hook.point}@ensured") do |t|
278
+ Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:timed_hooks_ensured] << t
279
+ end.measure do |ensured_chrono|
280
+ hook.ensured.each do |c|
281
+ next if c.ignore && c.ignore.call
282
+
283
+ if timer && !c.mandatory
284
+ remaining = budget - timer.elapsed
285
+ unless remaining > 0
286
+ Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:skipped_callbacks] << c && Thread.current[:sqreen_http_request][:time_budget_expended] = true
287
+ next
288
+ end
289
+ end
290
+
291
+ flow = catch(Ball.new) do |ball|
292
+ Timer.new(c.name) do |t|
293
+ Thread.current[:sqreen_http_request] && Thread.current[:sqreen_http_request][:timed_callbacks] << t
294
+ end.measure do
295
+ ensured_chrono.ignore do
296
+ c.call(CallbackCall.new(c, remaining, hooked_call.instance, hooked_call.args_passing ? hooked_call.args_pass : hooked_call.args_passed, nil, hooked_call.returned), ball)
297
+ end
298
+ end
299
+ end
300
+
301
+ next unless c.flow && flow.is_a?(Flow)
302
+ hooked_call.raise = flow.raise and hooked_call.raising = true if flow.raise?
303
+ hooked_call.return = flow.return and hooked_call.returning = true if flow.return?
304
+ break if flow.break?
305
+ end unless hook.disabled?
306
+ end
307
+
308
+ Thread.current[:sqreen_hook_entered] = false
309
+ timer.stop if timer
310
+ end
311
+ end # chrono
312
+ end
313
+ end
314
+ end
315
+ end
316
+ end
@@ -1,12 +1,26 @@
1
+ # typed: false
2
+
1
3
  # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
4
  # Please refer to our terms for more information: https://www.sqreen.com/terms.html
3
5
 
4
6
  require 'sqreen/dependency'
5
7
  require 'sqreen/backport/original_name'
8
+ require 'sqreen/graft'
9
+ require 'sqreen/graft/hook_point_error'
6
10
 
7
11
  module Sqreen
8
- module Dependency
9
- class HookPointError < StandardError; end
12
+ module Graft
13
+ class HookSpot < Module
14
+ def initialize(key)
15
+ @key = key
16
+ end
17
+
18
+ attr_reader :key
19
+
20
+ def inspect
21
+ "#<#{self.class.name}: #{@key.inspect}>"
22
+ end
23
+ end
10
24
 
11
25
  class HookPoint
12
26
  def self.parse(hook_point)
@@ -22,8 +36,9 @@ module Sqreen
22
36
 
23
37
  attr_reader :klass_name, :method_kind, :method_name
24
38
 
25
- def initialize(hook_point)
26
- @klass_name, @method_kind, @method_name = Sqreen::Dependency::HookPoint.parse(hook_point)
39
+ def initialize(hook_point, strategy = :chain)
40
+ @klass_name, @method_kind, @method_name = Sqreen::Graft::HookPoint.parse(hook_point)
41
+ @strategy = strategy
27
42
  end
28
43
 
29
44
  def to_s
@@ -34,11 +49,12 @@ module Sqreen
34
49
  return false unless Sqreen::Dependency.const_exist?(@klass_name)
35
50
 
36
51
  if klass_method?
37
- (klass.methods + klass.protected_methods + klass.private_methods).include?(@method_name)
52
+ (klass.singleton_class.public_instance_methods(false) + klass.singleton_class.protected_instance_methods(false) + klass.singleton_class.private_instance_methods(false)).include?(@method_name)
38
53
  elsif instance_method?
39
- (klass.instance_methods + klass.protected_instance_methods + klass.private_instance_methods).include?(@method_name)
54
+ (klass.public_instance_methods(false) + klass.protected_instance_methods(false) + klass.private_instance_methods(false)).include?(@method_name)
40
55
  else
41
- raise HookPointError, 'unknown hook point kind'
56
+ Sqreen::Graft.logger.error { "[#{Process.pid}] #{self} unknown hook point kind" }
57
+ raise HookPointError, "#{self} unknown hook point kind"
42
58
  end
43
59
  end
44
60
 
@@ -56,7 +72,8 @@ module Sqreen
56
72
  elsif instance_method?
57
73
  klass.private_instance_methods.include?(@method_name)
58
74
  else
59
- raise HookPointError, 'unknown hook point kind'
75
+ Sqreen::Graft.logger.error { "[#{Process.pid}] #{self} unknown hook point kind" }
76
+ raise HookPointError, "#{self} unknown hook point kind"
60
77
  end
61
78
  end
62
79
 
@@ -66,7 +83,8 @@ module Sqreen
66
83
  elsif instance_method?
67
84
  klass.protected_instance_methods.include?(@method_name)
68
85
  else
69
- raise HookPointError, 'unknown hook point kind'
86
+ Sqreen::Graft.logger.error { "[#{Process.pid}] #{self} unknown hook point kind" }
87
+ raise HookPointError, "#{self} unknown hook point kind"
70
88
  end
71
89
  end
72
90
 
@@ -74,58 +92,159 @@ module Sqreen
74
92
  @method_kind == :instance_method
75
93
  end
76
94
 
77
- def installed?(suffix)
78
- if klass_method?
79
- (klass.methods + klass.protected_methods + klass.private_methods).include?(:"#{method_name}_with_#{suffix}")
80
- elsif instance_method?
81
- (klass.instance_methods + klass.protected_instance_methods + klass.private_instance_methods).include?(:"#{method_name}_with_#{suffix}")
82
- else
83
- raise HookPointError, 'unknown hook point kind'
95
+ def installed?(key)
96
+ case @strategy
97
+ when :chain then defined(key)
98
+ when :prepend then prepended?(key) && overridden?(key)
84
99
  end
85
100
  end
86
101
 
102
+ def super?
103
+ @strategy == :prepend
104
+ end
105
+
87
106
  def apply(obj, suffix, *args, &block)
107
+ raise 'use super' if super?
108
+
88
109
  obj.send("#{method_name}_without_#{suffix}", *args, &block)
89
110
  end
90
111
 
91
- def install(suffix, &block)
92
- if installed?(suffix)
93
- Sqreen.log.debug "[#{Process.pid}] #{self} already installed"
112
+ def install(key, &block)
113
+ if installed?(key)
114
+ Sqreen::Graft.logger.debug { "[#{Process.pid}] #{self} already installed" }
115
+ return
94
116
  end
95
117
  unless exist?
96
- Sqreen.log.debug "[#{Process.pid}] #{self} hook point not found"
118
+ Sqreen::Graft.logger.debug { "[#{Process.pid}] #{self} hook point not found" }
119
+ return
97
120
  end
98
121
 
99
- define(suffix, &block)
100
- enable(suffix)
122
+ case @strategy
123
+ when :chain
124
+ define(key, &block)
125
+ chain(key)
126
+ when :prepend
127
+ prepend(key)
128
+ override(key, &block)
129
+ end
101
130
  end
102
131
 
103
- def uninstall(suffix)
104
- disable(suffix)
105
- remove(suffix)
132
+ def uninstall(key)
133
+ unless installed?(key)
134
+ Sqreen::Graft.logger.debug { "[#{Process.pid}] #{self} not installed" }
135
+ return
136
+ end
137
+ unless exist?
138
+ Sqreen::Graft.logger.debug { "[#{Process.pid}] #{self} hook point not found" }
139
+ return
140
+ end
141
+
142
+ case @strategy
143
+ when :chain
144
+ disable(key)
145
+ remove(key)
146
+ when :prepend
147
+ unoverride(key) if overridden?(key)
148
+ end
106
149
  end
107
150
 
108
- def enable(suffix)
109
- chain(suffix)
151
+ def enable(key)
152
+ case @strategy
153
+ when :chain
154
+ chain(key)
155
+ when :prepend
156
+ raise HookPointError, "enable called on prepend mode"
157
+ end
110
158
  end
111
159
 
112
- def disable(suffix)
113
- unchain(suffix)
160
+ def disable(key)
161
+ case @strategy
162
+ when :chain
163
+ unchain(key)
164
+ when :prepend
165
+ unoverride(key)
166
+ end
114
167
  end
115
168
 
116
- def disabled?(suffix)
117
- !chained?(suffix)
169
+ def disabled?(key)
170
+ case @strategy
171
+ when :chain
172
+ !chained?(key)
173
+ when :prepend
174
+ !overridden?(key)
175
+ end
118
176
  end
119
177
 
120
178
  private
121
179
 
180
+ def prepend(key)
181
+ target = klass_method? ? klass.singleton_class : klass
182
+ mod = target.ancestors.each { |e| break if e == target; break(e) if e.class == HookSpot && e.key == key }
183
+ mod ||= HookSpot.new(key)
184
+ target.instance_eval { prepend(mod) }
185
+ end
186
+
187
+ def prepended?(key)
188
+ target = klass_method? ? klass.singleton_class : klass
189
+ mod = target.ancestors.each { |e| break if e == target; break(e) if e.class == HookSpot && e.key == key }
190
+
191
+ mod != nil
192
+ end
193
+
194
+ def overridden?(key)
195
+ target = klass_method? ? klass.singleton_class : klass
196
+ mod = target.ancestors.each { |e| break if e == target; break(e) if e.class == HookSpot && e.key == key }
197
+
198
+ (mod.instance_methods(false) + mod.protected_instance_methods(false) + mod.private_instance_methods(false)).include?(method_name)
199
+ end
200
+
201
+ def override(key, &block)
202
+ hook_point = self
203
+ method_name = @method_name
204
+
205
+ target = klass_method? ? klass.singleton_class : klass
206
+ mod = target.ancestors.each { |e| break if e == target; break(e) if e.class == HookSpot && e.key == key }
207
+
208
+ mod.instance_eval do
209
+ if hook_point.private_method?
210
+ private
211
+ elsif hook_point.protected_method?
212
+ protected
213
+ else
214
+ public
215
+ end
216
+
217
+ define_method(:"#{method_name}", &block)
218
+ end
219
+ end
220
+
221
+ def unoverride(key)
222
+ method_name = @method_name
223
+
224
+ target = klass_method? ? klass.singleton_class : klass
225
+ mod = target.ancestors.each { |e| break if e == target; break(e) if e.class == HookSpot && e.key == key }
226
+
227
+ mod.instance_eval { remove_method(method_name) }
228
+ end
229
+
230
+ def defined(suffix)
231
+ if klass_method?
232
+ (klass.methods + klass.protected_methods + klass.private_methods).include?(:"#{method_name}_with_#{suffix}")
233
+ elsif instance_method?
234
+ (klass.instance_methods + klass.protected_instance_methods + klass.private_instance_methods).include?(:"#{method_name}_with_#{suffix}")
235
+ else
236
+ Sqreen::Graft.logger.error { "[#{Process.pid}] #{self} unknown hook point kind" }
237
+ raise HookPointError, "#{self} unknown hook point kind"
238
+ end
239
+ end
240
+
122
241
  def define(suffix, &block)
123
242
  hook_point = self
124
243
  method_name = @method_name
125
244
 
126
245
  if klass_method?
127
246
  klass.singleton_class.instance_eval do
128
- if hook_point.protected_method?
247
+ if hook_point.private_method?
129
248
  private
130
249
  elsif hook_point.protected_method?
131
250
  protected
@@ -137,7 +256,7 @@ module Sqreen
137
256
  end
138
257
  elsif instance_method?
139
258
  klass.class_eval do
140
- if hook_point.protected_method?
259
+ if hook_point.private_method?
141
260
  private
142
261
  elsif hook_point.protected_method?
143
262
  protected