order_as_specified 1.1 → 1.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 +4 -4
- data/CHANGELOG.md +19 -1
- data/README.md +36 -0
- data/lib/order_as_specified/version.rb +1 -1
- data/lib/order_as_specified.rb +4 -3
- data/order_as_specified.gemspec +1 -1
- data/spec/postgresql_spec.rb +26 -0
- data/spec/shared/order_as_specified_examples.rb +50 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69bf1783284bb4738f7c5a3d189e122d3f5f00a2
|
4
|
+
data.tar.gz: 6c9f53fe8658d25a1d44dc9cd120d8eda8d62537
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9f971220e0601e197de6ca6d16e5275db283f9d4c895953801220c527725242dd6a6401e94d73346555b4e0c7548893403dfc6538a91d8db9baf796db6aa13fa
|
7
|
+
data.tar.gz: 8f9dba4933b6c521c4a7dbfdbac6c7a23e2bd374151862ee1166ef2c916ebfa2dcf1a02975f3d51ec0abff5ca27002ec7ea5eb143546480cc3c21bdf50166777
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,24 @@
|
|
1
|
+
# 1.2
|
2
|
+
|
3
|
+
This release contains an important change: `order_as_specified` is no longer
|
4
|
+
vulnerable to SQL injection attacks. Big thanks to
|
5
|
+
[Anthony Mangano](https://github.com/wangteji) for finding and fixing this
|
6
|
+
issue.
|
7
|
+
|
8
|
+
In addition, this release corrects an error in the `.gemspec`—this gem relies on
|
9
|
+
ActiveRecord >= 4.0.1, not 4.0.0.
|
10
|
+
|
11
|
+
# 1.1
|
12
|
+
|
13
|
+
This release contains several minor changes:
|
14
|
+
|
15
|
+
- Tests now run against Ruby 2.2.5 and 2.3.1 and Rails 4.2 and 5.0.
|
16
|
+
- A code change was made to fix a Rails 5 deprecation warning.
|
17
|
+
|
1
18
|
# 1.0
|
2
19
|
|
3
|
-
We've hit our first major release! This release contains **no** breaking
|
20
|
+
We've hit our first major release! This release contains **no** breaking
|
21
|
+
changes. The changes:
|
4
22
|
|
5
23
|
- Added `:distinct_on` option. [[#3](https://github.com/panorama-ed/order_as_specified/issues/3)]
|
6
24
|
- Improved error message for `nil` arguments. [[#8](https://github.com/panorama-ed/order_as_specified/pull/8)]
|
data/README.md
CHANGED
@@ -80,6 +80,42 @@ TestObject.
|
|
80
80
|
]>
|
81
81
|
```
|
82
82
|
|
83
|
+
We can use chaining in this way to order by multiple attributes as well:
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
TestObject.
|
87
|
+
order_as_specified(language: ["es", "en"]).
|
88
|
+
order_as_specified(id: [4, 3, 5]).
|
89
|
+
order(:updated_at)
|
90
|
+
=> #<ActiveRecord::Relation [
|
91
|
+
|
92
|
+
# First is language "es"...
|
93
|
+
#<TestObject id: 1, language: "es", updated_at: "2016-08-01 02:22:00">,
|
94
|
+
|
95
|
+
# Within the language, we order by :updated_at...
|
96
|
+
#<TestObject id: 2, language: "es", updated_at: "2016-08-01 07:29:07">,
|
97
|
+
|
98
|
+
# Then language "en"...
|
99
|
+
#<TestObject id: 9, language: "en", updated_at: "2016-08-03 04:11:26">,
|
100
|
+
|
101
|
+
# Within the language, we order by :updated_at...
|
102
|
+
#<TestObject id: 8, language: "en", updated_at: "2016-08-04 18:52:14">,
|
103
|
+
|
104
|
+
# Then id 4...
|
105
|
+
#<TestObject id: 4, language: "fr", updated_at: "2016-08-01 12:59:33">,
|
106
|
+
|
107
|
+
# Then id 3...
|
108
|
+
#<TestObject id: 3, language: "ar", updated_at: "2016-08-02 19:41:44">,
|
109
|
+
|
110
|
+
# Then id 5...
|
111
|
+
#<TestObject id: 5, language: "ar", updated_at: "2016-08-02 22:12:52">,
|
112
|
+
|
113
|
+
# Then we order by :updated_at...
|
114
|
+
#<TestObject id: 7, language: "fr", updated_at: "2016-08-02 14:27:16">,
|
115
|
+
#<TestObject id: 6, language: "ar", updated_at: "2016-08-03 14:26:06">,
|
116
|
+
]>
|
117
|
+
```
|
118
|
+
|
83
119
|
We can also use this when we want to sort by an attribute in another model:
|
84
120
|
|
85
121
|
```ruby
|
data/lib/order_as_specified.rb
CHANGED
@@ -13,8 +13,8 @@ module OrderAsSpecified
|
|
13
13
|
distinct_on = hash.delete(:distinct_on)
|
14
14
|
params = extract_params(hash)
|
15
15
|
|
16
|
-
table = params[:table]
|
17
|
-
attribute = params[:attribute]
|
16
|
+
table = connection.quote_table_name(params[:table])
|
17
|
+
attribute = connection.quote_column_name(params[:attribute])
|
18
18
|
|
19
19
|
# We have to explicitly quote for now because SQL sanitization for ORDER BY
|
20
20
|
# queries isn't in less current versions of Rails.
|
@@ -22,7 +22,8 @@ module OrderAsSpecified
|
|
22
22
|
conditions = params[:values].map do |value|
|
23
23
|
raise OrderAsSpecified::Error, "Cannot order by `nil`" if value.nil?
|
24
24
|
|
25
|
-
|
25
|
+
# Sanitize each value to reduce the risk of SQL injection.
|
26
|
+
"#{table}.#{attribute}=#{sanitize(value)}"
|
26
27
|
end
|
27
28
|
|
28
29
|
scope = order(conditions.map { |cond| "#{cond} DESC" }.join(", "))
|
data/order_as_specified.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
-
spec.add_dependency "activerecord", ">= 4.0"
|
22
|
+
spec.add_dependency "activerecord", ">= 4.0.1"
|
23
23
|
|
24
24
|
spec.add_development_dependency "bundler", "~> 1.7"
|
25
25
|
spec.add_development_dependency "codeclimate-test-reporter", "~> 0.4"
|
data/spec/postgresql_spec.rb
CHANGED
@@ -28,5 +28,31 @@ RSpec.describe "PostgreSQL" do
|
|
28
28
|
it "returns distinct objects" do
|
29
29
|
expect(subject.length).to eq 3
|
30
30
|
end
|
31
|
+
|
32
|
+
context "input safety" do
|
33
|
+
before(:each) { TestClass.create(field: "foo") }
|
34
|
+
|
35
|
+
it "sanitizes column values" do
|
36
|
+
# Attempt to inject code to add a 'hi' field to each record. If the SQL inputs
|
37
|
+
# are properly sanitized, the code will be ignored and the returned model
|
38
|
+
# instances will not respond to #hi. If not, the code will execute and
|
39
|
+
# each of the returned model instances will have a #hi method to access the
|
40
|
+
# new field.
|
41
|
+
bad_value = "foo') field, 'hi'::varchar AS hi FROM test_classes --"
|
42
|
+
record = TestClass.order_as_specified(distinct_on: true, field: [bad_value]).to_a.first
|
43
|
+
expect(record).to_not respond_to(:hi)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "quotes column and table names" do
|
47
|
+
table = "association_test_classes"
|
48
|
+
quoted_table = AssociationTestClass.connection.quote_table_name(table)
|
49
|
+
|
50
|
+
column = "id"
|
51
|
+
quoted_column = AssociationTestClass.connection.quote_column_name(column)
|
52
|
+
|
53
|
+
sql = TestClass.order_as_specified(distinct_on: true, table => { column => ["foo"] }).to_sql
|
54
|
+
expect(sql).to include("DISTINCT ON (#{quoted_table}.#{quoted_column}")
|
55
|
+
end
|
56
|
+
end
|
31
57
|
end
|
32
58
|
end
|
@@ -30,6 +30,24 @@ RSpec.shared_examples ".order_as_specified" do
|
|
30
30
|
to eq [*shuffled_object_ids, omitted_object.id]
|
31
31
|
end
|
32
32
|
|
33
|
+
context "when the order is chained with other orderings" do
|
34
|
+
subject do
|
35
|
+
TestClass.
|
36
|
+
order_as_specified(field: shuffled_object_fields.take(3)).
|
37
|
+
order(:id)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "returns results in the given order by multiple fields" do
|
41
|
+
shuffled_objects # Build these objects first.
|
42
|
+
omitted_object # Build an object that isn't sorted in this list.
|
43
|
+
expect(subject.map(&:id)).to eq [
|
44
|
+
*shuffled_object_ids.take(3),
|
45
|
+
*shuffled_object_ids.drop(3).sort,
|
46
|
+
omitted_object.id
|
47
|
+
]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
33
51
|
context "when the order includes nil" do
|
34
52
|
let(:shuffled_objects) do
|
35
53
|
5.times.map do |i|
|
@@ -77,4 +95,36 @@ RSpec.shared_examples ".order_as_specified" do
|
|
77
95
|
to eq [*shuffled_object_ids, omitted_object.id]
|
78
96
|
end
|
79
97
|
end
|
98
|
+
|
99
|
+
context "input safety" do
|
100
|
+
before(:each) do
|
101
|
+
2.times { |i| TestClass.create(field: "foo#{i}") }
|
102
|
+
end
|
103
|
+
|
104
|
+
it "sanitizes column values" do
|
105
|
+
# Verify that the result set includes two records when using good column value.
|
106
|
+
good_value = "foo"
|
107
|
+
records = TestClass.order_as_specified(field: [good_value]).to_a
|
108
|
+
expect(records.count).to eq(2)
|
109
|
+
|
110
|
+
# Attempt to inject a LIMIT clause into the query. If the SQL inputs are
|
111
|
+
# properly sanitized, it will be ignored and the returned result set will
|
112
|
+
# include two records. If not, the LIMIT clause will execute and the result
|
113
|
+
# set will include just one record.
|
114
|
+
bad_value = "' LIMIT 1 --"
|
115
|
+
records = TestClass.order_as_specified(field: [bad_value]).to_a
|
116
|
+
expect(records.count).to eq(2)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "quotes column and table names" do
|
120
|
+
table = "association_test_classes"
|
121
|
+
quoted_table = AssociationTestClass.connection.quote_table_name(table)
|
122
|
+
|
123
|
+
column = "id"
|
124
|
+
quoted_column = AssociationTestClass.connection.quote_column_name(column)
|
125
|
+
|
126
|
+
sql = TestClass.order_as_specified(table => { column => ["foo"] }).to_sql
|
127
|
+
expect(sql).to include("ORDER BY #{quoted_table}.#{quoted_column}")
|
128
|
+
end
|
129
|
+
end
|
80
130
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: order_as_specified
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '1.
|
4
|
+
version: '1.2'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jacob Evelyn
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 4.0.1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 4.0.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|