immunio 0.15.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +234 -0
- data/README.md +147 -0
- data/bin/immunio +5 -0
- data/lib/immunio.rb +29 -0
- data/lib/immunio/agent.rb +260 -0
- data/lib/immunio/authentication.rb +96 -0
- data/lib/immunio/blocked_app.rb +38 -0
- data/lib/immunio/channel.rb +432 -0
- data/lib/immunio/cli.rb +39 -0
- data/lib/immunio/context.rb +114 -0
- data/lib/immunio/errors.rb +43 -0
- data/lib/immunio/immunio_ca.crt +45 -0
- data/lib/immunio/logger.rb +87 -0
- data/lib/immunio/plugins/action_dispatch.rb +45 -0
- data/lib/immunio/plugins/action_view.rb +431 -0
- data/lib/immunio/plugins/active_record.rb +707 -0
- data/lib/immunio/plugins/active_record_relation.rb +370 -0
- data/lib/immunio/plugins/authlogic.rb +80 -0
- data/lib/immunio/plugins/csrf.rb +24 -0
- data/lib/immunio/plugins/devise.rb +40 -0
- data/lib/immunio/plugins/environment_reporter.rb +69 -0
- data/lib/immunio/plugins/eval.rb +51 -0
- data/lib/immunio/plugins/exception_handler.rb +55 -0
- data/lib/immunio/plugins/gems_tracker.rb +5 -0
- data/lib/immunio/plugins/haml.rb +36 -0
- data/lib/immunio/plugins/http_finisher.rb +50 -0
- data/lib/immunio/plugins/http_tracker.rb +203 -0
- data/lib/immunio/plugins/io.rb +96 -0
- data/lib/immunio/plugins/redirect.rb +42 -0
- data/lib/immunio/plugins/warden.rb +66 -0
- data/lib/immunio/processor.rb +234 -0
- data/lib/immunio/rails.rb +26 -0
- data/lib/immunio/request.rb +139 -0
- data/lib/immunio/rufus_lua_ext/ref.rb +27 -0
- data/lib/immunio/rufus_lua_ext/state.rb +157 -0
- data/lib/immunio/rufus_lua_ext/table.rb +137 -0
- data/lib/immunio/rufus_lua_ext/utils.rb +13 -0
- data/lib/immunio/version.rb +5 -0
- data/lib/immunio/vm.rb +291 -0
- data/lua-hooks/ext/all.c +78 -0
- data/lua-hooks/ext/bitop/README +22 -0
- data/lua-hooks/ext/bitop/bit.c +189 -0
- data/lua-hooks/ext/extconf.rb +38 -0
- data/lua-hooks/ext/libinjection/COPYING +37 -0
- data/lua-hooks/ext/libinjection/libinjection.h +65 -0
- data/lua-hooks/ext/libinjection/libinjection_html5.c +847 -0
- data/lua-hooks/ext/libinjection/libinjection_html5.h +54 -0
- data/lua-hooks/ext/libinjection/libinjection_sqli.c +2301 -0
- data/lua-hooks/ext/libinjection/libinjection_sqli.h +295 -0
- data/lua-hooks/ext/libinjection/libinjection_sqli_data.h +9349 -0
- data/lua-hooks/ext/libinjection/libinjection_xss.c +531 -0
- data/lua-hooks/ext/libinjection/libinjection_xss.h +21 -0
- data/lua-hooks/ext/libinjection/lualib.c +109 -0
- data/lua-hooks/ext/lpeg/HISTORY +90 -0
- data/lua-hooks/ext/lpeg/lpcap.c +537 -0
- data/lua-hooks/ext/lpeg/lpcap.h +43 -0
- data/lua-hooks/ext/lpeg/lpcode.c +986 -0
- data/lua-hooks/ext/lpeg/lpcode.h +34 -0
- data/lua-hooks/ext/lpeg/lpeg-128.gif +0 -0
- data/lua-hooks/ext/lpeg/lpeg.html +1429 -0
- data/lua-hooks/ext/lpeg/lpprint.c +244 -0
- data/lua-hooks/ext/lpeg/lpprint.h +35 -0
- data/lua-hooks/ext/lpeg/lptree.c +1238 -0
- data/lua-hooks/ext/lpeg/lptree.h +77 -0
- data/lua-hooks/ext/lpeg/lptypes.h +149 -0
- data/lua-hooks/ext/lpeg/lpvm.c +355 -0
- data/lua-hooks/ext/lpeg/lpvm.h +58 -0
- data/lua-hooks/ext/lpeg/makefile +55 -0
- data/lua-hooks/ext/lpeg/re.html +498 -0
- data/lua-hooks/ext/lpeg/test.lua +1409 -0
- data/lua-hooks/ext/lua-cmsgpack/CMakeLists.txt +45 -0
- data/lua-hooks/ext/lua-cmsgpack/README.md +115 -0
- data/lua-hooks/ext/lua-cmsgpack/lua_cmsgpack.c +957 -0
- data/lua-hooks/ext/lua-cmsgpack/test.lua +570 -0
- data/lua-hooks/ext/lua-snapshot/LICENSE +7 -0
- data/lua-hooks/ext/lua-snapshot/Makefile +12 -0
- data/lua-hooks/ext/lua-snapshot/README.md +18 -0
- data/lua-hooks/ext/lua-snapshot/dump.lua +15 -0
- data/lua-hooks/ext/lua-snapshot/snapshot.c +455 -0
- data/lua-hooks/ext/lua/COPYRIGHT +34 -0
- data/lua-hooks/ext/lua/lapi.c +1087 -0
- data/lua-hooks/ext/lua/lapi.h +16 -0
- data/lua-hooks/ext/lua/lauxlib.c +652 -0
- data/lua-hooks/ext/lua/lauxlib.h +174 -0
- data/lua-hooks/ext/lua/lbaselib.c +659 -0
- data/lua-hooks/ext/lua/lcode.c +831 -0
- data/lua-hooks/ext/lua/lcode.h +76 -0
- data/lua-hooks/ext/lua/ldblib.c +398 -0
- data/lua-hooks/ext/lua/ldebug.c +638 -0
- data/lua-hooks/ext/lua/ldebug.h +33 -0
- data/lua-hooks/ext/lua/ldo.c +519 -0
- data/lua-hooks/ext/lua/ldo.h +57 -0
- data/lua-hooks/ext/lua/ldump.c +164 -0
- data/lua-hooks/ext/lua/lfunc.c +174 -0
- data/lua-hooks/ext/lua/lfunc.h +34 -0
- data/lua-hooks/ext/lua/lgc.c +710 -0
- data/lua-hooks/ext/lua/lgc.h +110 -0
- data/lua-hooks/ext/lua/linit.c +38 -0
- data/lua-hooks/ext/lua/liolib.c +556 -0
- data/lua-hooks/ext/lua/llex.c +463 -0
- data/lua-hooks/ext/lua/llex.h +81 -0
- data/lua-hooks/ext/lua/llimits.h +128 -0
- data/lua-hooks/ext/lua/lmathlib.c +263 -0
- data/lua-hooks/ext/lua/lmem.c +86 -0
- data/lua-hooks/ext/lua/lmem.h +49 -0
- data/lua-hooks/ext/lua/loadlib.c +705 -0
- data/lua-hooks/ext/lua/loadlib_rel.c +760 -0
- data/lua-hooks/ext/lua/lobject.c +214 -0
- data/lua-hooks/ext/lua/lobject.h +381 -0
- data/lua-hooks/ext/lua/lopcodes.c +102 -0
- data/lua-hooks/ext/lua/lopcodes.h +268 -0
- data/lua-hooks/ext/lua/loslib.c +243 -0
- data/lua-hooks/ext/lua/lparser.c +1339 -0
- data/lua-hooks/ext/lua/lparser.h +82 -0
- data/lua-hooks/ext/lua/lstate.c +214 -0
- data/lua-hooks/ext/lua/lstate.h +169 -0
- data/lua-hooks/ext/lua/lstring.c +111 -0
- data/lua-hooks/ext/lua/lstring.h +31 -0
- data/lua-hooks/ext/lua/lstrlib.c +871 -0
- data/lua-hooks/ext/lua/ltable.c +588 -0
- data/lua-hooks/ext/lua/ltable.h +40 -0
- data/lua-hooks/ext/lua/ltablib.c +287 -0
- data/lua-hooks/ext/lua/ltm.c +75 -0
- data/lua-hooks/ext/lua/ltm.h +54 -0
- data/lua-hooks/ext/lua/lua.c +392 -0
- data/lua-hooks/ext/lua/lua.def +131 -0
- data/lua-hooks/ext/lua/lua.h +388 -0
- data/lua-hooks/ext/lua/lua.rc +28 -0
- data/lua-hooks/ext/lua/lua_dll.rc +26 -0
- data/lua-hooks/ext/lua/luac.c +200 -0
- data/lua-hooks/ext/lua/luac.rc +1 -0
- data/lua-hooks/ext/lua/luaconf.h +763 -0
- data/lua-hooks/ext/lua/luaconf.h.in +724 -0
- data/lua-hooks/ext/lua/luaconf.h.orig +763 -0
- data/lua-hooks/ext/lua/lualib.h +53 -0
- data/lua-hooks/ext/lua/lundump.c +227 -0
- data/lua-hooks/ext/lua/lundump.h +36 -0
- data/lua-hooks/ext/lua/lvm.c +767 -0
- data/lua-hooks/ext/lua/lvm.h +36 -0
- data/lua-hooks/ext/lua/lzio.c +82 -0
- data/lua-hooks/ext/lua/lzio.h +67 -0
- data/lua-hooks/ext/lua/print.c +227 -0
- data/lua-hooks/ext/luautf8/README.md +152 -0
- data/lua-hooks/ext/luautf8/lutf8lib.c +1274 -0
- data/lua-hooks/ext/luautf8/unidata.h +3064 -0
- data/lua-hooks/lib/boot.lua +254 -0
- data/lua-hooks/lib/encode.lua +4 -0
- data/lua-hooks/lib/lexers/LICENSE +21 -0
- data/lua-hooks/lib/lexers/bash.lua +134 -0
- data/lua-hooks/lib/lexers/bash_dqstr.lua +62 -0
- data/lua-hooks/lib/lexers/css.lua +216 -0
- data/lua-hooks/lib/lexers/html.lua +106 -0
- data/lua-hooks/lib/lexers/javascript.lua +68 -0
- data/lua-hooks/lib/lexers/lexer.lua +1575 -0
- data/lua-hooks/lib/lexers/markers.lua +33 -0
- 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
|