searchgasm 0.9.1 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +3 -1
- data/Manifest +0 -1
- data/README.mdown +49 -36
- data/Rakefile +1 -1
- data/lib/searchgasm/active_record/associations.rb +18 -8
- data/lib/searchgasm/active_record/base.rb +38 -21
- data/lib/searchgasm/search/base.rb +52 -6
- data/lib/searchgasm/search/conditions.rb +26 -8
- data/lib/searchgasm/version.rb +1 -1
- data/lib/searchgasm.rb +0 -1
- data/searchgasm.gemspec +13 -5
- data/test/test_active_record_base.rb +17 -1
- data/test/test_searchgasm_base.rb +16 -0
- data/test/test_searchgasm_conditions.rb +10 -0
- metadata +12 -4
- data/lib/searchgasm/active_record/protection.rb +0 -37
data/CHANGELOG
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
-
v0.9.
|
1
|
+
v0.9.2. Enhanced protection
|
2
|
+
|
3
|
+
v0.9.1. Added aliases for datetime, date, time, and timestamp attrs. You could call created_at_after, mow you can also call created_after.
|
2
4
|
|
3
5
|
v0.9.0. First release
|
data/Manifest
CHANGED
data/README.mdown
CHANGED
@@ -2,12 +2,34 @@
|
|
2
2
|
|
3
3
|
Searchgasm is orgasmic. Maybe not orgasmic, but you will get aroused. So go grab a towel and let's dive in.
|
4
4
|
|
5
|
-
Searchgasm
|
5
|
+
Searchgasm was a super secret tool of mine until I decided to share with the world. It has saved me tons of time, allowed me to write less code, made searching painless, and kept my controllers DRY.
|
6
|
+
|
7
|
+
It originated to satisfy a VERY simple need: so that I could use my form builder when making search forms. Sounds simple right? The goal was to use an object, that represents a search, just like an ActiveRecord object in form\_for and fields\_for.
|
6
8
|
|
7
9
|
I'm a big fan of understanding what I'm using, so here's a quick explanation: The design behind this plugin is pretty simple. The search object "santiizes" down into the options passed into ActiveRecord::Base.find(). It basically serves as a transparent filter between you and ActiveRecord::Base.find(). This filter provides "enhancements" that get translated into options that ActiveRecord::Base.find() can understand. This doesn't step on the toes or dig into he internals of ActiveRecord. It uses what ActiveRecord provides publicly. Letting ActiveRecord do all of the hard work and keeping this plugin solid and less brittle.
|
8
10
|
|
9
11
|
Here's where you get aroused...
|
10
12
|
|
13
|
+
## Why I love Searchgasm!
|
14
|
+
|
15
|
+
# app/controllers/users_controller.rb
|
16
|
+
def index
|
17
|
+
@search = User.new_search(:conditions => params[:conditions])
|
18
|
+
@users, @users_count = @search.all, @search.count
|
19
|
+
end
|
20
|
+
|
21
|
+
Now your view:
|
22
|
+
|
23
|
+
# app/views/users/index.html.erb
|
24
|
+
<%= form_for :conditions, @search.conditions, :url => users_path do |f| %>
|
25
|
+
<%= f.text_field :first_name_contains %>
|
26
|
+
<%= f.calendar_date_select :created_after %> # nice rails plugin for replacing date_select
|
27
|
+
<% f.fields_for :orders, f.object.orders do |orders_f| %>
|
28
|
+
<%= f.select :total_gt, (1..100) %>
|
29
|
+
<% end %>
|
30
|
+
<%= f.submit %>
|
31
|
+
<% end %>
|
32
|
+
|
11
33
|
## Install and use
|
12
34
|
|
13
35
|
sudo gem install searchgasm
|
@@ -30,7 +52,7 @@ Now go into your console and try out any of these example with your own models.
|
|
30
52
|
|
31
53
|
**For all examples, let's assume the following relationships: User => Orders => Line Items**
|
32
54
|
|
33
|
-
##
|
55
|
+
## Simple Searching Example
|
34
56
|
|
35
57
|
User.all(
|
36
58
|
:conditions => {
|
@@ -44,7 +66,7 @@ Now go into your console and try out any of these example with your own models.
|
|
44
66
|
## Detailed Example w/ object based searching
|
45
67
|
|
46
68
|
# Instantiate
|
47
|
-
search = User.new_search(
|
69
|
+
@search = User.new_search(
|
48
70
|
:conditions => {
|
49
71
|
:first_name_contains => "Ben",
|
50
72
|
:age_gt => 18,
|
@@ -57,21 +79,23 @@ Now go into your console and try out any of these example with your own models.
|
|
57
79
|
)
|
58
80
|
|
59
81
|
# Set conditions on relationships
|
60
|
-
search.conditions.email_ends_with = "binarylogic.com"
|
61
|
-
search.conditions.oders.line_items.created_after = Time.now
|
82
|
+
@search.conditions.email_ends_with = "binarylogic.com"
|
83
|
+
@search.conditions.oders.line_items.created_after = Time.now
|
62
84
|
|
63
85
|
# Set options
|
64
|
-
search.per_page = 50 # overrides the 20 set above
|
86
|
+
@search.per_page = 50 # overrides the 20 set above
|
65
87
|
|
66
88
|
# Set ANY of the ActiveRecord options
|
67
|
-
search.group = "last_name"
|
68
|
-
search.readonly = true
|
89
|
+
@search.group = "last_name"
|
90
|
+
@search.readonly = true
|
69
91
|
# ... see ActiveRecord documentation
|
70
92
|
|
71
93
|
# Return results just like ActiveRecord
|
72
|
-
search.all
|
73
|
-
search.search # alias for all
|
74
|
-
search.first
|
94
|
+
@search.all
|
95
|
+
@search.search # alias for all
|
96
|
+
@search.first
|
97
|
+
|
98
|
+
Take the @search object and pass it right into form\_for or fields\_for (see above).
|
75
99
|
|
76
100
|
## Calculations
|
77
101
|
|
@@ -131,6 +155,8 @@ Again, if you want to be hardcore:
|
|
131
155
|
conditions.first_name_contains = "Ben"
|
132
156
|
conditions.search
|
133
157
|
|
158
|
+
Now pass the conditions object right into form\_for or fields\_for (see above for example).
|
159
|
+
|
134
160
|
## Scoped searching
|
135
161
|
|
136
162
|
@current_user.orders.find(:all, :conditions => {:total_lte => 500})
|
@@ -170,27 +196,24 @@ Not only can you use searchgasm when searching, but you can use it when setting
|
|
170
196
|
named_scope :sexy, :conditions => {:first_name => "Ben", email_ends_with => "binarylogic.com"}, :per_page => 20
|
171
197
|
end
|
172
198
|
|
173
|
-
## Always use protection
|
174
|
-
|
175
|
-
If there is one thing we in sex ed. it's to always use protection. As I mentioned above the purpose of this plugin was to create a search object and use it in form\_for or fields\_for. What about receiving the params in the controller and protecting against SQL injection?
|
199
|
+
## Always use protection...against SQL injections
|
176
200
|
|
177
|
-
|
178
|
-
accounts = Account.all_with_protection(params[:search])
|
179
|
-
account = Account.first_with_protection(params[:search])
|
201
|
+
If there is one thing we all know, it's to always use protection. That's why searchgasm protects you by default. The new\_search and new\_conditions methods are protected by default. This means that various checks are done to ensure it is not possible to perform any type of SQL injection. But this also limits how you can search, meaning you can't write raw SQL. If you want to be daring and search without protection prepare to accept the consequences. All that you have to do is add ! to the end of the methods: new\_search! and new\_conditions!. That was an awkward paragraph. Let's move on.
|
180
202
|
|
181
|
-
|
203
|
+
### Protected from SQL injections
|
182
204
|
|
183
|
-
|
184
|
-
|
185
|
-
account = Account.firstwp(params[:search])
|
205
|
+
search = Account.new_search(params[:search])
|
206
|
+
conditions = Account.new_conditions(params[:conditions])
|
186
207
|
|
187
|
-
|
208
|
+
### *NOT* protected from SQL injections
|
188
209
|
|
189
|
-
|
210
|
+
accounts = Account.find(params[:search])
|
190
211
|
accounts = Account.all(params[:search])
|
191
|
-
|
192
|
-
|
193
|
-
|
212
|
+
account = Account.first(params[:search])
|
213
|
+
search = Account.new_search!(params[:search])
|
214
|
+
conditions = Account.new_conditions!(params[:conditions])
|
215
|
+
|
216
|
+
Lesson learned: use new\_search and new\_conditions when passing in params as *ANY* of the options.
|
194
217
|
|
195
218
|
## Available Conditions
|
196
219
|
|
@@ -223,16 +246,6 @@ Some of these conditions come with aliases, so you have your choice how to call
|
|
223
246
|
|
224
247
|
You will notice above there is "contains" and "keywords". The difference is that "keywords" is an enhanced search. It acts like a real keyword search. It finds those keywords, in any order, and blacklists meaningless words such as "and", "the", etc. "contains" finds the EXACT string in the column you are searching, spaces and all.
|
225
248
|
|
226
|
-
## Creating your search form
|
227
|
-
|
228
|
-
After all of that, here's why I love this plugin:
|
229
|
-
|
230
|
-
<% form_for :search, User.new_conditions, :url => users_path do |f| %>
|
231
|
-
<%= f.text_field :first_name_contains %>
|
232
|
-
<%= f.calendar_date_select :created_after %>
|
233
|
-
<%= f.select :age_gt, (1..100) %>
|
234
|
-
<% end %>
|
235
|
-
|
236
249
|
## Credits
|
237
250
|
|
238
251
|
Author: [Ben Johnson](http://github.com/binarylogic) of [Binary Logic](http://www.binarylogic.com)
|
data/Rakefile
CHANGED
@@ -11,7 +11,7 @@ Echoe.new 'searchgasm' do |p|
|
|
11
11
|
p.summary = "Orgasmic ActiveRecord searching"
|
12
12
|
p.description = "Makes ActiveRecord searching easier, robust, and powerful. Automatic conditions, pagination support, object based searching, and more."
|
13
13
|
p.url = "http://github.com/binarylogic/searchgasm"
|
14
|
-
p.dependencies = %w(activerecord)
|
14
|
+
p.dependencies = %w(activerecord activesupport)
|
15
15
|
p.include_rakefile = true
|
16
16
|
end
|
17
17
|
|
@@ -3,12 +3,6 @@ module BinaryLogic
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Associations
|
5
5
|
module AssociationCollection
|
6
|
-
def self.included(klass)
|
7
|
-
klass.class_eval do
|
8
|
-
include Protection
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
6
|
def find_with_searchgasm(*args)
|
13
7
|
options = args.extract_options!
|
14
8
|
args << sanitize_options_with_searchgasm(options)
|
@@ -16,11 +10,27 @@ module BinaryLogic
|
|
16
10
|
end
|
17
11
|
|
18
12
|
def build_conditions(options = {}, &block)
|
19
|
-
@reflection.klass.build_conditions(options
|
13
|
+
conditions = @reflection.klass.build_conditions(options, &block)
|
14
|
+
conditions.scope = scope(:find)[:conditions]
|
15
|
+
conditions
|
16
|
+
end
|
17
|
+
|
18
|
+
def build_conditions!(options = {}, &block)
|
19
|
+
conditions = @reflection.klass.build_conditions!(options, &block)
|
20
|
+
conditions.scope = scope(:find)[:conditions]
|
21
|
+
conditions
|
20
22
|
end
|
21
23
|
|
22
24
|
def build_search(options = {}, &block)
|
23
|
-
@reflection.klass.build_search(options
|
25
|
+
conditions = @reflection.klass.build_search(options, &block)
|
26
|
+
conditions.scope = scope(:find)[:conditions]
|
27
|
+
conditions
|
28
|
+
end
|
29
|
+
|
30
|
+
def build_search!(options = {}, &block)
|
31
|
+
conditions = @reflection.klass.build_search!(options, &block)
|
32
|
+
conditions.scope = scope(:find)[:conditions]
|
33
|
+
conditions
|
24
34
|
end
|
25
35
|
end
|
26
36
|
|
@@ -2,49 +2,62 @@ module BinaryLogic
|
|
2
2
|
module Searchgasm
|
3
3
|
module ActiveRecord
|
4
4
|
module Base
|
5
|
-
def self.included(klass)
|
6
|
-
klass.class_eval do
|
7
|
-
alias_method :new_conditions, :build_conditions
|
8
|
-
alias_method :new_search, :build_search
|
9
|
-
include Protection
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
5
|
def calculate_with_searchgasm(*args)
|
14
6
|
options = args.extract_options!
|
15
7
|
options = sanitize_options_with_searchgasm(options)
|
16
8
|
args << options
|
17
9
|
calculate_without_searchgasm(*args)
|
18
10
|
end
|
19
|
-
|
11
|
+
|
20
12
|
def find_with_searchgasm(*args)
|
21
13
|
options = args.extract_options!
|
22
14
|
options = sanitize_options_with_searchgasm(options)
|
23
15
|
args << options
|
24
16
|
find_without_searchgasm(*args)
|
25
17
|
end
|
26
|
-
|
18
|
+
|
27
19
|
def scope_with_searchgasm(method, key = nil)
|
28
20
|
scope = scope_without_searchgasm(method, key)
|
29
|
-
return sanitize_options_with_searchgasm(scope) if key.nil? && method == :find
|
21
|
+
return sanitize_options_with_searchgasm(scope) if key.nil? && method == :find && !scope.blank?
|
30
22
|
scope
|
31
23
|
end
|
32
|
-
|
33
|
-
def build_conditions(
|
34
|
-
|
24
|
+
|
25
|
+
def build_conditions(values = {}, &block)
|
26
|
+
conditions = searchgasm_conditions({})
|
27
|
+
conditions.protect = true
|
28
|
+
conditions.value = values
|
29
|
+
yield conditions if block_given?
|
30
|
+
conditions
|
35
31
|
end
|
36
|
-
|
37
|
-
def
|
38
|
-
|
39
|
-
yield
|
40
|
-
|
32
|
+
|
33
|
+
def build_conditions!(values = {}, &block)
|
34
|
+
conditions = searchgasm_conditions(values)
|
35
|
+
yield conditions if block_given?
|
36
|
+
conditions
|
41
37
|
end
|
42
|
-
|
38
|
+
|
39
|
+
def build_search(options = {}, &block)
|
40
|
+
options[:protect] = true
|
41
|
+
search = searchgasm_searcher(options)
|
42
|
+
yield search if block_given?
|
43
|
+
search
|
44
|
+
end
|
45
|
+
|
46
|
+
def build_search!(options = {}, &block)
|
47
|
+
search = searchgasm_searcher(options)
|
48
|
+
yield search if block_given?
|
49
|
+
search
|
50
|
+
end
|
51
|
+
|
43
52
|
private
|
44
53
|
def sanitize_options_with_searchgasm(options)
|
45
54
|
searchgasm_searcher(options).sanitize
|
46
55
|
end
|
47
|
-
|
56
|
+
|
57
|
+
def searchgasm_conditions(options)
|
58
|
+
BinaryLogic::Searchgasm::Search::Conditions.new(self, options)
|
59
|
+
end
|
60
|
+
|
48
61
|
def searchgasm_searcher(options)
|
49
62
|
BinaryLogic::Searchgasm::Search::Base.new(self, options)
|
50
63
|
end
|
@@ -61,6 +74,10 @@ module ::ActiveRecord
|
|
61
74
|
alias_method_chain :calculate, :searchgasm
|
62
75
|
alias_method_chain :find, :searchgasm
|
63
76
|
alias_method_chain :scope, :searchgasm
|
77
|
+
alias_method :new_conditions, :build_conditions
|
78
|
+
alias_method :new_conditions!, :build_conditions!
|
79
|
+
alias_method :new_search, :build_search
|
80
|
+
alias_method :new_search!, :build_search!
|
64
81
|
|
65
82
|
def valid_find_options
|
66
83
|
VALID_FIND_OPTIONS
|
@@ -1,14 +1,16 @@
|
|
1
1
|
module BinaryLogic
|
2
2
|
module Searchgasm
|
3
|
-
module Search
|
3
|
+
module Search
|
4
4
|
class Base
|
5
5
|
include Utilities
|
6
6
|
|
7
7
|
SPECIAL_FIND_OPTIONS = [:order_by, :order_as, :page, :per_page]
|
8
8
|
VALID_FIND_OPTIONS = ::ActiveRecord::Base.valid_find_options + SPECIAL_FIND_OPTIONS
|
9
|
+
SAFE_OPTIONS = SPECIAL_FIND_OPTIONS + [:conditions, :limit, :offset]
|
10
|
+
VULNERABLE_OPTIONS = VALID_FIND_OPTIONS - SAFE_OPTIONS
|
9
11
|
|
10
12
|
attr_accessor :klass
|
11
|
-
attr_reader :conditions
|
13
|
+
attr_reader :conditions, :protect
|
12
14
|
attr_writer :options
|
13
15
|
|
14
16
|
def initialize(klass, options = {})
|
@@ -18,10 +20,12 @@ module BinaryLogic
|
|
18
20
|
end
|
19
21
|
|
20
22
|
(::ActiveRecord::Base.valid_find_options - [:conditions]).each do |option|
|
21
|
-
|
23
|
+
src = <<-SRC
|
22
24
|
def #{option}(sanitize = false); options[:#{option}]; end
|
23
25
|
def #{option}=(value); self.options[:#{option}] = value; end
|
24
26
|
SRC
|
27
|
+
|
28
|
+
class_eval src
|
25
29
|
end
|
26
30
|
|
27
31
|
alias_method :per_page, :limit
|
@@ -61,6 +65,11 @@ module BinaryLogic
|
|
61
65
|
includes.blank? ? nil : (includes.size == 1 ? includes.first : includes)
|
62
66
|
end
|
63
67
|
|
68
|
+
def inspect
|
69
|
+
options_as_nice_string = ::ActiveRecord::Base.valid_find_options.collect { |name| "#{name}: #{send(name)}" }.join(", ")
|
70
|
+
"#<#{klass} #{options_as_nice_string}>"
|
71
|
+
end
|
72
|
+
|
64
73
|
def limit=(value)
|
65
74
|
return options[:limit] = nil if value.nil? || value == 0
|
66
75
|
|
@@ -75,9 +84,12 @@ module BinaryLogic
|
|
75
84
|
@options ||= {}
|
76
85
|
end
|
77
86
|
|
78
|
-
def options=(
|
79
|
-
return unless
|
80
|
-
|
87
|
+
def options=(values)
|
88
|
+
return unless values.is_a?(Hash)
|
89
|
+
self.protect = values.delete(:protect) if values.has_key?(:protect) # make sure we do this first
|
90
|
+
values.symbolize_keys.assert_valid_keys(VALID_FIND_OPTIONS)
|
91
|
+
frisk!(values) if protect?
|
92
|
+
values.each { |option, value| send("#{option}=", value) }
|
81
93
|
end
|
82
94
|
|
83
95
|
def order_as
|
@@ -114,6 +126,15 @@ module BinaryLogic
|
|
114
126
|
value
|
115
127
|
end
|
116
128
|
|
129
|
+
def protect=(value)
|
130
|
+
conditions.protect = value
|
131
|
+
@protect = value
|
132
|
+
end
|
133
|
+
|
134
|
+
def protect?
|
135
|
+
protect == true
|
136
|
+
end
|
137
|
+
|
117
138
|
def reset!
|
118
139
|
conditions.reset!
|
119
140
|
self.options = {}
|
@@ -136,6 +157,31 @@ module BinaryLogic
|
|
136
157
|
def scope=(value)
|
137
158
|
conditions.scope = value
|
138
159
|
end
|
160
|
+
|
161
|
+
private
|
162
|
+
def order_by_safe?(order_by)
|
163
|
+
return true if order_by.blank?
|
164
|
+
|
165
|
+
column_names = klass.column_names
|
166
|
+
|
167
|
+
[order_by].flatten.each do |column|
|
168
|
+
case column
|
169
|
+
when Hash
|
170
|
+
return false unless order_by_safe?(column.to_a)
|
171
|
+
when Array
|
172
|
+
return false unless order_by_safe?(column)
|
173
|
+
else
|
174
|
+
return false unless column_names.include?(column)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
true
|
179
|
+
end
|
180
|
+
|
181
|
+
def frisk!(options)
|
182
|
+
options.symbolize_keys.assert_valid_keys(SAFE_OPTIONS)
|
183
|
+
raise(ArgumentError, ":order_by can only contain colum names in the string, hash, or array format") unless order_by_safe?(options[:order_by])
|
184
|
+
end
|
139
185
|
end
|
140
186
|
end
|
141
187
|
end
|
@@ -4,7 +4,7 @@ module BinaryLogic
|
|
4
4
|
class Conditions
|
5
5
|
include Utilities
|
6
6
|
|
7
|
-
attr_accessor :klass, :relationship_name, :scope
|
7
|
+
attr_accessor :klass, :protect, :relationship_name, :scope
|
8
8
|
|
9
9
|
class << self
|
10
10
|
def condition_types_for_column_type(type)
|
@@ -55,17 +55,27 @@ module BinaryLogic
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
def initialize(klass,
|
58
|
+
def initialize(klass, init_values = {})
|
59
59
|
self.klass = klass
|
60
60
|
klass.columns.each { |column| add_conditions_for_column!(column) }
|
61
61
|
klass.reflect_on_all_associations.each { |association| add_association!(association) }
|
62
|
-
self.value =
|
62
|
+
self.value = init_values
|
63
|
+
end
|
64
|
+
|
65
|
+
def assert_valid_values(values)
|
66
|
+
keys = condition_names.collect { |condition_name| condition_name.to_sym }
|
67
|
+
keys += klass.reflect_on_all_associations.collect { |association| association.name }
|
68
|
+
values.symbolize_keys.assert_valid_keys(keys)
|
63
69
|
end
|
64
70
|
|
65
71
|
def associations
|
66
72
|
objects.select { |object| object.is_a?(self.class) }
|
67
73
|
end
|
68
74
|
|
75
|
+
def condition_names
|
76
|
+
@condition_names ||= []
|
77
|
+
end
|
78
|
+
|
69
79
|
def includes
|
70
80
|
i = []
|
71
81
|
associations.each do |association|
|
@@ -79,6 +89,10 @@ module BinaryLogic
|
|
79
89
|
@objects ||= []
|
80
90
|
end
|
81
91
|
|
92
|
+
def protect?
|
93
|
+
protect == true
|
94
|
+
end
|
95
|
+
|
82
96
|
def reset!
|
83
97
|
dupped_objects = objects.dup
|
84
98
|
dupped_objects.each do |object|
|
@@ -97,15 +111,17 @@ module BinaryLogic
|
|
97
111
|
merge_conditions(sanitized_objects, scope)
|
98
112
|
end
|
99
113
|
|
100
|
-
def value=(
|
114
|
+
def value=(values)
|
101
115
|
reset!
|
102
116
|
self.scope = nil
|
103
117
|
|
104
|
-
case
|
118
|
+
case values
|
105
119
|
when Hash
|
106
|
-
|
120
|
+
assert_valid_values(values)
|
121
|
+
values.each { |condition, value| send("#{condition}=", value) }
|
107
122
|
else
|
108
|
-
|
123
|
+
raise(ArgumentError, "You can not set a scope or pass SQL while the search is being protected") if protect?
|
124
|
+
self.scope = values
|
109
125
|
end
|
110
126
|
end
|
111
127
|
|
@@ -143,7 +159,8 @@ module BinaryLogic
|
|
143
159
|
|
144
160
|
def add_condition!(condition_type, column)
|
145
161
|
name = Condition.generate_name(column, condition_type)
|
146
|
-
|
162
|
+
self.condition_names << name
|
163
|
+
|
147
164
|
# Define accessor methods
|
148
165
|
self.class.class_eval <<-SRC
|
149
166
|
def #{name}_object
|
@@ -161,6 +178,7 @@ module BinaryLogic
|
|
161
178
|
|
162
179
|
# Define aliases
|
163
180
|
self.class.aliases_for_condition(column, condition_type).each do |alias_name|
|
181
|
+
self.condition_names << alias_name
|
164
182
|
self.class.class_eval do
|
165
183
|
alias_method alias_name, name
|
166
184
|
alias_method "#{alias_name}=", "#{name}="
|
data/lib/searchgasm/version.rb
CHANGED
data/lib/searchgasm.rb
CHANGED
data/searchgasm.gemspec
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
|
2
|
-
# Gem::Specification for Searchgasm-0.9.
|
2
|
+
# Gem::Specification for Searchgasm-0.9.2
|
3
3
|
# Originally generated by Echoe
|
4
4
|
|
5
5
|
--- !ruby/object:Gem::Specification
|
6
6
|
name: searchgasm
|
7
7
|
version: !ruby/object:Gem::Version
|
8
|
-
version: 0.9.
|
8
|
+
version: 0.9.2
|
9
9
|
platform: ruby
|
10
10
|
authors:
|
11
11
|
- Ben Johnson of Binary Logic
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
|
15
|
-
date: 2008-09-
|
15
|
+
date: 2008-09-02 00:00:00 -04:00
|
16
16
|
default_executable:
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
@@ -25,6 +25,16 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: "0"
|
27
27
|
version:
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: activesupport
|
30
|
+
type: :runtime
|
31
|
+
version_requirement:
|
32
|
+
version_requirements: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: "0"
|
37
|
+
version:
|
28
38
|
- !ruby/object:Gem::Dependency
|
29
39
|
name: echoe
|
30
40
|
type: :development
|
@@ -45,7 +55,6 @@ extra_rdoc_files:
|
|
45
55
|
- CHANGELOG
|
46
56
|
- lib/searchgasm/active_record/associations.rb
|
47
57
|
- lib/searchgasm/active_record/base.rb
|
48
|
-
- lib/searchgasm/active_record/protection.rb
|
49
58
|
- lib/searchgasm/helpers.rb
|
50
59
|
- lib/searchgasm/search/base.rb
|
51
60
|
- lib/searchgasm/search/condition.rb
|
@@ -59,7 +68,6 @@ files:
|
|
59
68
|
- init.rb
|
60
69
|
- lib/searchgasm/active_record/associations.rb
|
61
70
|
- lib/searchgasm/active_record/base.rb
|
62
|
-
- lib/searchgasm/active_record/protection.rb
|
63
71
|
- lib/searchgasm/helpers.rb
|
64
72
|
- lib/searchgasm/search/base.rb
|
65
73
|
- lib/searchgasm/search/condition.rb
|
@@ -12,6 +12,19 @@ class TestActiveRecordBase < Test::Unit::TestCase
|
|
12
12
|
teardown_db
|
13
13
|
end
|
14
14
|
|
15
|
+
def test_standard_searches
|
16
|
+
assert_nothing_raised { Account.all }
|
17
|
+
assert_nothing_raised { Account.first }
|
18
|
+
assert_nothing_raised { Account.find(:all) }
|
19
|
+
assert_nothing_raised { Account.find(:first) }
|
20
|
+
assert_nothing_raised { Account.find(:all, nil) }
|
21
|
+
assert_nothing_raised { Account.find(:all, {}) }
|
22
|
+
assert_nothing_raised { Account.count({}) }
|
23
|
+
assert_nothing_raised { Account.count(nil) }
|
24
|
+
assert_nothing_raised { Account.sum("id") }
|
25
|
+
assert_nothing_raised { Account.sum("id", {}) }
|
26
|
+
end
|
27
|
+
|
15
28
|
def test_valid_find_options
|
16
29
|
assert_equal [ :conditions, :include, :joins, :limit, :offset, :order, :select, :readonly, :group, :from, :lock ], ActiveRecord::Base.valid_find_options
|
17
30
|
end
|
@@ -23,6 +36,9 @@ class TestActiveRecordBase < Test::Unit::TestCase
|
|
23
36
|
assert_equal "awesome", search.conditions.name_keywords
|
24
37
|
assert_equal 2, search.page
|
25
38
|
assert_equal 15, search.per_page
|
39
|
+
|
40
|
+
search = Account.new_search(:conditions => {:name_keywords => "awesome"}, :page => 2, :per_page => 15)
|
41
|
+
assert_equal Account, search.klass
|
26
42
|
end
|
27
43
|
|
28
44
|
def test_searching
|
@@ -43,6 +59,6 @@ class TestActiveRecordBase < Test::Unit::TestCase
|
|
43
59
|
end
|
44
60
|
|
45
61
|
def test_scoping
|
46
|
-
assert_equal
|
62
|
+
assert_equal nil, Account.send(:scope, :find)
|
47
63
|
end
|
48
64
|
end
|
@@ -113,4 +113,20 @@ class TestSearchgasmBase < Test::Unit::TestCase
|
|
113
113
|
assert_equal search.first, Account.find(1)
|
114
114
|
assert_equal search.find(:first), Account.find(1)
|
115
115
|
end
|
116
|
+
|
117
|
+
def test_protection
|
118
|
+
assert_raise(ArgumentError) { Account.build_search(:conditions => "(DELETE FROM users)", :page => 2, :per_page => 15) }
|
119
|
+
BinaryLogic::Searchgasm::Search::Base::VULNERABLE_OPTIONS.each { |option| assert_raise(ArgumentError) { Account.build_search(option => "(DELETE FROM users)") } }
|
120
|
+
|
121
|
+
assert_nothing_raised { Account.build_search!(:conditions => "(DELETE FROM users)", :page => 2, :per_page => 15) }
|
122
|
+
BinaryLogic::Searchgasm::Search::Base::VULNERABLE_OPTIONS.each { |option| assert_nothing_raised { Account.build_search!(option => "(DELETE FROM users)") } }
|
123
|
+
|
124
|
+
account = Account.first
|
125
|
+
|
126
|
+
assert_raise(ArgumentError) { account.users.build_search(:conditions => "(DELETE FROM users)", :page => 2, :per_page => 15) }
|
127
|
+
BinaryLogic::Searchgasm::Search::Base::VULNERABLE_OPTIONS.each { |option| assert_raise(ArgumentError) { account.users.build_search(option => "(DELETE FROM users)") } }
|
128
|
+
|
129
|
+
assert_nothing_raised { account.users.build_search!(:conditions => "(DELETE FROM users)", :page => 2, :per_page => 15) }
|
130
|
+
BinaryLogic::Searchgasm::Search::Base::VULNERABLE_OPTIONS.each { |option| assert_nothing_raised { account.users.build_search!(option => "(DELETE FROM users)") } }
|
131
|
+
end
|
116
132
|
end
|
@@ -118,6 +118,16 @@ class TestSearchgasmConditions < Test::Unit::TestCase
|
|
118
118
|
assert_equal conditions.scope, "id in (1,2,3,4)"
|
119
119
|
end
|
120
120
|
|
121
|
+
def test_protection
|
122
|
+
assert_raise(ArgumentError) { Account.new_conditions("(DELETE FROM users)") }
|
123
|
+
assert_nothing_raised { Account.build_conditions!("(DELETE FROM users)") }
|
124
|
+
|
125
|
+
account = Account.first
|
126
|
+
|
127
|
+
assert_raise(ArgumentError) { account.users.build_conditions("(DELETE FROM users)") }
|
128
|
+
assert_nothing_raised { account.users.build_conditions!("(DELETE FROM users)") }
|
129
|
+
end
|
130
|
+
|
121
131
|
private
|
122
132
|
def column_value(column)
|
123
133
|
case column.type
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: searchgasm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Johnson of Binary Logic
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-09-
|
12
|
+
date: 2008-09-02 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -22,6 +22,16 @@ dependencies:
|
|
22
22
|
- !ruby/object:Gem::Version
|
23
23
|
version: "0"
|
24
24
|
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: activesupport
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
25
35
|
- !ruby/object:Gem::Dependency
|
26
36
|
name: echoe
|
27
37
|
type: :development
|
@@ -42,7 +52,6 @@ extra_rdoc_files:
|
|
42
52
|
- CHANGELOG
|
43
53
|
- lib/searchgasm/active_record/associations.rb
|
44
54
|
- lib/searchgasm/active_record/base.rb
|
45
|
-
- lib/searchgasm/active_record/protection.rb
|
46
55
|
- lib/searchgasm/helpers.rb
|
47
56
|
- lib/searchgasm/search/base.rb
|
48
57
|
- lib/searchgasm/search/condition.rb
|
@@ -56,7 +65,6 @@ files:
|
|
56
65
|
- init.rb
|
57
66
|
- lib/searchgasm/active_record/associations.rb
|
58
67
|
- lib/searchgasm/active_record/base.rb
|
59
|
-
- lib/searchgasm/active_record/protection.rb
|
60
68
|
- lib/searchgasm/helpers.rb
|
61
69
|
- lib/searchgasm/search/base.rb
|
62
70
|
- lib/searchgasm/search/condition.rb
|
@@ -1,37 +0,0 @@
|
|
1
|
-
module BinaryLogic
|
2
|
-
module Searchgasm
|
3
|
-
module ActiveRecord
|
4
|
-
module Protection
|
5
|
-
def self.included(klass)
|
6
|
-
klass.class_eval do
|
7
|
-
alias_method :new_search, :build_search
|
8
|
-
alias_method :findwp, :find_with_protection
|
9
|
-
alias_method :allwp, :all_with_protection
|
10
|
-
alias_method :firstwp, :first_with_protection
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def find_with_protection(*args)
|
15
|
-
options = args.extract_options!
|
16
|
-
options.merge!(:protect => true)
|
17
|
-
args << options
|
18
|
-
find(*args)
|
19
|
-
end
|
20
|
-
|
21
|
-
def all_with_protection(*args)
|
22
|
-
options = args.extract_options!
|
23
|
-
options.merge!(:protect => true)
|
24
|
-
args << options
|
25
|
-
all(*args)
|
26
|
-
end
|
27
|
-
|
28
|
-
def first_with_protection(*args)
|
29
|
-
options = args.extract_options!
|
30
|
-
options.merge!(:protect => true)
|
31
|
-
args << options
|
32
|
-
first(*args)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|