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,20 @@
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 ExecJsRunnable < ExecutableJs
11
+ def initialize(compiled)
12
+ @compiled = compiled
13
+ end
14
+
15
+ def run_js_cb(cbname, _budget, arguments)
16
+ @compiled.call(cbname, *arguments)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,8 +1,14 @@
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
+ # TODO: => Sqreen::JS:ExecJS
5
+
4
6
  require 'execjs'
5
7
  require 'weakref'
8
+ require 'sqreen/js/executable_js'
9
+ require 'sqreen/js/js_service_adapter'
10
+ require 'sqreen/js/exec_js_runnable'
11
+ require 'sqreen/js/thread_local_exec_js_runnable'
6
12
 
7
13
  module Sqreen
8
14
  module Js
@@ -25,52 +31,5 @@ module Sqreen
25
31
  ExecJS.runtime.name != 'therubyrhino (Rhino)'
26
32
  end
27
33
  end
28
-
29
- class ExecJsRunnable < ExecutableJs
30
- def initialize(compiled)
31
- @compiled = compiled
32
- end
33
-
34
- def run_js_cb(cbname, _budget, arguments)
35
- @compiled.call(cbname, *arguments)
36
- end
37
- end
38
-
39
- class ThreadLocalExecJsRunnable < ExecutableJs
40
- def initialize(code)
41
- @code = code
42
- @tl_key = "SQREEN_EXECJS_CONTEXT_#{object_id}".freeze
43
- @runtimes = [] # place where to keep strong references
44
- @runtimes_mutex = Mutex.new
45
- end
46
-
47
- def run_js_cb(cbname, _budget, arguments)
48
- tl_exec_js_runnable.call(cbname, *arguments)
49
- end
50
-
51
- def with_runtimes_mutex
52
- @runtimes_mutex.synchronize { yield }
53
- end
54
-
55
- private
56
-
57
- def dispose_from_dead_threads
58
- with_runtimes_mutex do
59
- @runtimes.delete_if { |th, _runtime| !th.alive? }
60
- end
61
- end
62
-
63
- def tl_exec_js_runnable
64
- runnable = Thread.current[@tl_key]
65
- return runnable if runnable && runnable.weakref_alive?
66
-
67
- dispose_from_dead_threads
68
- runtime = ExecJS.compile(@code)
69
- with_runtimes_mutex do
70
- @runtimes << [Thread.current, runtime]
71
- end
72
- Thread.current[@tl_key] = WeakRef.new(runtime)
73
- end
74
- end
75
34
  end
76
35
  end
@@ -0,0 +1,12 @@
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
+ module Sqreen
5
+ module Js
6
+ class ExecutableJs
7
+ def run_js_cb(_cb_name, _budget, _arguments)
8
+ raise Sqreen::NotImplementedYet
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,12 +1,12 @@
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/log'
4
5
  require 'sqreen/exception'
6
+ require 'singleton'
5
7
 
6
8
  module Sqreen
7
9
  module Js
8
- # start public interface
9
-
10
10
  class JsService
11
11
  include ::Singleton
12
12
 
@@ -66,25 +66,5 @@ module Sqreen
66
66
  false
67
67
  end
68
68
  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
69
  end
90
70
  end
@@ -0,0 +1,18 @@
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
+ module Js
8
+ class JsServiceAdapter
9
+ def preprocess(_rule_name, _code)
10
+ raise Sqreen::NotImplementedYet
11
+ end
12
+
13
+ def variant_name
14
+ 'unspecified'
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,14 +1,19 @@
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
+ # TODO: => Sqreen::JS:MiniRacer
5
+
4
6
  require 'digest'
5
7
  require 'json'
8
+ require 'sqreen/js/executable_js'
9
+ require 'sqreen/js/js_service_adapter'
10
+ require 'sqreen/js/context_pool'
11
+ require 'sqreen/js/mini_racer_executable_js'
6
12
 
7
13
  module Sqreen
8
14
  module Js
9
15
  DEFAULT_GC_THRESHOLD = 15000000 # 15 MB
10
16
 
11
-
12
17
  class MiniRacerAdapter < JsServiceAdapter
13
18
  def initialize(vendored = false)
14
19
  @vendored = vendored
@@ -30,184 +35,5 @@ module Sqreen
30
35
  @done_static_init = true
31
36
  end
32
37
  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
38
  end
213
39
  end
@@ -0,0 +1,142 @@
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:MiniRacer
5
+ # TODO: remove class vars
6
+
7
+ require 'sqreen/log'
8
+
9
+ module Sqreen
10
+ module Js
11
+ class MiniRacerExecutableJs < ExecutableJs
12
+ @@ctx_defined = false # rubocop:disable Style/ClassVars
13
+
14
+ def ctx_defined?
15
+ @@ctx_defined
16
+ end
17
+
18
+ def define_ctx!
19
+ @@ctx_defined = true # rubocop:disable Style/ClassVars
20
+ end
21
+
22
+ def initialize(pool, code, vendored)
23
+ @pool = pool
24
+ @code = code
25
+ @code_id = self.class.code_id(code)
26
+
27
+ @module = vendored ? Sqreen::MiniRacer : MiniRacer
28
+
29
+ mod = vendored ? Sqreen::MiniRacer : MiniRacer
30
+
31
+ return if ctx_defined?
32
+
33
+ self.class.define_sqreen_context(mod)
34
+ define_ctx!
35
+ end
36
+
37
+ def run_js_cb(cb_name, budget, arguments)
38
+ @pool.with_context do |ctx|
39
+ if ctx.code_failed?(@code_id)
40
+ Sqreen.log.debug do
41
+ "Skipping execution of callback #{cb_name} (code md5 #{@code_id})" \
42
+ " due to prev failure of definition evaluation"
43
+ end
44
+ return nil
45
+ end
46
+
47
+ ctx.add_code(@code_id, @code) unless ctx.code?(@code_id)
48
+
49
+ # mini_racer expects timeout to be in ms
50
+ ctx.timeout = budget ? budget * 1000.0 : nil
51
+ begin
52
+ ctx.call("sqreen_#{@code_id}_#{cb_name}", *arguments)
53
+ rescue @module::ScriptTerminatedError
54
+ Sqreen.log.debug "ScriptTerminatedError/#{cb_name}"
55
+ nil
56
+ end
57
+ end
58
+ end
59
+
60
+ def self.code_id(code)
61
+ Digest::MD5.hexdigest(code)
62
+ end
63
+
64
+ class << self
65
+ def define_sqreen_context(modoole)
66
+ # Context specialized for Sqreen usage
67
+ Sqreen::Js.const_set 'SqreenContext', Class.new(modoole.const_get('Context'))
68
+ SqreenContext.class_eval do
69
+ attr_accessor :gc_threshold_in_bytes
70
+ attr_accessor :gc_load
71
+ attr_writer :timeout
72
+
73
+ def code?(code_id)
74
+ return false unless @code_ids
75
+ @code_ids.include?(code_id)
76
+ end
77
+
78
+ def code_failed?(code_id)
79
+ return false unless @failed_code_ids
80
+ @failed_code_ids.include?(code_id)
81
+ end
82
+
83
+ def add_code(code_id, code)
84
+ # It's important that the definition is run in its own scope (by executing it inside an anonymous function)
85
+ # Otherwise some auxiliary functions that the backend server sends will collide the name
86
+ # Because they're defined with `var`, running the definitions inside a function is enough
87
+ eval_unsafe "(function() { #{code} })()"
88
+ transf_global_funcs code_id
89
+ @code_ids ||= Set.new
90
+ @code_ids << code_id
91
+ rescue StandardError
92
+ @failed_code_ids ||= Set.new
93
+ @failed_code_ids << code_id
94
+ raise
95
+ end
96
+
97
+ def eval_unsafe(str, filename = nil, timeoutv = nil)
98
+ # Beware, timeout could be kept in the context
99
+ # if perf cap is removed after having been activated
100
+ # As it's unused by execjscb we are not cleaning it
101
+ return super(str, filename) if timeoutv.nil?
102
+ return if timeoutv <= 0.0
103
+ timeoutv *= 1000 # Timeout are currently expressed in seconds
104
+ @timeout = timeoutv
105
+ @eval_thread = Thread.current
106
+ timeout do
107
+ super(str, filename)
108
+ end
109
+ end
110
+
111
+ def possibly_gc
112
+ @gc_threshold_in_bytes ||= DEFAULT_GC_THRESHOLD
113
+ @gc_load ||= 0
114
+
115
+ # garbage collections max 1 in every 4 calls (avg)
116
+ if heap_stats[:total_heap_size] > @gc_threshold_in_bytes
117
+ low_memory_notification
118
+ @gc_load += 4
119
+ else
120
+ @gc_load = [0, @gc_load - 1].max
121
+ end
122
+ end
123
+
124
+ private
125
+
126
+ def transf_global_funcs(code_id)
127
+ # Multiple callbacks may share the same name. In order to avoid collisions, we rename them here.
128
+ eval_unsafe <<-JS
129
+ Object.keys(this).forEach(name => {
130
+ if (typeof this[name] === "function" && !name.startsWith("sqreen_")) {
131
+ this['sqreen_#{code_id}_' + name] = this[name];
132
+ this[name] = undefined;
133
+ }
134
+ });
135
+ JS
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end