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.
- 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
|