order_as_specified 1.1 → 1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c3348c26ab12edd7c9a4066c710b0cc914974946
4
- data.tar.gz: 10b274073cfa67a6237afe207f3df7ae7141fddf
3
+ metadata.gz: 69bf1783284bb4738f7c5a3d189e122d3f5f00a2
4
+ data.tar.gz: 6c9f53fe8658d25a1d44dc9cd120d8eda8d62537
5
5
  SHA512:
6
- metadata.gz: 4a8cb8f25341c51ede2a157a997c41bafe0f4c1ba2174e727f7c9f4b35ffb39f86c5d229cd614a25967eda34e731ae9704c2f372f12d77a9bda5a2261b67ea45
7
- data.tar.gz: 92fce24b220e4a658da7eb7c6be5520f532935806793f2d145b190e4db7b85190d7b39bc14f4c6782e79b708ff1433c616623d760a56cd7d46f78438dd8c7a1e
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 changes. The changes:
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
@@ -1,3 +1,3 @@
1
1
  module OrderAsSpecified
2
- VERSION = "1.1"
2
+ VERSION = "1.2"
3
3
  end
@@ -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
- "#{table}.#{attribute}='#{value}'"
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(", "))
@@ -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"
@@ -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.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-07-13 00:00:00.000000000 Z
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: '4.0'
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: '4.0'
26
+ version: 4.0.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement