immunio 0.15.2

Sign up to get free protection for your applications and to get access to all the features.
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