contrast-agent 5.1.0 → 5.2.0

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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/ext/cs__assess_kernel/cs__assess_kernel.c +7 -4
  3. data/ext/cs__assess_module/cs__assess_module.c +7 -7
  4. data/ext/cs__common/cs__common.c +4 -0
  5. data/ext/cs__common/cs__common.h +1 -0
  6. data/ext/cs__contrast_patch/cs__contrast_patch.c +52 -27
  7. data/ext/cs__contrast_patch/cs__contrast_patch.h +2 -0
  8. data/ext/cs__scope/cs__scope.c +747 -0
  9. data/ext/cs__scope/cs__scope.h +88 -0
  10. data/ext/cs__scope/extconf.rb +5 -0
  11. data/lib/contrast/agent/assess/contrast_event.rb +20 -13
  12. data/lib/contrast/agent/assess/contrast_object.rb +4 -1
  13. data/lib/contrast/agent/assess/policy/propagation_node.rb +2 -5
  14. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +2 -0
  15. data/lib/contrast/agent/assess/policy/trigger_method.rb +4 -1
  16. data/lib/contrast/agent/assess/rule/response/{autocomplete_rule.rb → auto_complete_rule.rb} +4 -3
  17. data/lib/contrast/agent/assess/rule/response/base_rule.rb +12 -79
  18. data/lib/contrast/agent/assess/rule/response/body_rule.rb +109 -0
  19. data/lib/contrast/agent/assess/rule/response/cache_control_header_rule.rb +157 -0
  20. data/lib/contrast/agent/assess/rule/response/click_jacking_header_rule.rb +26 -0
  21. data/lib/contrast/agent/assess/rule/response/csp_header_insecure_rule.rb +14 -15
  22. data/lib/contrast/agent/assess/rule/response/csp_header_missing_rule.rb +5 -25
  23. data/lib/contrast/agent/assess/rule/response/framework/rails_support.rb +29 -0
  24. data/lib/contrast/agent/assess/rule/response/header_rule.rb +70 -0
  25. data/lib/contrast/agent/assess/rule/response/hsts_header_rule.rb +12 -36
  26. data/lib/contrast/agent/assess/rule/response/parameters_pollution_rule.rb +2 -1
  27. data/lib/contrast/agent/assess/rule/response/x_content_type_header_rule.rb +26 -0
  28. data/lib/contrast/agent/assess/rule/response/x_xss_protection_header_rule.rb +36 -0
  29. data/lib/contrast/agent/middleware.rb +1 -0
  30. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +1 -3
  31. data/lib/contrast/agent/patching/policy/patch.rb +2 -6
  32. data/lib/contrast/agent/patching/policy/patcher.rb +1 -1
  33. data/lib/contrast/agent/protect/input_analyzer/input_analyzer.rb +94 -0
  34. data/lib/contrast/agent/protect/rule/base.rb +28 -1
  35. data/lib/contrast/agent/protect/rule/base_service.rb +10 -1
  36. data/lib/contrast/agent/protect/rule/cmd_injection.rb +2 -0
  37. data/lib/contrast/agent/protect/rule/deserialization.rb +6 -0
  38. data/lib/contrast/agent/protect/rule/http_method_tampering.rb +5 -1
  39. data/lib/contrast/agent/protect/rule/no_sqli.rb +1 -0
  40. data/lib/contrast/agent/protect/rule/path_traversal.rb +1 -0
  41. data/lib/contrast/agent/protect/rule/sqli/sqli_input_classification.rb +124 -0
  42. data/lib/contrast/agent/protect/rule/sqli/sqli_worth_watching.rb +121 -0
  43. data/lib/contrast/agent/protect/rule/sqli.rb +33 -0
  44. data/lib/contrast/agent/protect/rule/xxe.rb +4 -0
  45. data/lib/contrast/agent/reporting/input_analysis/input_analysis.rb +44 -0
  46. data/lib/contrast/agent/reporting/input_analysis/input_analysis_result.rb +115 -0
  47. data/lib/contrast/agent/reporting/input_analysis/input_type.rb +44 -0
  48. data/lib/contrast/agent/reporting/input_analysis/score_level.rb +21 -0
  49. data/lib/contrast/agent/reporting/report.rb +1 -0
  50. data/lib/contrast/agent/reporting/reporter.rb +8 -1
  51. data/lib/contrast/agent/reporting/reporting_events/finding.rb +69 -36
  52. data/lib/contrast/agent/reporting/reporting_events/finding_event.rb +88 -59
  53. data/lib/contrast/agent/reporting/reporting_events/{finding_object.rb → finding_event_object.rb} +24 -20
  54. data/lib/contrast/agent/reporting/reporting_events/finding_event_parent_object.rb +39 -0
  55. data/lib/contrast/agent/reporting/reporting_events/finding_event_property.rb +40 -0
  56. data/lib/contrast/agent/reporting/reporting_events/{finding_signature.rb → finding_event_signature.rb} +29 -24
  57. data/lib/contrast/agent/reporting/reporting_events/finding_event_source.rb +12 -8
  58. data/lib/contrast/agent/reporting/reporting_events/{finding_stack.rb → finding_event_stack.rb} +23 -19
  59. data/lib/contrast/agent/reporting/reporting_events/{finding_taint_range.rb → finding_event_taint_range.rb} +17 -15
  60. data/lib/contrast/agent/reporting/reporting_events/finding_request.rb +26 -53
  61. data/lib/contrast/agent/reporting/reporting_events/poll.rb +29 -0
  62. data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +5 -4
  63. data/lib/contrast/agent/reporting/reporting_events/route_discovery.rb +1 -0
  64. data/lib/contrast/agent/reporting/reporting_events/server_activity.rb +1 -1
  65. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +10 -3
  66. data/lib/contrast/agent/reporting/reporting_utilities/endpoints.rb +0 -1
  67. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +1 -0
  68. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +28 -20
  69. data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +1 -1
  70. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +13 -1
  71. data/lib/contrast/agent/request_context.rb +6 -1
  72. data/lib/contrast/agent/request_context_extend.rb +85 -21
  73. data/lib/contrast/agent/scope.rb +102 -107
  74. data/lib/contrast/agent/service_heartbeat.rb +45 -2
  75. data/lib/contrast/agent/version.rb +1 -1
  76. data/lib/contrast/api/decorators/bot_blocker.rb +37 -0
  77. data/lib/contrast/api/decorators/ip_denylist.rb +37 -0
  78. data/lib/contrast/api/decorators/rasp_rule_sample.rb +29 -0
  79. data/lib/contrast/api/decorators/user_input.rb +11 -1
  80. data/lib/contrast/api/decorators/virtual_patch.rb +34 -0
  81. data/lib/contrast/components/logger.rb +5 -0
  82. data/lib/contrast/components/protect.rb +4 -2
  83. data/lib/contrast/components/scope.rb +98 -91
  84. data/lib/contrast/config/agent_configuration.rb +58 -12
  85. data/lib/contrast/config/api_configuration.rb +100 -12
  86. data/lib/contrast/config/api_proxy_configuration.rb +55 -3
  87. data/lib/contrast/config/application_configuration.rb +114 -15
  88. data/lib/contrast/config/assess_configuration.rb +106 -12
  89. data/lib/contrast/config/assess_rules_configuration.rb +44 -3
  90. data/lib/contrast/config/base_configuration.rb +1 -0
  91. data/lib/contrast/config/certification_configuration.rb +74 -3
  92. data/lib/contrast/config/exception_configuration.rb +61 -3
  93. data/lib/contrast/config/heap_dump_configuration.rb +101 -17
  94. data/lib/contrast/config/inventory_configuration.rb +64 -3
  95. data/lib/contrast/config/logger_configuration.rb +46 -3
  96. data/lib/contrast/config/protect_rule_configuration.rb +36 -9
  97. data/lib/contrast/config/protect_rules_configuration.rb +120 -17
  98. data/lib/contrast/config/request_audit_configuration.rb +68 -3
  99. data/lib/contrast/config/ruby_configuration.rb +96 -22
  100. data/lib/contrast/config/sampling_configuration.rb +76 -10
  101. data/lib/contrast/config/server_configuration.rb +56 -11
  102. data/lib/contrast/configuration.rb +6 -3
  103. data/lib/contrast/logger/cef_log.rb +151 -0
  104. data/lib/contrast/utils/hash_digest.rb +14 -6
  105. data/lib/contrast/utils/log_utils.rb +114 -0
  106. data/lib/contrast/utils/middleware_utils.rb +6 -7
  107. data/lib/contrast/utils/net_http_base.rb +12 -9
  108. data/lib/contrast/utils/patching/policy/patch_utils.rb +0 -4
  109. data/lib/contrast.rb +4 -3
  110. data/ruby-agent.gemspec +1 -1
  111. data/service_executables/VERSION +1 -1
  112. data/service_executables/linux/contrast-service +0 -0
  113. data/service_executables/mac/contrast-service +0 -0
  114. metadata +41 -21
  115. data/lib/contrast/agent/assess/rule/response/cachecontrol_rule.rb +0 -184
  116. data/lib/contrast/agent/assess/rule/response/clickjacking_rule.rb +0 -66
  117. data/lib/contrast/agent/assess/rule/response/x_content_type_rule.rb +0 -52
  118. data/lib/contrast/agent/assess/rule/response/x_xss_protection_rule.rb +0 -53
  119. data/lib/contrast/extension/kernel.rb +0 -54
@@ -0,0 +1,747 @@
1
+ /* Copyright (c) 2022 Contrast Security, Inc. See
2
+ * https://www.contrastsecurity.com/enduser-terms-0317a for more details. */
3
+
4
+ #include "../cs__common/cs__common.h"
5
+ #include "cs__scope.h"
6
+ #include <ruby.h>
7
+
8
+ /* Calls to Contrast modules and classes */
9
+ VALUE contrast, agent, components;
10
+ VALUE scope_interface, scope_inst_methods;
11
+ VALUE scope_mod, scope_klass;
12
+
13
+ /* helpers */
14
+
15
+ /* get scope for ec or create new
16
+ *
17
+ * [Ruby definition]
18
+ *
19
+ * EXECUTION_CONTEXT[Fiber.current] ||= Contrast::Agent::Scope.new
20
+ */
21
+ VALUE get_ec() {
22
+ VALUE ec, scope_inst, new_inst, keys, fiber;
23
+
24
+ ec = rb_const_get(scope_mod, rb_intern(rb_const_ec));
25
+ scope_inst = rb_hash_aref(ec, rb_fiber_current());
26
+ keys = rb_const_get(scope_mod, rb_intern(rb_const_ec_keys));
27
+ fiber = rb_fiber_current();
28
+
29
+ if (RB_TYPE_P(scope_inst, T_NIL))
30
+ {
31
+ new_inst = rb_new_c_scope();
32
+ rb_ary_push(keys, fiber);
33
+ rb_hash_aset(ec, fiber, new_inst);
34
+ return new_inst;
35
+ } else {
36
+ return scope_inst;
37
+ }
38
+ }
39
+
40
+ /* create new Scope class instance */
41
+ VALUE rb_new_c_scope() {
42
+ return rb_class_new_instance(0, 0, scope_klass);
43
+ }
44
+
45
+ int scope_increase(int scope) {
46
+ int inc = scope;
47
+ inc = inc + 1;
48
+
49
+ return INT2FIX(inc);
50
+ }
51
+
52
+ int scope_decrease(int scope) {
53
+ int inc = scope;
54
+ inc = inc - 1;
55
+
56
+ return INT2FIX(inc);
57
+ }
58
+
59
+ VALUE is_in_scope(int scope) {
60
+ if (scope > 0)
61
+ {
62
+ return Qtrue;
63
+ } else if (scope <= 0) {
64
+ return Qfalse;
65
+ }
66
+ }
67
+
68
+ /* default: raise NoMethodError, "Scope '#{ name.inspect }' is not registered as a scope." */
69
+ void rb_raise_scope_no_method_err(const VALUE method_scope_sym) {
70
+ rb_raise(rb_eNoMethodError, "Scope ':%"PRIsVALUE"' is not registered as a scope.", rb_sym_to_s(method_scope_sym));
71
+ }
72
+
73
+ /*
74
+ * @class Contrast::Agent::Scope
75
+ */
76
+
77
+ /*
78
+ * @method initialize
79
+ *
80
+ * sets scope instance variables.
81
+ * def initialize
82
+ * @contrast_scope = 0
83
+ * @deserialization_scope = 0
84
+ * @split_scope = 0
85
+ * end
86
+ */
87
+ VALUE contrast_scope_klass_init(VALUE self, VALUE args) {
88
+ rb_iv_set(self, rb_iv_cntr_scope, INT2FIX(0));
89
+ rb_iv_set(self, rb_iv_dslr_scope, INT2FIX(0));
90
+ rb_iv_set(self, rb_iv_split_scope, INT2FIX(0));
91
+
92
+ return self;
93
+ }
94
+
95
+ /*
96
+ * @method in_contrast_scope?
97
+ *
98
+ * Check if we are in contrast scope.
99
+ *
100
+ * @return [Boolean] check if we are in contrast scope
101
+ * if the scope is above 0 return true.
102
+ * def in_contrast_scope?
103
+ * @contrast_scope.positive?
104
+ * end
105
+ */
106
+ VALUE in_cntr_scope(VALUE self, VALUE args) {
107
+ return is_in_scope(FIX2INT(rb_iv_get(self, rb_iv_cntr_scope)));
108
+ }
109
+
110
+ /*
111
+ * @method enter_contrast_scope!
112
+ *
113
+ * Enters contrast scope.
114
+ *
115
+ * @return @contrast_scope [Integer] contrast scope increased.
116
+ * def enter_contrast_scope!
117
+ * @contrast_scope += 1
118
+ * end
119
+ */
120
+ VALUE enter_cntr_scope(VALUE self, VALUE args) {
121
+ int scope = FIX2INT(rb_iv_get(self, rb_iv_cntr_scope));
122
+ rb_iv_set(self, rb_iv_cntr_scope, scope_increase(scope));
123
+
124
+ return rb_iv_get(self, rb_iv_cntr_scope);
125
+ }
126
+
127
+ /*
128
+ * @method exit_contrast_scope!
129
+ *
130
+ * Exits contrast scope.
131
+ *
132
+ * @return @contrast_scope [Integer] contrast scope decreased.
133
+ * def exit_contrast_scope!
134
+ * @contrast_scope -= 1
135
+ * end
136
+ */
137
+ VALUE exit_cntr_scope(VALUE self, VALUE args) {
138
+ int scope = FIX2INT(rb_iv_get(self, rb_iv_cntr_scope));
139
+ rb_iv_set(self, rb_iv_cntr_scope, scope_decrease(scope));
140
+
141
+ return rb_iv_get(self, rb_iv_cntr_scope);
142
+ }
143
+
144
+ /*
145
+ * @method in_deserialization_scope?
146
+ *
147
+ * Check if we are in deserialization scope.
148
+ *
149
+ * @return [Boolean] check if we are in deserialization scope
150
+ * if the scope is above 0 return true.
151
+ * def in_deserialization_scope?
152
+ * @deserialization_scope.positive?
153
+ * end
154
+ */
155
+ VALUE in_dslr_scope(VALUE self, VALUE args) {
156
+ return is_in_scope(FIX2INT(rb_iv_get(self, rb_iv_dslr_scope)));
157
+ }
158
+
159
+ /*
160
+ * @method enter_deserialization_scope!
161
+ *
162
+ * Enters deserialization scope.
163
+ *
164
+ * @return @deserialization_scope [Integer] deserialization scope increased.
165
+ * def enter_deserialization_scope!
166
+ * @deserialization_scope += 1
167
+ * end
168
+ */
169
+ VALUE enter_dsrl_scope(VALUE self, VALUE args) {
170
+ int scope = FIX2INT(rb_iv_get(self, rb_iv_dslr_scope));
171
+ rb_iv_set(self, rb_iv_dslr_scope, scope_increase(scope));
172
+
173
+ return rb_iv_get(self, rb_iv_dslr_scope);
174
+ }
175
+
176
+ /*
177
+ * @method exit_deserialization_scope!
178
+ *
179
+ * Exits deserialization scope.
180
+ *
181
+ * @return @deserialization_scope [Integer] deserialization scope decreased.
182
+ * def enter_deserialization_scope!
183
+ * @deserialization_scope += 1
184
+ * end
185
+ */
186
+ VALUE exit_dsrl_scope(VALUE self, VALUE args) {
187
+ int scope = FIX2INT(rb_iv_get(self, rb_iv_dslr_scope));
188
+ rb_iv_set(self, rb_iv_dslr_scope, scope_decrease(scope));
189
+
190
+ return rb_iv_get(self, rb_iv_dslr_scope);
191
+ }
192
+
193
+ /*
194
+ * @method in_split_scope?
195
+ *
196
+ * Check if we are in split scope.
197
+ *
198
+ * @return [Boolean] check if we are in split scope
199
+ * if the scope is above 0 return true.
200
+ * def in_split_scope?
201
+ * @split_scope.positive?
202
+ * end
203
+ */
204
+ VALUE in_split_scope(VALUE self, VALUE args) {
205
+ return is_in_scope(FIX2INT(rb_iv_get(self, rb_iv_split_scope)));
206
+ }
207
+
208
+ /*
209
+ * @method enter_split_scope!
210
+ *
211
+ * Enters split scope.
212
+ *
213
+ * @return @split_scope [Integer] split scope increased.
214
+ * def enter_split_scope!
215
+ * @split_scope += 1
216
+ * end
217
+ */
218
+ VALUE enter_split_scope(VALUE self, VALUE args) {
219
+ int scope = FIX2INT(rb_iv_get(self, rb_iv_split_scope));
220
+ rb_iv_set(self, rb_iv_split_scope, scope_increase(scope));
221
+
222
+ return rb_iv_get(self, rb_iv_split_scope);
223
+ }
224
+
225
+ /*
226
+ * @method exit_split_scope!
227
+ *
228
+ * Exits split scope.
229
+ *
230
+ * @return @split_scope [Integer] split scope decreased.
231
+ * def enter_split_scope!
232
+ * @split_scope -= 1
233
+ * end
234
+ */
235
+ VALUE exit_split_scope(VALUE self, VALUE args) {
236
+ int scope = FIX2INT(rb_iv_get(self, rb_iv_split_scope));
237
+ rb_iv_set(self, rb_iv_split_scope, scope_decrease(scope));
238
+
239
+ return rb_iv_get(self, rb_iv_split_scope);
240
+ }
241
+
242
+ /*
243
+ * @method split_scope_depth
244
+ *
245
+ * Returns split scope current depth.
246
+ *
247
+ * @return @split_scope [Integer]
248
+ * def split_scope_depth
249
+ * @split_scope
250
+ * end
251
+ */
252
+ VALUE split_scope_depth(VALUE self, VALUE args) {
253
+ return rb_iv_get(self, rb_iv_split_scope);
254
+ }
255
+
256
+ /*
257
+ * Static methods to be used, the cases are defined by the usage from the above methods
258
+ * if more methods are added - please extend the case statements as they are no longed dynamic
259
+ */
260
+
261
+ /*
262
+ * @method in_scope?
263
+ *
264
+ * Check if we are in specific scope.
265
+ *
266
+ * @param name [Symbol] scope symbol representing scope to check.
267
+ * @return [Boolean] check if we are in passed scope.
268
+ * def in_scope? name
269
+ * case name
270
+ * when :contrast
271
+ * in_contrast_scope?
272
+ * when :deserialization
273
+ * in_deserialization_scope?
274
+ * when :split
275
+ * in_split_scope?
276
+ * else
277
+ * raise NoMethodError, "Scope '#{ name.inspect }' is not registered as a scope."
278
+ * end
279
+ * end
280
+ */
281
+ VALUE scope_klass_in_scope(VALUE self, VALUE method_scope_sym) {
282
+ VALUE in_scope = Qnil;
283
+
284
+ if (method_scope_sym == rb_sym_contrast) {
285
+ /* in_contrast_scope? */
286
+ in_scope = in_cntr_scope(self, 0);
287
+ } else if (method_scope_sym == rb_sym_deserialization) {
288
+ /* in_deserialization_scope? */
289
+ in_scope = in_dslr_scope(self, 0);
290
+ } else if (method_scope_sym == rb_sym_split) {
291
+ /* in_split_scope? */
292
+ in_scope = in_split_scope(self, 0);
293
+ } else {
294
+ rb_raise_scope_no_method_err(method_scope_sym);
295
+ }
296
+
297
+ return in_scope;
298
+ }
299
+
300
+ /*
301
+ * @method enter_scope!
302
+ *
303
+ * Enters specific scope.
304
+ *
305
+ * @param name [Symbol] scope symbol representing scope to enter.
306
+ * @return [Boolean] entered scope value increased.
307
+ * def enter_scope! name
308
+ * case name
309
+ * when :contrast
310
+ * enter_contrast_scope!
311
+ * when :deserialization
312
+ * enter_deserialization_scope!
313
+ * when :split
314
+ * enter_split_scope!
315
+ * else
316
+ * raise NoMethodError, "Scope '#{ name.inspect }' is not registered as a scope."
317
+ * end
318
+ * end
319
+ */
320
+ VALUE scope_klass_enter_scope(VALUE self, VALUE method_scope_sym) {
321
+ VALUE enter_scope = Qnil;
322
+
323
+ if (method_scope_sym == rb_sym_contrast) {
324
+ enter_scope = enter_cntr_scope(self, 0);
325
+ } else if (method_scope_sym == rb_sym_deserialization) {
326
+ enter_scope = enter_dsrl_scope(self, 0);
327
+ } else if (method_scope_sym == rb_sym_split) {
328
+ enter_scope = enter_split_scope(self, 0);
329
+ } else {
330
+ rb_raise_scope_no_method_err(method_scope_sym);
331
+ }
332
+
333
+ return enter_scope;
334
+ }
335
+
336
+ /*
337
+ * @method exit_scope!
338
+ *
339
+ * Exits specific scope.
340
+ *
341
+ * @param name [Symbol] scope symbol representing scope to exit.
342
+ * @return [Boolean] entered scope value decreased.
343
+ * def exit_scope! name
344
+ * case name
345
+ * when :contrast
346
+ * exit_contrast_scope!
347
+ * when :deserialization
348
+ * exit_deserialization_scope!
349
+ * when :split
350
+ * exit_split_scope!
351
+ * else
352
+ * raise NoMethodError, "Scope '#{ name.inspect }' is not registered as a scope."
353
+ * end
354
+ * end
355
+ */
356
+ VALUE scope_klass_exit_scope(VALUE self, VALUE method_scope_sym) {
357
+ VALUE exit_scope = Qnil;
358
+
359
+ if (method_scope_sym == rb_sym_contrast) {
360
+ exit_scope = exit_cntr_scope(self, 0);
361
+ } else if (method_scope_sym == rb_sym_deserialization) {
362
+ exit_scope = exit_dsrl_scope(self, 0);
363
+ } else if (method_scope_sym == rb_sym_split) {
364
+ exit_scope = exit_split_scope(self, 0);
365
+ } else {
366
+ rb_raise_scope_no_method_err(method_scope_sym);
367
+ }
368
+
369
+ return exit_scope;
370
+ }
371
+
372
+ /*
373
+ * @class Contrast::Components::Interface
374
+ */
375
+
376
+ /* @method #initialize
377
+ *
378
+ * init set new scope for current fiber
379
+ * @return ec [Hash<Fiber => Scope>]
380
+ *
381
+ * [Ruby definition]
382
+ *
383
+ * EXECUTION_CONTEXT[Fiber.current] = Contrast::Agent::Scope.new
384
+ */
385
+ VALUE contrast_scope_interface_init(VALUE self, VALUE args) {
386
+ VALUE ec = rb_const_get(scope_mod, rb_intern(rb_const_ec));
387
+ rb_hash_aset(ec, rb_fiber_current(), rb_new_c_scope());
388
+
389
+ return ec;
390
+ }
391
+
392
+ /*
393
+ * @method #scope_for_current_ec
394
+ *
395
+ * This returns the scope governing the current execution context. Use this sparingly, preferring the instance
396
+ * & class methods to access and query scope, rather than interacting with the scope object directly.
397
+ *
398
+ * Alternative to Monitor => mutex.synchronize
399
+ * rb_mutex_new(void)
400
+ * rb_mutex_synchronize (VALUE mutex, VALUE(*func)(VALUE arg), VALUE arg)
401
+ * Mutex.synchronize do ... end goes here:
402
+ *
403
+ *
404
+ * [ruby definition]
405
+ *
406
+ * def scope_for_current_ec
407
+ * MONITOR.synchronize do
408
+ * return EXECUTION_CONTEXT[Fiber.current] ||= Contrast::Agent::Scope.new
409
+ * end
410
+ * end
411
+ */
412
+ VALUE contrast_scope_for_current_ec(VALUE self, VALUE args) {
413
+ /* synchronize */
414
+ VALUE mutex = rb_const_get(scope_mod, rb_intern(rb_const_mon));
415
+
416
+ return rb_mutex_synchronize(mutex, get_ec, 0);
417
+ }
418
+
419
+ /*
420
+ * @module Contrast::Components::Scope::InstanceMethods
421
+ */
422
+
423
+ /*
424
+ * @method #enter_method_scope!
425
+ *
426
+ * Iterates over the method policy's scopes and enters in each one.
427
+ *
428
+ * @param scopes_to_enter [Array<Symbol>] Scopes form method_policy#scopes_to_enter
429
+ * for the scope current method policy
430
+ * @return scopes_to_enter [Array<Symbol>]
431
+ *
432
+ * [Ruby definition]
433
+ *
434
+ * def enter_method_scope! scopes_to_enter
435
+ * scopes_to_enter.each do |scope|
436
+ * enter_scope!(scope)
437
+ * end
438
+ * end
439
+ */
440
+ VALUE inst_methods_enter_method_scope(VALUE self, VALUE scopes_to_enter) {
441
+ VALUE scopes_ary, scope;
442
+
443
+ scopes_ary = rb_ary_dup(scopes_to_enter);
444
+ scope = rb_ary_pop(scopes_ary);
445
+
446
+ while(!RB_TYPE_P(scope, T_NIL)){
447
+ inst_methods_enter_scope(self, scope);
448
+ scope = rb_ary_pop(scopes_ary);
449
+ }
450
+
451
+ return scopes_to_enter;
452
+ }
453
+
454
+ /*
455
+ * @method #exit_method_scope!
456
+ *
457
+ * Iterates over the method policy's scopes and exits each one.
458
+ *
459
+ * @param scopes_to_exit [Array<Symbol>] Scopes form method_policy#scopes_to_exit
460
+ * for the scope current method policy
461
+ * @return scopes_to_exit [Array<Symbol>]
462
+ *
463
+ * [Ruby definition]
464
+ *
465
+ * def enter_method_scope! scopes_to_exit
466
+ * scopes_to_exit.each do |scope|
467
+ * enter_scope!(scope)
468
+ * end
469
+ * end
470
+ */
471
+ VALUE inst_methods_exit_method_scope(VALUE self, VALUE scopes_to_exit) {
472
+ VALUE scopes_ary, scope;
473
+
474
+ scopes_ary = rb_ary_dup(scopes_to_exit);
475
+ scope = rb_ary_pop(scopes_ary);
476
+
477
+ while(!RB_TYPE_P(scope, T_NIL)){
478
+ inst_methods_exit_scope(self, scope);
479
+ scope = rb_ary_pop(scopes_ary);
480
+ }
481
+
482
+ return scopes_to_exit;
483
+ }
484
+
485
+ /* For the InstanceMethods we need to call all the scope methods from the current ec context
486
+ * All methods bellow are with same names as Contrast::Agent::Scope class with the difference
487
+ * that they act as forwarders:
488
+ * exp:
489
+ * def in_contrast_scope?
490
+ * scope_for_current_ec.in_contrast_scope?
491
+ * end
492
+ */
493
+ VALUE inst_methods_in_cntr_scope(VALUE self, VALUE args) {
494
+ return is_in_scope(FIX2INT(rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope)));
495
+ }
496
+
497
+ VALUE inst_methods_enter_cntr_scope(VALUE self, VALUE args) {
498
+ int scope = FIX2INT(rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope));
499
+ rb_iv_set(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope, scope_increase(scope));
500
+
501
+ return rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope);
502
+ }
503
+
504
+ VALUE inst_methods_exit_cntr_scope(VALUE self, VALUE args) {
505
+ int scope = FIX2INT(rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope));
506
+ rb_iv_set(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope, scope_decrease(scope));
507
+
508
+ return rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope);
509
+ }
510
+
511
+ VALUE inst_methods_in_split_scope(VALUE self, VALUE args) {
512
+ return is_in_scope(FIX2INT(rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope)));
513
+ }
514
+
515
+ VALUE inst_methods_enter_split_scope(VALUE self, VALUE args) {
516
+ int scope = FIX2INT(rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope));
517
+
518
+ return rb_iv_set(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope, scope_increase(scope));
519
+ }
520
+
521
+ VALUE inst_methods_exit_split_scope(VALUE self, VALUE args) {
522
+ int scope = FIX2INT(rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope));
523
+ rb_iv_set(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope, scope_decrease(scope));
524
+
525
+ return rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope);
526
+ }
527
+
528
+ VALUE inst_methods_split_scope_depth(VALUE self, VALUE args) {
529
+ return rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope);
530
+ }
531
+
532
+ VALUE inst_methods_in_dsrl_scope(VALUE self, VALUE args) {
533
+ return is_in_scope(FIX2INT(rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope)));
534
+ }
535
+
536
+ VALUE inst_methods_enter_dsrl_scope(VALUE self, VALUE args) {
537
+ int scope = FIX2INT(rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope));
538
+ rb_iv_set(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope, scope_increase(scope));
539
+
540
+ return rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope);
541
+ }
542
+
543
+ VALUE inst_methods_exit_dsrl_scope(VALUE self, VALUE args) {
544
+ int scope = FIX2INT(rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope));
545
+ rb_iv_set(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope, scope_decrease(scope));
546
+
547
+ return rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope);
548
+ }
549
+
550
+ VALUE inst_methods_in_scope(VALUE self, VALUE method_scope_sym) {
551
+ if (method_scope_sym == rb_sym_contrast) {
552
+ inst_methods_in_cntr_scope(self, 0);
553
+ } else if (method_scope_sym == rb_sym_deserialization) {
554
+ inst_methods_in_dsrl_scope(self, 0);
555
+ } else if (method_scope_sym == rb_sym_split) {
556
+ inst_methods_in_split_scope(self, 0);
557
+ } else {
558
+ rb_raise_scope_no_method_err(method_scope_sym);
559
+ }
560
+ }
561
+
562
+ VALUE inst_methods_enter_scope(VALUE self, VALUE method_scope_sym) {
563
+ if (method_scope_sym == rb_sym_contrast) {
564
+ inst_methods_enter_cntr_scope(self, 0);
565
+ } else if (method_scope_sym == rb_sym_deserialization) {
566
+ inst_methods_enter_dsrl_scope(self, 0);
567
+ } else if (method_scope_sym == rb_sym_split) {
568
+ inst_methods_enter_split_scope(self, 0);
569
+ } else {
570
+ rb_raise_scope_no_method_err(method_scope_sym);
571
+ }
572
+ }
573
+
574
+ VALUE inst_methods_exit_scope(VALUE self, VALUE method_scope_sym) {
575
+ if (method_scope_sym == rb_sym_contrast) {
576
+ inst_methods_exit_cntr_scope(self, 0);
577
+ } else if (method_scope_sym == rb_sym_deserialization) {
578
+ inst_methods_exit_dsrl_scope(self, 0);
579
+ } else if (method_scope_sym == rb_sym_split) {
580
+ inst_methods_exit_split_scope(self, 0);
581
+ } else {
582
+ rb_raise_scope_no_method_err(method_scope_sym);
583
+ }
584
+ }
585
+
586
+ /*
587
+ * @module Contrast::Components::Scope
588
+ *
589
+ * TODO: RUBY-534, #sweep_dead_ecs compensates for a lack of weak tables. when we can use WeakRef, we should
590
+ * investigate removing this call and instead use the WeakRef for the Execution Context's Keys or using our
591
+ * Finalizers Hash for Fibers
592
+ *
593
+ * [Ruby definition]
594
+ *
595
+ * MONITOR.synchronize do
596
+ * EXECUTION_CONTEXT.delete_if do |ec, _scope|
597
+ * !ec.alive?
598
+ * end
599
+ * end
600
+ * end
601
+ */
602
+ VALUE scope_mod_sweep_dead_ecs(VALUE self, VALUE args) {
603
+ VALUE mutex, ec, ec_keys, key, test;
604
+
605
+ mutex = rb_const_get(scope_mod, rb_intern(rb_const_mon));
606
+ ec = rb_const_get(scope_mod, rb_intern(rb_const_ec));
607
+ ec_keys = rb_const_get(scope_mod, rb_intern(rb_const_ec_keys));
608
+
609
+ /* Check if the key is dead (terminated fiber) and delete if true. */
610
+ int i = 0;
611
+ int size = rb_hash_size(ec_keys);
612
+
613
+ for (i = 0; i < size; ++i) {
614
+ key = rb_ary_entry(ec_keys, i);
615
+ test = key;
616
+ if (!RB_TYPE_P(key, T_NIL)) {
617
+ if(!rb_fiber_alive_p(key)) {
618
+ rb_hash_delete(ec, key);
619
+ }
620
+ }
621
+ }
622
+
623
+ return ec;
624
+ }
625
+
626
+ void Init_cs__scope() {
627
+ /* ivs */
628
+ rb_iv_cntr_scope = "@contrast_scope";
629
+ rb_iv_dslr_scope = "@deserialization_scope";
630
+ rb_iv_split_scope = "@split_scope";
631
+
632
+ /* constants */
633
+ rb_const_mon = "MONITOR";
634
+ rb_const_ec = "EXECUTION_CONTEXT";
635
+ rb_const_ec_keys = "EC_KEYS";
636
+
637
+ /* Symbols */
638
+ rb_sym_scope_mod = rb_intern("Scope");
639
+ rb_sym_contrast = ID2SYM(rb_intern("contrast"));
640
+ rb_sym_deserialization = ID2SYM(rb_intern("deserialization"));
641
+ rb_sym_split = ID2SYM(rb_intern("split"));
642
+
643
+ /* method names */
644
+ rb_method_name_init = "initialize";
645
+ rb_method_name_in_scope = "in_scope?";
646
+ rb_method_name_enter_scope = "enter_scope!";
647
+ rb_method_name_exit_scope = "exit_scope!";
648
+ rb_method_name_scope_for_current_ec = "scope_for_current_ec";
649
+ rb_method_name_in_cntr_scope = "in_contrast_scope?";
650
+ rb_method_name_enter_cntr_scope = "enter_contrast_scope!";
651
+ rb_method_name_exit_cntr_scope = "exit_contrast_scope!";
652
+ rb_method_name_in_dslr_scope = "in_deserialization_scope?";
653
+ rb_method_name_enter_dslr_scope = "enter_deserialization_scope!";
654
+ rb_method_name_exit_dslr_scope = "exit_deserialization_scope!";
655
+ rb_method_name_in_split_scope = "in_split_scope?";
656
+ rb_method_name_enter_split_scope = "enter_split_scope!";
657
+ rb_method_name_exit_split_scope = "exit_split_scope!";
658
+ rb_method_name_split_scope_depth = "split_scope_depth";
659
+
660
+ /* Define the new scope modules and objects */
661
+ contrast = rb_define_module("Contrast");
662
+ agent = rb_define_module_under(contrast, "Agent");
663
+ /* components => Contrast::Components */
664
+ components = rb_define_module_under(contrast, "Components");
665
+ /* scope_mod => Contrast::Components::Scope */
666
+ scope_mod = rb_define_module_under(components, "Scope");
667
+ /* scope_interface => Contrast::Components::Scope::Interface */
668
+ scope_interface = rb_define_class_under(scope_mod, "Interface", rb_cObject);
669
+ /* scope_inst_methods => Contrast::Components::Scope::InstanceMethods */
670
+ scope_inst_methods = rb_define_module_under(scope_mod, "InstanceMethods");
671
+ /* scope_klass => Contrast::Agent::Scope */
672
+ scope_klass = rb_define_class_under(agent, "Scope", rb_cObject);
673
+
674
+ /*
675
+ * @class Contrast::Agent::Scope
676
+ */
677
+
678
+ /* Instance methods: */
679
+ rb_define_method(scope_klass, rb_method_name_init, contrast_scope_klass_init, 0);
680
+ rb_define_method(scope_klass, rb_method_name_in_cntr_scope, in_cntr_scope, 0);
681
+ rb_define_method(scope_klass, rb_method_name_enter_cntr_scope, enter_cntr_scope, 0);
682
+ rb_define_method(scope_klass, rb_method_name_exit_cntr_scope, exit_cntr_scope, 0);
683
+ rb_define_method(scope_klass, rb_method_name_in_dslr_scope, in_dslr_scope, 0);
684
+ rb_define_method(scope_klass, rb_method_name_enter_dslr_scope, enter_dsrl_scope, 0);
685
+ rb_define_method(scope_klass, rb_method_name_exit_dslr_scope, exit_dsrl_scope, 0);
686
+ rb_define_method(scope_klass, rb_method_name_in_split_scope, in_split_scope, 0);
687
+ rb_define_method(scope_klass, rb_method_name_enter_split_scope, enter_split_scope, 0);
688
+ rb_define_method(scope_klass, rb_method_name_exit_split_scope, exit_split_scope, 0);
689
+ rb_define_method(scope_klass, rb_method_name_split_scope_depth, split_scope_depth, 0);
690
+ rb_define_method(scope_klass, rb_method_name_in_scope, scope_klass_in_scope, 1);
691
+ rb_define_method(scope_klass, rb_method_name_enter_scope, scope_klass_enter_scope, 1);
692
+ rb_define_method(scope_klass, rb_method_name_exit_scope, scope_klass_exit_scope, 1);
693
+
694
+ /*
695
+ * @class Contrast::Components::Interface
696
+ */
697
+
698
+ /* Interface#initialize */
699
+ rb_define_method(scope_interface, rb_method_name_init, contrast_scope_interface_init, 0);
700
+ /* Interface#scope_for_current_ec */
701
+ rb_define_method(scope_interface, rb_method_name_scope_for_current_ec, contrast_scope_for_current_ec, 0);
702
+
703
+ /*
704
+ * @module Contrast::Components::Scope
705
+ */
706
+
707
+ /* Constants */
708
+
709
+ /* Contrast::Components::Scope::EC_KEYS */
710
+ rb_const_set(scope_mod, rb_intern(rb_const_ec_keys), rb_ary_new());
711
+ /* Contrast::Components::Scope::EXECUTION_CONTEXT => {} */
712
+ rb_define_const(scope_mod, rb_const_ec, rb_hash_new());
713
+ /* Contrast::Components::Scope::MONITOR => Mutex.new */
714
+ rb_define_const(scope_mod, rb_const_mon, rb_mutex_new());
715
+
716
+ /*
717
+ * @module Contrast::Components::Scope::InstanceMethods
718
+ */
719
+
720
+ /* InstanceMethods#scope_for_current_ec */
721
+ rb_define_method(scope_inst_methods, rb_method_name_scope_for_current_ec, contrast_scope_for_current_ec, 0);
722
+ /* Forwarders */
723
+ rb_define_method(scope_inst_methods, rb_method_name_in_cntr_scope, inst_methods_in_cntr_scope, 0);
724
+ rb_define_method(scope_inst_methods, rb_method_name_enter_cntr_scope, inst_methods_enter_cntr_scope, 0);
725
+ rb_define_method(scope_inst_methods, rb_method_name_exit_cntr_scope, inst_methods_exit_cntr_scope, 0);
726
+ rb_define_method(scope_inst_methods, rb_method_name_in_dslr_scope, inst_methods_in_dsrl_scope, 0);
727
+ rb_define_method(scope_inst_methods, rb_method_name_enter_dslr_scope, inst_methods_enter_dsrl_scope, 0);
728
+ rb_define_method(scope_inst_methods, rb_method_name_exit_dslr_scope, inst_methods_exit_dsrl_scope, 0);
729
+ rb_define_method(scope_inst_methods, rb_method_name_in_split_scope, inst_methods_in_split_scope, 0);
730
+ rb_define_method(scope_inst_methods, rb_method_name_enter_split_scope, inst_methods_enter_split_scope, 0);
731
+ rb_define_method(scope_inst_methods, rb_method_name_exit_split_scope, inst_methods_exit_split_scope, 0);
732
+ rb_define_method(scope_inst_methods, rb_method_name_split_scope_depth, inst_methods_split_scope_depth, 0);
733
+ rb_define_method(scope_inst_methods, rb_method_name_in_scope, inst_methods_in_scope, 1);
734
+ rb_define_method(scope_inst_methods, rb_method_name_enter_scope, inst_methods_enter_scope, 1);
735
+ rb_define_method(scope_inst_methods, rb_method_name_exit_scope, inst_methods_exit_scope, 1);
736
+
737
+ /*
738
+ * @module Contrast::Components::Scope
739
+ */
740
+ rb_define_singleton_method(scope_mod, "sweep_dead_ecs", scope_mod_sweep_dead_ecs, 0);
741
+
742
+ /*
743
+ * @module Contrast::Components::Scope::InstanceMethods
744
+ */
745
+ rb_define_method(scope_inst_methods, "contrast_enter_method_scopes!", inst_methods_enter_method_scope, 1);
746
+ rb_define_method(scope_inst_methods, "contrast_exit_method_scopes!", inst_methods_exit_method_scope, 1);
747
+ }