immunio 0.15.2

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 (157) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +234 -0
  3. data/README.md +147 -0
  4. data/bin/immunio +5 -0
  5. data/lib/immunio.rb +29 -0
  6. data/lib/immunio/agent.rb +260 -0
  7. data/lib/immunio/authentication.rb +96 -0
  8. data/lib/immunio/blocked_app.rb +38 -0
  9. data/lib/immunio/channel.rb +432 -0
  10. data/lib/immunio/cli.rb +39 -0
  11. data/lib/immunio/context.rb +114 -0
  12. data/lib/immunio/errors.rb +43 -0
  13. data/lib/immunio/immunio_ca.crt +45 -0
  14. data/lib/immunio/logger.rb +87 -0
  15. data/lib/immunio/plugins/action_dispatch.rb +45 -0
  16. data/lib/immunio/plugins/action_view.rb +431 -0
  17. data/lib/immunio/plugins/active_record.rb +707 -0
  18. data/lib/immunio/plugins/active_record_relation.rb +370 -0
  19. data/lib/immunio/plugins/authlogic.rb +80 -0
  20. data/lib/immunio/plugins/csrf.rb +24 -0
  21. data/lib/immunio/plugins/devise.rb +40 -0
  22. data/lib/immunio/plugins/environment_reporter.rb +69 -0
  23. data/lib/immunio/plugins/eval.rb +51 -0
  24. data/lib/immunio/plugins/exception_handler.rb +55 -0
  25. data/lib/immunio/plugins/gems_tracker.rb +5 -0
  26. data/lib/immunio/plugins/haml.rb +36 -0
  27. data/lib/immunio/plugins/http_finisher.rb +50 -0
  28. data/lib/immunio/plugins/http_tracker.rb +203 -0
  29. data/lib/immunio/plugins/io.rb +96 -0
  30. data/lib/immunio/plugins/redirect.rb +42 -0
  31. data/lib/immunio/plugins/warden.rb +66 -0
  32. data/lib/immunio/processor.rb +234 -0
  33. data/lib/immunio/rails.rb +26 -0
  34. data/lib/immunio/request.rb +139 -0
  35. data/lib/immunio/rufus_lua_ext/ref.rb +27 -0
  36. data/lib/immunio/rufus_lua_ext/state.rb +157 -0
  37. data/lib/immunio/rufus_lua_ext/table.rb +137 -0
  38. data/lib/immunio/rufus_lua_ext/utils.rb +13 -0
  39. data/lib/immunio/version.rb +5 -0
  40. data/lib/immunio/vm.rb +291 -0
  41. data/lua-hooks/ext/all.c +78 -0
  42. data/lua-hooks/ext/bitop/README +22 -0
  43. data/lua-hooks/ext/bitop/bit.c +189 -0
  44. data/lua-hooks/ext/extconf.rb +38 -0
  45. data/lua-hooks/ext/libinjection/COPYING +37 -0
  46. data/lua-hooks/ext/libinjection/libinjection.h +65 -0
  47. data/lua-hooks/ext/libinjection/libinjection_html5.c +847 -0
  48. data/lua-hooks/ext/libinjection/libinjection_html5.h +54 -0
  49. data/lua-hooks/ext/libinjection/libinjection_sqli.c +2301 -0
  50. data/lua-hooks/ext/libinjection/libinjection_sqli.h +295 -0
  51. data/lua-hooks/ext/libinjection/libinjection_sqli_data.h +9349 -0
  52. data/lua-hooks/ext/libinjection/libinjection_xss.c +531 -0
  53. data/lua-hooks/ext/libinjection/libinjection_xss.h +21 -0
  54. data/lua-hooks/ext/libinjection/lualib.c +109 -0
  55. data/lua-hooks/ext/lpeg/HISTORY +90 -0
  56. data/lua-hooks/ext/lpeg/lpcap.c +537 -0
  57. data/lua-hooks/ext/lpeg/lpcap.h +43 -0
  58. data/lua-hooks/ext/lpeg/lpcode.c +986 -0
  59. data/lua-hooks/ext/lpeg/lpcode.h +34 -0
  60. data/lua-hooks/ext/lpeg/lpeg-128.gif +0 -0
  61. data/lua-hooks/ext/lpeg/lpeg.html +1429 -0
  62. data/lua-hooks/ext/lpeg/lpprint.c +244 -0
  63. data/lua-hooks/ext/lpeg/lpprint.h +35 -0
  64. data/lua-hooks/ext/lpeg/lptree.c +1238 -0
  65. data/lua-hooks/ext/lpeg/lptree.h +77 -0
  66. data/lua-hooks/ext/lpeg/lptypes.h +149 -0
  67. data/lua-hooks/ext/lpeg/lpvm.c +355 -0
  68. data/lua-hooks/ext/lpeg/lpvm.h +58 -0
  69. data/lua-hooks/ext/lpeg/makefile +55 -0
  70. data/lua-hooks/ext/lpeg/re.html +498 -0
  71. data/lua-hooks/ext/lpeg/test.lua +1409 -0
  72. data/lua-hooks/ext/lua-cmsgpack/CMakeLists.txt +45 -0
  73. data/lua-hooks/ext/lua-cmsgpack/README.md +115 -0
  74. data/lua-hooks/ext/lua-cmsgpack/lua_cmsgpack.c +957 -0
  75. data/lua-hooks/ext/lua-cmsgpack/test.lua +570 -0
  76. data/lua-hooks/ext/lua-snapshot/LICENSE +7 -0
  77. data/lua-hooks/ext/lua-snapshot/Makefile +12 -0
  78. data/lua-hooks/ext/lua-snapshot/README.md +18 -0
  79. data/lua-hooks/ext/lua-snapshot/dump.lua +15 -0
  80. data/lua-hooks/ext/lua-snapshot/snapshot.c +455 -0
  81. data/lua-hooks/ext/lua/COPYRIGHT +34 -0
  82. data/lua-hooks/ext/lua/lapi.c +1087 -0
  83. data/lua-hooks/ext/lua/lapi.h +16 -0
  84. data/lua-hooks/ext/lua/lauxlib.c +652 -0
  85. data/lua-hooks/ext/lua/lauxlib.h +174 -0
  86. data/lua-hooks/ext/lua/lbaselib.c +659 -0
  87. data/lua-hooks/ext/lua/lcode.c +831 -0
  88. data/lua-hooks/ext/lua/lcode.h +76 -0
  89. data/lua-hooks/ext/lua/ldblib.c +398 -0
  90. data/lua-hooks/ext/lua/ldebug.c +638 -0
  91. data/lua-hooks/ext/lua/ldebug.h +33 -0
  92. data/lua-hooks/ext/lua/ldo.c +519 -0
  93. data/lua-hooks/ext/lua/ldo.h +57 -0
  94. data/lua-hooks/ext/lua/ldump.c +164 -0
  95. data/lua-hooks/ext/lua/lfunc.c +174 -0
  96. data/lua-hooks/ext/lua/lfunc.h +34 -0
  97. data/lua-hooks/ext/lua/lgc.c +710 -0
  98. data/lua-hooks/ext/lua/lgc.h +110 -0
  99. data/lua-hooks/ext/lua/linit.c +38 -0
  100. data/lua-hooks/ext/lua/liolib.c +556 -0
  101. data/lua-hooks/ext/lua/llex.c +463 -0
  102. data/lua-hooks/ext/lua/llex.h +81 -0
  103. data/lua-hooks/ext/lua/llimits.h +128 -0
  104. data/lua-hooks/ext/lua/lmathlib.c +263 -0
  105. data/lua-hooks/ext/lua/lmem.c +86 -0
  106. data/lua-hooks/ext/lua/lmem.h +49 -0
  107. data/lua-hooks/ext/lua/loadlib.c +705 -0
  108. data/lua-hooks/ext/lua/loadlib_rel.c +760 -0
  109. data/lua-hooks/ext/lua/lobject.c +214 -0
  110. data/lua-hooks/ext/lua/lobject.h +381 -0
  111. data/lua-hooks/ext/lua/lopcodes.c +102 -0
  112. data/lua-hooks/ext/lua/lopcodes.h +268 -0
  113. data/lua-hooks/ext/lua/loslib.c +243 -0
  114. data/lua-hooks/ext/lua/lparser.c +1339 -0
  115. data/lua-hooks/ext/lua/lparser.h +82 -0
  116. data/lua-hooks/ext/lua/lstate.c +214 -0
  117. data/lua-hooks/ext/lua/lstate.h +169 -0
  118. data/lua-hooks/ext/lua/lstring.c +111 -0
  119. data/lua-hooks/ext/lua/lstring.h +31 -0
  120. data/lua-hooks/ext/lua/lstrlib.c +871 -0
  121. data/lua-hooks/ext/lua/ltable.c +588 -0
  122. data/lua-hooks/ext/lua/ltable.h +40 -0
  123. data/lua-hooks/ext/lua/ltablib.c +287 -0
  124. data/lua-hooks/ext/lua/ltm.c +75 -0
  125. data/lua-hooks/ext/lua/ltm.h +54 -0
  126. data/lua-hooks/ext/lua/lua.c +392 -0
  127. data/lua-hooks/ext/lua/lua.def +131 -0
  128. data/lua-hooks/ext/lua/lua.h +388 -0
  129. data/lua-hooks/ext/lua/lua.rc +28 -0
  130. data/lua-hooks/ext/lua/lua_dll.rc +26 -0
  131. data/lua-hooks/ext/lua/luac.c +200 -0
  132. data/lua-hooks/ext/lua/luac.rc +1 -0
  133. data/lua-hooks/ext/lua/luaconf.h +763 -0
  134. data/lua-hooks/ext/lua/luaconf.h.in +724 -0
  135. data/lua-hooks/ext/lua/luaconf.h.orig +763 -0
  136. data/lua-hooks/ext/lua/lualib.h +53 -0
  137. data/lua-hooks/ext/lua/lundump.c +227 -0
  138. data/lua-hooks/ext/lua/lundump.h +36 -0
  139. data/lua-hooks/ext/lua/lvm.c +767 -0
  140. data/lua-hooks/ext/lua/lvm.h +36 -0
  141. data/lua-hooks/ext/lua/lzio.c +82 -0
  142. data/lua-hooks/ext/lua/lzio.h +67 -0
  143. data/lua-hooks/ext/lua/print.c +227 -0
  144. data/lua-hooks/ext/luautf8/README.md +152 -0
  145. data/lua-hooks/ext/luautf8/lutf8lib.c +1274 -0
  146. data/lua-hooks/ext/luautf8/unidata.h +3064 -0
  147. data/lua-hooks/lib/boot.lua +254 -0
  148. data/lua-hooks/lib/encode.lua +4 -0
  149. data/lua-hooks/lib/lexers/LICENSE +21 -0
  150. data/lua-hooks/lib/lexers/bash.lua +134 -0
  151. data/lua-hooks/lib/lexers/bash_dqstr.lua +62 -0
  152. data/lua-hooks/lib/lexers/css.lua +216 -0
  153. data/lua-hooks/lib/lexers/html.lua +106 -0
  154. data/lua-hooks/lib/lexers/javascript.lua +68 -0
  155. data/lua-hooks/lib/lexers/lexer.lua +1575 -0
  156. data/lua-hooks/lib/lexers/markers.lua +33 -0
  157. metadata +308 -0
@@ -0,0 +1,370 @@
1
+ # Wrap methods to keep track of ActiveRecord::Relation method calls and query
2
+ # executions.
3
+
4
+ module Immunio
5
+ module RelationHooks
6
+ extend ActiveSupport::Concern
7
+
8
+ # ActiveRecord will "spawn", or clone, relations under many circumstances.
9
+ # For example, #where will first spawn the relation, then add the where
10
+ # clause to the new relation, then return the new relation. This is so
11
+ # you can do something like:
12
+ #
13
+ # prods = Product.where(cost: 10)
14
+ #
15
+ # # Return blue products costing $10
16
+ # prods_blue = prods.where(color: 'blue').to_a
17
+ #
18
+ # # Return count of all products costing $10
19
+ # prods_count = prods.count
20
+ #
21
+ # We must track the prods relation and the relations returned by the color
22
+ # where call separately.
23
+ def clone_with_immunio
24
+ cloned = clone_without_immunio
25
+
26
+ Request.time "plugin", "Immunio::RelationTracking" do
27
+ QueryTracker.instance.spawn_relation self, cloned
28
+ end
29
+
30
+ cloned
31
+ end
32
+
33
+ included do
34
+ alias_method :clone_without_immunio, :clone
35
+ alias_method :clone, :clone_with_immunio
36
+ end
37
+ end
38
+
39
+ module SpawnHooks
40
+ extend ActiveSupport::Concern
41
+ # Sometimes ActiveRecord creates a new relation with a new condition and
42
+ # merges it into an existing relation. I'm not sure why, and I'm not going
43
+ # to ask. Just copy the context data from the other relation into this one.
44
+ def merge_with_immunio(other)
45
+ return merge_without_immunio(other) unless other && !other.is_a?(Array)
46
+
47
+ # Rails 4 added the ability to call merge with a proc, like:
48
+ #
49
+ # relation.merge(-> { where(:foo) })
50
+ #
51
+ # We don't need to do anything here. If the proc calls relation methods,
52
+ # they will be called on the right relation and everything will be good.
53
+ if !other.is_a?(ActiveRecord::Relation) && other.respond_to?(:to_proc)
54
+ return merge_without_immunio(other)
55
+ end
56
+
57
+ # Rails 4 added the ability to merge in a hash of conditions, like:
58
+ #
59
+ # Developer.merge(where: 'projects IS NOT NULL')
60
+ #
61
+ # Use the internal HashMerger to build a real relation from the hash
62
+ # before merging values into the spawned relation.
63
+ if other.is_a?(Hash)
64
+ # This shouldn't happen, but let's be safe.
65
+ unless defined? ActiveRecord::Relation::HashMerger
66
+ return merge_without_immunio(other)
67
+ end
68
+
69
+ other = ActiveRecord::Relation::HashMerger.new(self, other).other
70
+ end
71
+
72
+ spawned = merge_without_immunio(other)
73
+ Request.time "plugin", "Immunio::RelationTracking" do
74
+ QueryTracker.instance.merge_relations spawned, other
75
+ end
76
+ spawned
77
+ end
78
+
79
+ included do
80
+ alias_method :merge_without_immunio, :merge
81
+ alias_method :merge, :merge_with_immunio
82
+ end
83
+ end
84
+
85
+ module QueryingHooks
86
+ extend ActiveSupport::Concern
87
+
88
+ class << self
89
+ attr_accessor :methods_to_wrap
90
+ end
91
+
92
+ self.methods_to_wrap = []
93
+
94
+ # Wrap ActiveRecord::Relation methods to add API calls to the context data
95
+ # for a query. Some additional methods are wrapped because they execute
96
+ # queries. In those cases, we must associate the connection to the relation
97
+ # so we can grab the context info when we see the query being executed.
98
+ def self.wrap_method(method)
99
+ method_with_immunio = "#{method}_with_immunio".to_sym
100
+ method_without_immunio = "#{method}_without_immunio".to_sym
101
+
102
+ define_method method_with_immunio do |*args, &block|
103
+ Request.time "plugin", "Immunio::RelationTracking" do
104
+ # Construct the context data. The name method returns the name of the
105
+ # class of self, which is the name of the model. This allows us to
106
+ # differentiate between scopes from the same lines of code but on
107
+ # different models.
108
+ #
109
+ # In Ruby 2 and up, we can ask for specific frames and the bits we
110
+ # need from them. This doesn't impact performance much. But in earlier
111
+ # versions of Ruby we would need to gather the entire stack trace and
112
+ # parse the strings for the frames we need. As that is too much of a
113
+ # performance hit, we don't include caller information in the
114
+ # additional context data.
115
+ if defined? caller_locations
116
+ stack = caller_locations(4..5)
117
+
118
+ # If the method was called on a relation directly, the real caller
119
+ # is four frames up. But if the method was called on the model
120
+ # class, like `User.where`, then there is an extra frame for
121
+ # delegating the method call to the result of the `all` method. We
122
+ # look for the telltale sign of the caller being in the querying.rb
123
+ # file of ActiveRecord and with the same name as the method.
124
+ if stack[0].path.end_with?('querying.rb') && stack[0].label == method.to_s
125
+ frame = stack[1]
126
+ else
127
+ frame = stack[0]
128
+ end
129
+
130
+ caller_method = frame.label
131
+ caller_line = frame.lineno
132
+
133
+ data = "Relation for #{name}, method called: #{method}, caller: #{caller_method}:#{caller_line}"
134
+ else
135
+ data = "Relation for #{name}, method called: #{method}"
136
+ end
137
+
138
+ # In Rails 3, #where and #having clone the relation and return it with
139
+ # the conditions added, but use the original relation to actually
140
+ # build the conditions. We have to add a hack to push the last cloned
141
+ # relation onto the connection's relation stack so parameters are
142
+ # associated with the cloned relation and not the original relation.
143
+ relation = if method == :build_where && Rails::VERSION::MAJOR == 3
144
+ QueryTracker.instance.last_spawned_relation connection
145
+ else
146
+ self
147
+ end # rubocop:disable Lint/EndAlignment
148
+
149
+ # Push the current relation onto the stack of relations for the
150
+ # connection. The top relation on the stack at query execution time is
151
+ # used for contextual data.
152
+ QueryTracker.instance.push_relation relation
153
+
154
+ begin
155
+ # Call original method
156
+ ret = Request.pause "plugin", "Immunio::RelationTracking" do
157
+ send method_without_immunio, *args, &block
158
+ end
159
+
160
+ # These two methods create clones of the original without actually
161
+ # calling #clone. Copy relation data over in this special case.
162
+ if method == :except || method == :only
163
+ QueryTracker.instance.spawn_relation self, ret
164
+ end
165
+
166
+ # If ret is a relation, such as when #where is called, we need to
167
+ # add data about the API call to the relation. Otherwise, the API
168
+ # call is returning actual data from a query execution, like #count.
169
+ # We don't need to add context data about calls that execute queries
170
+ # because the stack trace at query execution time will include this
171
+ # call.
172
+ if ret.is_a? ActiveRecord::Relation
173
+ QueryTracker.instance.add_relation_data ret, data
174
+ end
175
+ ensure
176
+ QueryTracker.instance.pop_relation relation
177
+ end
178
+
179
+ ret
180
+ end
181
+ end
182
+ end
183
+
184
+ if defined? ActiveRecord::Querying
185
+ # Public ActiveRecord::Relation API methods we want to add context for.
186
+ # Examples: #where, #group, #order, #joins
187
+ self.methods_to_wrap = ActiveRecord::Querying.public_instance_methods
188
+
189
+ # Additional methods that execute queries. We must push the
190
+ # ActiveRecord::Relation onto the stack of relations for the connection so
191
+ # we can grab its contextual data when the query is executed.
192
+ self.methods_to_wrap += [
193
+ :insert,
194
+ :update_record,
195
+ :_update_record,
196
+ :exec_queries,
197
+ :find_with_associations,
198
+ :limited_ids_for,
199
+ :execute_simple_calculation,
200
+ :execute_grouped_calculation
201
+ ]
202
+
203
+ # Additional methods that sanitize SQL fragments, which we wrap to gather
204
+ # parameters. We wrap these methods to ensure the proper relation is at
205
+ # the top of the connection relation stack so we save parameters to the
206
+ # correct relation.
207
+ self.methods_to_wrap += [
208
+ :having!,
209
+ :where!,
210
+ :build_where
211
+ ]
212
+ end
213
+
214
+ self.methods_to_wrap.each do |method|
215
+ wrap_method method
216
+ end
217
+
218
+ included do
219
+ methods = Immunio::QueryingHooks.methods_to_wrap
220
+ methods.keep_if do |method|
221
+ next unless method_defined?(method) || private_method_defined?(method)
222
+
223
+ method_with_immunio = "#{method}_with_immunio".to_sym
224
+ method_without_immunio = "#{method}_without_immunio".to_sym
225
+
226
+ alias_method method_without_immunio, method
227
+ alias_method method, method_with_immunio
228
+
229
+ true
230
+ end
231
+
232
+ Immunio.logger.debug {"Wrapped ActiveRecord::Relation methods for context: #{methods.join ", "}"}
233
+ end
234
+ end
235
+
236
+ # Some simple queries, specifically #find and #find_by with simple conditions
237
+ # may be saved in a statement cache so the statement doesn't need to be built
238
+ # from an AST every time. When a query is executed from the cache, the cached
239
+ # query statement is executed without going through any of the wrapped
240
+ # ActiveRecord::Relation methods. We wrap the execution to ensure we push the
241
+ # original relation for the cached statement onto the connection relation
242
+ # stack.
243
+ #
244
+ # Note: Because cached statements won't build up the query from an AST, we
245
+ # won't have AST context data. That should be ok because only the simplest
246
+ # queries are cacheable, queries that won't vary in structure even for the
247
+ # same stack trace and relation API calls.
248
+ module StatementCacheHooks
249
+ extend ActiveSupport::Concern
250
+
251
+ module ClassMethods
252
+ # When a statement is cached, store the final relation used to generate
253
+ # the statement in a special immunio instance variable.
254
+ def create_with_immunio(*args, &block)
255
+ # The relation is returned by the block passed into the create method.
256
+ # This is more fragile than most of our method wrappings as we depend on
257
+ # *how* the method works, but there's no way around it.
258
+ relation = block.call ActiveRecord::StatementCache::Params.new
259
+
260
+ ret = create_without_immunio(*args) { relation }
261
+ ret.instance_variable_set :@__immunio_relation, relation
262
+ ret
263
+ end
264
+ end
265
+
266
+ # When a statement is executed, push the original relation onto the
267
+ # connection relation stack.
268
+ def execute_with_immunio(*args)
269
+ Request.time "plugin", "Immunio::RelationTracking" do
270
+ QueryTracker.instance.push_relation @__immunio_relation
271
+ end
272
+
273
+ begin
274
+ execute_without_immunio(*args)
275
+ ensure
276
+ Request.time "plugin", "Immunio::RelationTracking" do
277
+ QueryTracker.instance.pop_relation @__immunio_relation
278
+ end
279
+ end
280
+ end
281
+
282
+ included do
283
+ # StatementCache is available since 4.0, but not used by ActiveRecord
284
+ # itself until 4.2. The signature changed in 4.2, so we wrap the 4.2
285
+ # version and don't bother with previous versions. If any apps use
286
+ # StatementCache directly on Rails 4.0 or 4.1, we will not propagate
287
+ # relational context data properly.
288
+ if ActiveRecord::StatementCache.singleton_class.method_defined? :create
289
+ singleton_class.send :alias_method, :create_without_immunio, :create
290
+ singleton_class.send :alias_method, :create, :create_with_immunio
291
+
292
+ alias_method :execute_without_immunio, :execute
293
+ alias_method :execute, :execute_with_immunio
294
+ end
295
+ end
296
+ end
297
+
298
+ module HasManyThroughAssociationHooks
299
+ extend ActiveSupport::Concern
300
+
301
+ private
302
+ # One off, non-ActiveRecord::Relation method that under one condition
303
+ # executes a query in Rails 4.1 and up. Unfortunately, wrapping won't work
304
+ # as the relation used to generate the query is a temporary relation that is
305
+ # created in the method. The easiest way to deal with it, though very hacky,
306
+ # is to copy the method from upstream Rails and push the temporary relation
307
+ # onto the stack for the connection right before the query is executed.
308
+ def delete_records_with_immunio(records, method, *args)
309
+ scope = through_association.scope
310
+
311
+ unless method == :destroy && !scope.klass.primary_key
312
+ return delete_records_without_immunio(records, method, *args)
313
+ end
314
+
315
+ # From here on down, copied from upstream Rails 4.2. Verified to work on
316
+ # Rails 4.1, too.
317
+ ensure_not_nested
318
+
319
+ scope.where! construct_join_attributes(*records)
320
+
321
+ scope.each do |record|
322
+ record.run_callbacks :destroy
323
+ end
324
+
325
+ arel = scope.arel
326
+
327
+ stmt = Arel::DeleteManager.new arel.engine
328
+ stmt.from scope.klass.arel_table
329
+ stmt.wheres = arel.constraints
330
+
331
+ Request.time "plugin", "Immunio::RelationTracking" do
332
+ QueryTracker.instance.push_relation scope
333
+ end
334
+
335
+ begin
336
+ count = scope.klass.connection.delete(stmt, 'SQL', scope.bind_values)
337
+ ensure
338
+ Request.time "plugin", "Immunio::RelationTracking" do
339
+ QueryTracker.instance.pop_relation scope
340
+ end
341
+ end
342
+
343
+ delete_through_records(records)
344
+
345
+ if source_reflection.options[:counter_cache] && method != :destroy
346
+ counter = source_reflection.counter_cache_column
347
+ klass.decrement_counter counter, records.map(&:id)
348
+ end
349
+
350
+ if through_reflection.collection? && update_through_counter?(method)
351
+ update_counter(-count, through_reflection)
352
+ end
353
+
354
+ update_counter(-count)
355
+ end
356
+
357
+ included do
358
+ if Rails::VERSION::MAJOR >= 4 && Rails::VERSION::MINOR >= 1
359
+ alias_method :delete_records_without_immunio, :delete_records
360
+ alias_method :delete_records, :delete_records_with_immunio
361
+ end
362
+ end
363
+ end
364
+ end
365
+
366
+ ActiveRecord::Relation.send :include, Immunio::RelationHooks if defined? ActiveRecord::Relation
367
+ ActiveRecord::Relation.send :include, Immunio::SpawnHooks if defined? ActiveRecord::SpawnMethods
368
+ ActiveRecord::Relation.send :include, Immunio::QueryingHooks if defined? ActiveRecord::Querying
369
+ ActiveRecord::StatementCache.send :include, Immunio::StatementCacheHooks if defined? ActiveRecord::StatementCache
370
+ ActiveRecord::Associations::HasManyThroughAssociation.send :include, Immunio::HasManyThroughAssociationHooks if defined? ActiveRecord::Associations::HasManyThroughAssociation
@@ -0,0 +1,80 @@
1
+ # Register callbacks to Authlogic (https://github.com/binarylogic/authlogic). A popular authentication system.
2
+
3
+ begin
4
+ require "authlogic"
5
+ rescue LoadError # rubocop:disable Lint/HandleExceptions
6
+ # Ignore
7
+ end
8
+
9
+ if defined? Authlogic
10
+ module Immunio
11
+ module Authlogic
12
+ module SessionHooks
13
+ def self.included(klass)
14
+ klass.class_eval do
15
+ include InstanceMethods
16
+ after_create :immunio_login
17
+ validate :immunio_check_failed_login
18
+ before_destroy :immunio_logout
19
+ after_persisting :immunio_set_user
20
+ end
21
+ end
22
+
23
+ module InstanceMethods
24
+ private
25
+ def opts
26
+ info = {plugin: "authlogic"}
27
+
28
+ if defined?(:record) && record
29
+ # record is set when already logged in, e.g. you are now logging out
30
+ info[:user_record] = record
31
+ elsif defined?(:attempted_record) && attempted_record
32
+ # attempted_record is set when attempting to log in and the user record has been fetched
33
+ info[:user_record] = attempted_record
34
+ end
35
+
36
+ # credentials are set when the user attempts to log in
37
+ if defined?(:credentials) && defined?(:login_field)
38
+ login = credentials[login_field.to_sym]
39
+ info[:username] = login if login
40
+ end
41
+
42
+ info
43
+ end
44
+
45
+ def immunio_login
46
+ Immunio::Request.time "plugin", "#{Module.nesting[0]}::#{__method__}" do
47
+ Immunio.logger.debug {"Authlogic instrumentation fired for login with opts #{opts}"}
48
+ Immunio.login opts
49
+ end
50
+ end
51
+
52
+ def immunio_check_failed_login
53
+ if errors.any?
54
+ Immunio::Request.time "plugin", "#{Module.nesting[0]}::#{__method__}" do
55
+ Immunio.logger.debug "Authlogic instrumentation fired for before_failure with opts #{opts}"
56
+ Immunio.failed_login opts
57
+ end
58
+ end
59
+ end
60
+
61
+ def immunio_logout
62
+ Immunio::Request.time "plugin", "#{Module.nesting[0]}::#{__method__}" do
63
+ Immunio.logger.debug "Authlogic instrumentation fired for logout with opts #{opts}"
64
+ Immunio.logout opts
65
+ end
66
+ end
67
+
68
+ def immunio_set_user
69
+ Immunio::Request.time "plugin", "#{Module.nesting[0]}::#{__method__}" do
70
+ Immunio.logger.debug "Authlogic instrumentation fired for after_set_user with opts #{opts}"
71
+ Immunio.set_user opts
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ Authlogic::Session::Base.send :include, Immunio::Authlogic::SessionHooks
80
+ end
@@ -0,0 +1,24 @@
1
+ module Immunio
2
+ module CsrfHook
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ alias_method_chain :verify_authenticity_token, :immunio if method_defined? :verify_authenticity_token
7
+ end
8
+
9
+ protected
10
+ def verify_authenticity_token_with_immunio
11
+ Request.time "plugin", "#{Module.nesting[0]}::#{__method__}" do
12
+ Immunio.logger.debug "ActiveSupport checking CSRF token"
13
+
14
+ Immunio.run_hook! "csrf", "framework_csrf_check", valid: verified_request?
15
+
16
+ Request.pause "plugin", "#{Module.nesting[0]}::#{__method__}" do
17
+ verify_authenticity_token_without_immunio
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ ActionController::Base.send :include, Immunio::CsrfHook