sqreen 1.18.2-java → 1.19.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 (184) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -0
  3. data/LICENSE +3 -0
  4. data/lib/sqreen.rb +2 -0
  5. data/lib/sqreen/actions.rb +13 -337
  6. data/lib/sqreen/actions/actions_index.rb +16 -0
  7. data/lib/sqreen/actions/base.rb +104 -0
  8. data/lib/sqreen/actions/block_ip.rb +34 -0
  9. data/lib/sqreen/actions/block_user.rb +46 -0
  10. data/lib/sqreen/actions/ip_range_indexed_action_class.rb +16 -0
  11. data/lib/sqreen/actions/ip_ranges_index.rb +57 -0
  12. data/lib/sqreen/actions/redirect_ip.rb +42 -0
  13. data/lib/sqreen/actions/redirect_user.rb +47 -0
  14. data/lib/sqreen/actions/repository.rb +43 -0
  15. data/lib/sqreen/actions/unknown_action_type.rb +20 -0
  16. data/lib/sqreen/actions/user_action_class.rb +16 -0
  17. data/lib/sqreen/actions/users_index.rb +35 -0
  18. data/lib/sqreen/agent.rb +6 -2
  19. data/lib/sqreen/attack_blocked.rb +19 -0
  20. data/lib/sqreen/backport.rb +2 -0
  21. data/lib/sqreen/backport/clock_gettime.rb +74 -0
  22. data/lib/sqreen/backport/original_name.rb +2 -0
  23. data/lib/sqreen/binding_accessor.rb +11 -102
  24. data/lib/sqreen/binding_accessor/path_elem.rb +10 -0
  25. data/lib/sqreen/binding_accessor/transforms.rb +114 -0
  26. data/lib/sqreen/call_countable.rb +2 -0
  27. data/lib/sqreen/capped_queue.rb +4 -0
  28. data/lib/sqreen/{callbacks.rb → cb.rb} +3 -53
  29. data/lib/sqreen/{callback_tree.rb → cb_tree.rb} +4 -2
  30. data/lib/sqreen/condition_evaluator.rb +24 -5
  31. data/lib/sqreen/conditionable.rb +2 -0
  32. data/lib/sqreen/configuration.rb +19 -0
  33. data/lib/sqreen/context.rb +2 -0
  34. data/lib/sqreen/default_cb.rb +22 -0
  35. data/lib/sqreen/deferred_logger.rb +65 -0
  36. data/lib/sqreen/deliveries.rb +12 -0
  37. data/lib/sqreen/deliveries/batch.rb +9 -1
  38. data/lib/sqreen/deliveries/simple.rb +7 -0
  39. data/lib/sqreen/dependency.rb +3 -1
  40. data/lib/sqreen/dependency/detector.rb +22 -14
  41. data/lib/sqreen/dependency/libsqreen.rb +32 -0
  42. data/lib/sqreen/dependency/new_relic.rb +2 -0
  43. data/lib/sqreen/dependency/rack.rb +10 -5
  44. data/lib/sqreen/dependency/rails.rb +8 -0
  45. data/lib/sqreen/dependency/sentry.rb +2 -0
  46. data/lib/sqreen/dependency/sinatra.rb +58 -14
  47. data/lib/sqreen/encoding_sanitizer.rb +2 -0
  48. data/lib/sqreen/error_handling_middleware.rb +32 -0
  49. data/lib/sqreen/event.rb +4 -0
  50. data/lib/sqreen/events/attack.rb +4 -0
  51. data/lib/sqreen/events/remote_exception.rb +2 -0
  52. data/lib/sqreen/events/request_record.rb +13 -56
  53. data/lib/sqreen/exception.rb +11 -40
  54. data/lib/sqreen/formatter_with_tid.rb +47 -0
  55. data/lib/sqreen/framework_cb.rb +30 -0
  56. data/lib/sqreen/frameworks.rb +9 -0
  57. data/lib/sqreen/frameworks/generic.rb +22 -2
  58. data/lib/sqreen/frameworks/rails.rb +3 -0
  59. data/lib/sqreen/frameworks/rails3.rb +2 -0
  60. data/lib/sqreen/frameworks/request_recorder.rb +5 -0
  61. data/lib/sqreen/frameworks/sinatra.rb +4 -0
  62. data/lib/sqreen/frameworks/sqreen_test.rb +4 -0
  63. data/lib/sqreen/graft.rb +12 -0
  64. data/lib/sqreen/graft/call.rb +150 -0
  65. data/lib/sqreen/{dependency → graft}/callback.rb +12 -4
  66. data/lib/sqreen/graft/hook.rb +316 -0
  67. data/lib/sqreen/{dependency → graft}/hook_point.rb +152 -33
  68. data/lib/sqreen/graft/hook_point_error.rb +10 -0
  69. data/lib/sqreen/invalid_signature_exception.rb +10 -0
  70. data/lib/sqreen/js.rb +11 -0
  71. data/lib/sqreen/js/call_context.rb +12 -0
  72. data/lib/sqreen/js/context_pool.rb +62 -0
  73. data/lib/sqreen/js/exec_js_runnable.rb +22 -0
  74. data/lib/sqreen/js/execjs_adapter.rb +8 -47
  75. data/lib/sqreen/js/executable_js.rb +14 -0
  76. data/lib/sqreen/js/js_service.rb +4 -22
  77. data/lib/sqreen/js/js_service_adapter.rb +20 -0
  78. data/lib/sqreen/js/mini_racer_adapter.rb +8 -180
  79. data/lib/sqreen/js/mini_racer_executable_js.rb +144 -0
  80. data/lib/sqreen/js/thread_local_exec_js_runnable.rb +49 -0
  81. data/lib/{sqreen-alt.rb → sqreen/legacy.rb} +5 -1
  82. data/lib/sqreen/{instrumentation.rb → legacy/instrumentation.rb} +44 -15
  83. data/lib/sqreen/log.rb +10 -188
  84. data/lib/sqreen/log/loggable.rb +28 -0
  85. data/lib/sqreen/logger.rb +85 -0
  86. data/lib/sqreen/metrics.rb +2 -0
  87. data/lib/sqreen/metrics/average.rb +2 -0
  88. data/lib/sqreen/metrics/base.rb +2 -0
  89. data/lib/sqreen/metrics/binning.rb +2 -0
  90. data/lib/sqreen/metrics/collect.rb +2 -0
  91. data/lib/sqreen/metrics/sum.rb +2 -0
  92. data/lib/sqreen/metrics_store.rb +5 -11
  93. data/lib/sqreen/metrics_store/already_registered_metric.rb +13 -0
  94. data/lib/sqreen/metrics_store/unknown_metric.rb +13 -0
  95. data/lib/sqreen/metrics_store/unregistered_metric.rb +13 -0
  96. data/lib/sqreen/middleware.rb +2 -34
  97. data/lib/sqreen/mono_time.rb +4 -0
  98. data/lib/sqreen/node.rb +46 -0
  99. data/lib/sqreen/not_implemented_yet.rb +10 -0
  100. data/lib/sqreen/null_logger.rb +26 -0
  101. data/lib/sqreen/payload_creator.rb +4 -19
  102. data/lib/sqreen/payload_creator/header_section.rb +30 -0
  103. data/lib/sqreen/performance_notifications.rb +2 -0
  104. data/lib/sqreen/performance_notifications/binned_metrics.rb +2 -0
  105. data/lib/sqreen/performance_notifications/log.rb +2 -0
  106. data/lib/sqreen/performance_notifications/log_performance.rb +2 -0
  107. data/lib/sqreen/performance_notifications/metrics.rb +2 -0
  108. data/lib/sqreen/performance_notifications/newrelic.rb +2 -0
  109. data/lib/sqreen/prefix.rb +35 -0
  110. data/lib/sqreen/rails_middleware.rb +16 -0
  111. data/lib/sqreen/remote_command.rb +3 -8
  112. data/lib/sqreen/remote_command/failure_output.rb +16 -0
  113. data/lib/sqreen/rules.rb +34 -2
  114. data/lib/sqreen/{rule_attributes.rb → rules/attrs.rb} +2 -0
  115. data/lib/sqreen/{rules_callbacks/sdk_auth_track.rb → rules/auth_track_cb.rb} +4 -2
  116. data/lib/sqreen/{rules_callbacks/binding_accessor_matcher.rb → rules/binding_accessor_matcher_cb.rb} +6 -8
  117. data/lib/sqreen/{rules_callbacks → rules}/binding_accessor_metrics.rb +3 -1
  118. data/lib/sqreen/{rules_callbacks/blacklist_ips.rb → rules/blacklist_ips_cb.rb} +5 -2
  119. data/lib/sqreen/{rules_callbacks → rules}/count_http_codes.rb +4 -2
  120. data/lib/sqreen/{rules_callbacks/crawler_user_agent_matches.rb → rules/crawler_user_agent_matches_cb.rb} +3 -1
  121. data/lib/sqreen/{rules_callbacks/crawler_user_agent_matches_metrics.rb → rules/crawler_user_agent_matches_metrics_cb.rb} +3 -1
  122. data/lib/sqreen/{rules_callbacks/custom_error.rb → rules/custom_error_cb.rb} +3 -1
  123. data/lib/sqreen/{rules_callbacks/devise_auth_track.rb → rules/devise_auth_track_cb.rb} +4 -2
  124. data/lib/sqreen/{rules_callbacks/devise_signup_track.rb → rules/devise_signup_track_cb.rb} +4 -2
  125. data/lib/sqreen/{rules_callbacks/execjs.rb → rules/execjs_cb.rb} +51 -50
  126. data/lib/sqreen/{rules_callbacks/headers_insert.rb → rules/headers_insert_cb.rb} +8 -1
  127. data/lib/sqreen/{rules_callbacks → rules}/matcher_rule.rb +4 -2
  128. data/lib/sqreen/{rules_callbacks/not_found.rb → rules/not_found_cb.rb} +7 -2
  129. data/lib/sqreen/{rules_callbacks/rails_parameters.rb → rules/rails_parameters_cb.rb} +3 -1
  130. data/lib/sqreen/{rules_callbacks → rules}/record_request_context.rb +3 -1
  131. data/lib/sqreen/{rules_callbacks/regexp_rule.rb → rules/regexp_rule_cb.rb} +3 -1
  132. data/lib/sqreen/{rule_callback.rb → rules/rule_cb.rb} +4 -2
  133. data/lib/sqreen/{rules_callbacks → rules}/run_req_start_actions.rb +7 -3
  134. data/lib/sqreen/{rules_callbacks → rules}/run_user_actions.rb +4 -2
  135. data/lib/sqreen/{rules_callbacks/shell_env.rb → rules/shell_env_cb.rb} +3 -1
  136. data/lib/sqreen/{rules_callbacks/sdk_signup_track.rb → rules/signup_track_cb.rb} +4 -2
  137. data/lib/sqreen/rules/update_request_context.rb +22 -0
  138. data/lib/sqreen/{rules_callbacks/url_matches.rb → rules/url_matches_cb.rb} +3 -1
  139. data/lib/sqreen/{rules_callbacks/user_agent_matches.rb → rules/user_agent_matches_cb.rb} +3 -1
  140. data/lib/sqreen/{rules_callbacks/waf.rb → rules/waf_cb.rb} +41 -21
  141. data/lib/sqreen/{rules_callbacks/reflected_xss.rb → rules/xss_cb.rb} +12 -7
  142. data/lib/sqreen/run_when_called_cb.rb +23 -0
  143. data/lib/sqreen/runner.rb +25 -7
  144. data/lib/sqreen/runtime_infos.rb +4 -9
  145. data/lib/sqreen/safe_json.rb +2 -0
  146. data/lib/sqreen/sdk.rb +4 -0
  147. data/lib/sqreen/sensitive_data_redactor.rb +113 -0
  148. data/lib/sqreen/serializer.rb +2 -0
  149. data/lib/sqreen/session.rb +2 -0
  150. data/lib/sqreen/shared_storage.rb +2 -0
  151. data/lib/sqreen/shared_storage23.rb +2 -0
  152. data/lib/sqreen/shrink_wrap.rb +16 -0
  153. data/lib/sqreen/signature_verifier.rb +22 -0
  154. data/lib/sqreen/sinatra_middleware.rb +16 -0
  155. data/lib/sqreen/{rules_signature.rb → sqreen_signed_verifier.rb} +7 -17
  156. data/lib/sqreen/token_invalid_exception.rb +10 -0
  157. data/lib/sqreen/token_not_found_exception.rb +11 -0
  158. data/lib/sqreen/trie.rb +5 -64
  159. data/lib/sqreen/unauthorized.rb +10 -0
  160. data/lib/sqreen/util.rb +7 -0
  161. data/lib/sqreen/util/capped_array.rb +35 -0
  162. data/lib/sqreen/util/capped_hash.rb +41 -0
  163. data/lib/sqreen/util/capped_string.rb +26 -0
  164. data/lib/sqreen/util/capper.rb +67 -0
  165. data/lib/sqreen/version.rb +3 -1
  166. data/lib/sqreen/waf_error.rb +20 -0
  167. data/lib/sqreen/weave.rb +12 -0
  168. data/lib/sqreen/weave/hardcoded.rb +19 -0
  169. data/lib/sqreen/weave/instrumentor.rb +48 -0
  170. data/lib/sqreen/weave/legacy.rb +12 -0
  171. data/lib/sqreen/weave/legacy/instrumentation.rb +406 -0
  172. data/lib/sqreen/web_server.rb +2 -0
  173. data/lib/sqreen/web_server/generic.rb +2 -0
  174. data/lib/sqreen/web_server/passenger.rb +2 -0
  175. data/lib/sqreen/web_server/puma.rb +2 -0
  176. data/lib/sqreen/web_server/rainbows.rb +2 -0
  177. data/lib/sqreen/web_server/thin.rb +2 -0
  178. data/lib/sqreen/web_server/unicorn.rb +2 -0
  179. data/lib/sqreen/web_server/webrick.rb +2 -0
  180. data/lib/sqreen/worker.rb +2 -0
  181. metadata +105 -39
  182. data/lib/sqreen/dependency/hook.rb +0 -102
  183. data/lib/sqreen/rules_callbacks.rb +0 -35
  184. data/lib/sqreen/rules_callbacks/inspect_rule.rb +0 -25
@@ -0,0 +1,14 @@
1
+ # typed: true
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
+ module Sqreen
7
+ module Js
8
+ class ExecutableJs
9
+ def run_js_cb(_cb_name, _budget, _arguments)
10
+ raise Sqreen::NotImplementedYet
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,12 +1,14 @@
1
+ # typed: ignore
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
 
6
+ require 'sqreen/log'
4
7
  require 'sqreen/exception'
8
+ require 'singleton'
5
9
 
6
10
  module Sqreen
7
11
  module Js
8
- # start public interface
9
-
10
12
  class JsService
11
13
  include ::Singleton
12
14
 
@@ -66,25 +68,5 @@ module Sqreen
66
68
  false
67
69
  end
68
70
  end
69
-
70
- CallContext = Struct.new(:inst, :args, :rv)
71
-
72
- class ExecutableJs
73
- def run_js_cb(_cb_name, _budget, _arguments)
74
- raise Sqreen::NotImplementedYet
75
- end
76
- end
77
-
78
- # end public interface
79
-
80
- class JsServiceAdapter
81
- def preprocess(rule_name, code)
82
- raise Sqreen::NotImplementedYet
83
- end
84
-
85
- def variant_name
86
- 'unspecified'
87
- end
88
- end
89
71
  end
90
72
  end
@@ -0,0 +1,20 @@
1
+ # typed: true
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/exception'
7
+
8
+ module Sqreen
9
+ module Js
10
+ class JsServiceAdapter
11
+ def preprocess(_rule_name, _code)
12
+ raise Sqreen::NotImplementedYet
13
+ end
14
+
15
+ def variant_name
16
+ 'unspecified'
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,14 +1,21 @@
1
+ # typed: ignore
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
 
6
+ # TODO: => Sqreen::JS:MiniRacer
7
+
4
8
  require 'digest'
5
9
  require 'json'
10
+ require 'sqreen/js/executable_js'
11
+ require 'sqreen/js/js_service_adapter'
12
+ require 'sqreen/js/context_pool'
13
+ require 'sqreen/js/mini_racer_executable_js'
6
14
 
7
15
  module Sqreen
8
16
  module Js
9
17
  DEFAULT_GC_THRESHOLD = 15000000 # 15 MB
10
18
 
11
-
12
19
  class MiniRacerAdapter < JsServiceAdapter
13
20
  def initialize(vendored = false)
14
21
  @vendored = vendored
@@ -30,184 +37,5 @@ module Sqreen
30
37
  @done_static_init = true
31
38
  end
32
39
  end
33
-
34
- # Auxiliary classes
35
-
36
- class ContextPool
37
- def initialize
38
- @mutex = Mutex.new
39
- @total_ctxs = 0
40
- @contexts = []
41
- end
42
-
43
- def with_context(&block)
44
- isolate = get_context
45
- begin
46
- block[isolate]
47
- ensure
48
- give_back_context isolate
49
- end
50
- end
51
-
52
- private
53
-
54
- def get_context
55
- @mutex.synchronize do
56
- if @contexts.empty?
57
- @total_ctxs += 1
58
- Sqreen.log.debug "Creating new V8 context (#{@total_ctxs})"
59
- SqreenContext.new
60
- else
61
- @contexts.pop
62
- end
63
- end
64
- end
65
-
66
- def give_back_context(context)
67
- context.possibly_gc
68
-
69
- if context.gc_load > 30
70
- if context.gc_threshold_in_bytes == DEFAULT_GC_THRESHOLD
71
- context.gc_threshold_in_bytes *= 2
72
- Sqreen.log.warn("Context #{context} had too many close garbage " \
73
- 'collections; doubling the threshold to ' \
74
- "#{context.gc_threshold_in_bytes} bytes")
75
- context.gc_load = 0
76
- else
77
- Sqreen.log.warn("Context #{context} had too many close garbage " \
78
- 'collections; discarding it')
79
- context.dispose
80
- return
81
- end
82
- end
83
-
84
- @mutex.synchronize { @contexts.push(context); }
85
- end
86
- end
87
-
88
- class MiniRacerExecutableJs < ExecutableJs
89
- @@ctx_defined = false
90
-
91
- def initialize(pool, code, vendored)
92
- @pool = pool
93
- @code = code
94
- @code_id = self.class.code_id(code)
95
-
96
- @module = vendored ? Sqreen::MiniRacer : MiniRacer
97
-
98
- mod = vendored ? Sqreen::MiniRacer : MiniRacer
99
- unless @@ctx_defined
100
- self.class.define_sqreen_context(mod)
101
- @@ctx_defined = true
102
- end
103
- end
104
-
105
- def run_js_cb(cb_name, budget, arguments)
106
- @pool.with_context do |ctx|
107
- if ctx.code_failed?(@code_id)
108
- Sqreen.log.debug do
109
- "Skipping execution of callback #{cb_name} (code md5 #{@code_id})" \
110
- " due to prev failure of definition evaluation"
111
- end
112
- return nil
113
- end
114
-
115
- ctx.add_code(@code_id, @code) unless ctx.has_code?(@code_id)
116
-
117
- # mini_racer expects timeout to be in ms
118
- ctx.timeout = budget ? budget * 1000.0 : nil
119
- begin
120
- ctx.call("sqreen_#{@code_id}_#{cb_name}", *arguments)
121
- rescue @module::ScriptTerminatedError
122
- Sqreen.log.debug "ScriptTerminatedError/#{cb_name}"
123
- nil
124
- end
125
- end
126
- end
127
-
128
- def self.code_id(code)
129
- Digest::MD5.hexdigest(code)
130
- end
131
-
132
- private
133
-
134
- class << self
135
- def define_sqreen_context(modoole)
136
- # Context specialized for Sqreen usage
137
- Sqreen::Js.const_set 'SqreenContext', Class.new(modoole.const_get('Context'))
138
- SqreenContext.class_eval do
139
- attr_accessor :gc_threshold_in_bytes
140
- attr_accessor :gc_load
141
- attr_writer :timeout
142
-
143
- def has_code?(code_id)
144
- return false unless @code_ids
145
- @code_ids.include?(code_id)
146
- end
147
-
148
- def code_failed?(code_id)
149
- return false unless @failed_code_ids
150
- @failed_code_ids.include?(code_id)
151
- end
152
-
153
- def add_code(code_id, code)
154
- # It's important that the definition is run in its own scope (by executing it inside an anonymous function)
155
- # Otherwise some auxiliary functions that the backend server sends will collide the name
156
- # Because they're defined with `var`, running the definitions inside a function is enough
157
- eval_unsafe "(function() { #{code} })()"
158
- transf_global_funcs code_id
159
- @code_ids ||= Set.new
160
- @code_ids << code_id
161
- rescue
162
- @failed_code_ids ||= Set.new
163
- @failed_code_ids << code_id
164
- raise
165
- end
166
-
167
- def eval_unsafe(str, filename = nil, timeoutv = nil)
168
- # Beware, timeout could be kept in the context
169
- # if perf cap is removed after having been activated
170
- # As it's unused by execjscb we are not cleaning it
171
- return super(str, filename) if timeoutv.nil?
172
- return if timeoutv <= 0.0
173
- timeoutv *= 1000 # Timeout are currently expressed in seconds
174
- @timeout = timeoutv
175
- @eval_thread = Thread.current
176
- timeout do
177
- super(str, filename)
178
- end
179
- end
180
-
181
- def possibly_gc
182
- @gc_threshold_in_bytes ||= DEFAULT_GC_THRESHOLD
183
- @gc_load ||= 0
184
-
185
- # garbage collections max 1 in every 4 calls (avg)
186
- if heap_stats[:total_heap_size] > @gc_threshold_in_bytes
187
- low_memory_notification
188
- @gc_load += 4
189
- else
190
- @gc_load = [0, @gc_load - 1].max
191
- end
192
- end
193
-
194
- private
195
-
196
- def transf_global_funcs(code_id)
197
- # Multiple callbacks may share the same name. In order to avoid collisions, we rename them here.
198
- eval_unsafe <<EOD
199
- Object.keys(this).forEach(name => {
200
- if (typeof this[name] === "function" && !name.startsWith("sqreen_")) {
201
- this['sqreen_#{code_id}_' + name] = this[name];
202
- this[name] = undefined;
203
- }
204
- });
205
- EOD
206
-
207
- end
208
- end
209
- end
210
- end
211
- end
212
40
  end
213
41
  end
@@ -0,0 +1,144 @@
1
+ # typed: ignore
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
+ # TODO: => Sqreen::JS:MiniRacer
7
+ # TODO: remove class vars
8
+
9
+ require 'sqreen/log'
10
+
11
+ module Sqreen
12
+ module Js
13
+ class MiniRacerExecutableJs < ExecutableJs
14
+ @@ctx_defined = false # rubocop:disable Style/ClassVars
15
+
16
+ def ctx_defined?
17
+ @@ctx_defined
18
+ end
19
+
20
+ def define_ctx!
21
+ @@ctx_defined = true # rubocop:disable Style/ClassVars
22
+ end
23
+
24
+ def initialize(pool, code, vendored)
25
+ @pool = pool
26
+ @code = code
27
+ @code_id = self.class.code_id(code)
28
+
29
+ @module = vendored ? Sqreen::MiniRacer : MiniRacer
30
+
31
+ mod = vendored ? Sqreen::MiniRacer : MiniRacer
32
+
33
+ return if ctx_defined?
34
+
35
+ self.class.define_sqreen_context(mod)
36
+ define_ctx!
37
+ end
38
+
39
+ def run_js_cb(cb_name, budget, arguments)
40
+ @pool.with_context do |ctx|
41
+ if ctx.code_failed?(@code_id)
42
+ Sqreen.log.debug do
43
+ "Skipping execution of callback #{cb_name} (code md5 #{@code_id})" \
44
+ " due to prev failure of definition evaluation"
45
+ end
46
+ return nil
47
+ end
48
+
49
+ ctx.add_code(@code_id, @code) unless ctx.code?(@code_id)
50
+
51
+ # mini_racer expects timeout to be in ms
52
+ ctx.timeout = budget ? budget * 1000.0 : nil
53
+ begin
54
+ ctx.call("sqreen_#{@code_id}_#{cb_name}", *arguments)
55
+ rescue @module::ScriptTerminatedError
56
+ Sqreen.log.debug "ScriptTerminatedError/#{cb_name}"
57
+ nil
58
+ end
59
+ end
60
+ end
61
+
62
+ def self.code_id(code)
63
+ Digest::MD5.hexdigest(code)
64
+ end
65
+
66
+ class << self
67
+ def define_sqreen_context(modoole)
68
+ # Context specialized for Sqreen usage
69
+ Sqreen::Js.const_set 'SqreenContext', Class.new(modoole.const_get('Context'))
70
+ SqreenContext.class_eval do
71
+ attr_accessor :gc_threshold_in_bytes
72
+ attr_accessor :gc_load
73
+ attr_writer :timeout
74
+
75
+ def code?(code_id)
76
+ return false unless @code_ids
77
+ @code_ids.include?(code_id)
78
+ end
79
+
80
+ def code_failed?(code_id)
81
+ return false unless @failed_code_ids
82
+ @failed_code_ids.include?(code_id)
83
+ end
84
+
85
+ def add_code(code_id, code)
86
+ # It's important that the definition is run in its own scope (by executing it inside an anonymous function)
87
+ # Otherwise some auxiliary functions that the backend server sends will collide the name
88
+ # Because they're defined with `var`, running the definitions inside a function is enough
89
+ eval_unsafe "(function() { #{code} })()"
90
+ transf_global_funcs code_id
91
+ @code_ids ||= Set.new
92
+ @code_ids << code_id
93
+ rescue StandardError
94
+ @failed_code_ids ||= Set.new
95
+ @failed_code_ids << code_id
96
+ raise
97
+ end
98
+
99
+ def eval_unsafe(str, filename = nil, timeoutv = nil)
100
+ # Beware, timeout could be kept in the context
101
+ # if perf cap is removed after having been activated
102
+ # As it's unused by execjscb we are not cleaning it
103
+ return super(str, filename) if timeoutv.nil?
104
+ return if timeoutv <= 0.0
105
+ timeoutv *= 1000 # Timeout are currently expressed in seconds
106
+ @timeout = timeoutv
107
+ @eval_thread = Thread.current
108
+ timeout do
109
+ super(str, filename)
110
+ end
111
+ end
112
+
113
+ def possibly_gc
114
+ @gc_threshold_in_bytes ||= DEFAULT_GC_THRESHOLD
115
+ @gc_load ||= 0
116
+
117
+ # garbage collections max 1 in every 4 calls (avg)
118
+ if heap_stats[:total_heap_size] > @gc_threshold_in_bytes
119
+ low_memory_notification
120
+ @gc_load += 4
121
+ else
122
+ @gc_load = [0, @gc_load - 1].max
123
+ end
124
+ end
125
+
126
+ private
127
+
128
+ def transf_global_funcs(code_id)
129
+ # Multiple callbacks may share the same name. In order to avoid collisions, we rename them here.
130
+ eval_unsafe <<-JS
131
+ Object.keys(this).forEach(name => {
132
+ if (typeof this[name] === "function" && !name.startsWith("sqreen_")) {
133
+ this['sqreen_#{code_id}_' + name] = this[name];
134
+ this[name] = undefined;
135
+ }
136
+ });
137
+ JS
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,49 @@
1
+ # typed: true
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
+ # TODO: => Sqreen::JS:ExecJS
7
+
8
+ require 'sqreen/js/executable_js'
9
+
10
+ module Sqreen
11
+ module Js
12
+ class ThreadLocalExecJsRunnable < ExecutableJs
13
+ def initialize(code)
14
+ @code = code
15
+ @tl_key = "SQREEN_EXECJS_CONTEXT_#{object_id}".freeze
16
+ @runtimes = [] # place where to keep strong references
17
+ @runtimes_mutex = Mutex.new
18
+ end
19
+
20
+ def run_js_cb(cbname, _budget, arguments)
21
+ tl_exec_js_runnable.call(cbname, *arguments)
22
+ end
23
+
24
+ def with_runtimes_mutex
25
+ @runtimes_mutex.synchronize { yield }
26
+ end
27
+
28
+ private
29
+
30
+ def dispose_from_dead_threads
31
+ with_runtimes_mutex do
32
+ @runtimes.delete_if { |th, _runtime| !th.alive? }
33
+ end
34
+ end
35
+
36
+ def tl_exec_js_runnable
37
+ runnable = Thread.current[@tl_key]
38
+ return runnable if runnable && runnable.weakref_alive?
39
+
40
+ dispose_from_dead_threads
41
+ runtime = ExecJS.compile(@code)
42
+ with_runtimes_mutex do
43
+ @runtimes << [Thread.current, runtime]
44
+ end
45
+ Thread.current[@tl_key] = WeakRef.new(runtime)
46
+ end
47
+ end
48
+ end
49
+ end