coercive 1.0.0 → 1.0.1
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/README.md +16 -2
- data/coercive.gemspec +12 -0
- data/lib/coercive/uri.rb +16 -19
- data/test/coercive.rb +1 -1
- data/test/uri.rb +29 -17
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 867918b9a3ee09bdfc89a10e802032e229fdf4ab250f80727a68197f8144d615
|
4
|
+
data.tar.gz: 9d1dcfcc05db65d1669049c87f36bf9a252fe91ce68b9298a7578ce630aeae50
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2dc34142c2553b747290451db7ca631dd12b31e9e7488502e4a54b3fc1cf372a7dd498c951498f8c3b86d519172bb879fb7f650e97448260dd8fad2c5c5c8662
|
7
|
+
data.tar.gz: 513cb240285c58fbf784d3a61fae066bf7c768836cdc06281357b92f8c7a62ad72ea2c0114f3ba6645da29b4ed01fbabff616f10db91f93b8194c587d2f9c623
|
data/README.md
CHANGED
@@ -1,10 +1,20 @@
|
|
1
|
-
#
|
1
|
+
# Coercive
|
2
|
+
|
3
|
+
# Install
|
4
|
+
|
5
|
+
```
|
6
|
+
$ gem install coercive
|
7
|
+
```
|
8
|
+
|
9
|
+
# Usage
|
2
10
|
|
3
11
|
`Coercive` is a Ruby library to validate and coerce user input.
|
4
12
|
|
5
13
|
Define your coercion modules like this:
|
6
14
|
|
7
15
|
```ruby
|
16
|
+
require "coercive"
|
17
|
+
|
8
18
|
module CoerceFoo
|
9
19
|
extend Coercive
|
10
20
|
|
@@ -184,7 +194,7 @@ CoerceFoo.call("foo" => [BasicObject.new])
|
|
184
194
|
|
185
195
|
### `hash`
|
186
196
|
|
187
|
-
`hash` coercion let's you manipulate the key and values, similarly to how `array` does
|
197
|
+
`hash` coercion let's you manipulate the key and values, similarly to how `array` does.
|
188
198
|
|
189
199
|
```ruby
|
190
200
|
module CoerceFoo
|
@@ -199,3 +209,7 @@ CoerceFoo.call("foo" => {"bar" => "0.1"})
|
|
199
209
|
CoerceFoo.call("foo" => {"barrrr" => "0.1"})
|
200
210
|
# => Coercive::Error: {"foo"=>{"barrrr"=>"too_long"}}
|
201
211
|
```
|
212
|
+
|
213
|
+
### `uri`
|
214
|
+
|
215
|
+
The `uri` coercion validates
|
data/coercive.gemspec
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "coercive"
|
3
|
+
s.version = "1.0.1"
|
4
|
+
s.summary = "Coercive is a library to validate and coerce user input"
|
5
|
+
s.description = s.summary
|
6
|
+
s.authors = ["Joe McIlvain", "Lucas Tolchinsky"]
|
7
|
+
s.email = ["joe.eli.mac@gmail.com", "tonchis@protonmail.com"]
|
8
|
+
s.homepage = "https://github.com/Theorem/coercive"
|
9
|
+
s.license = "MIT"
|
10
|
+
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
end
|
data/lib/coercive/uri.rb
CHANGED
@@ -1,16 +1,12 @@
|
|
1
1
|
require "ipaddr"
|
2
2
|
require "uri"
|
3
3
|
|
4
|
-
module
|
4
|
+
module Coercive
|
5
5
|
module URI
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# This SHOULD NOT be set in PRODUCTION.
|
11
|
-
ALLOW_PRIVATE_IP_CONNECTIONS =
|
12
|
-
ENV.fetch("ALLOW_PRIVATE_IP_CONNECTIONS", "").downcase == "true"
|
13
|
-
|
6
|
+
# The IP ranges below are considered private and by default not permitted by the `uri`
|
7
|
+
# coercion function. To allow connecting to local services (in development, for example)
|
8
|
+
# users can set the `allow_private_ip` option, which ignores if the URI resolves to a public
|
9
|
+
# address or not.
|
14
10
|
PRIVATE_IP_RANGES = [
|
15
11
|
IPAddr.new("0.0.0.0/8"), # Broadcasting to the current network. RFC 1700.
|
16
12
|
IPAddr.new("10.0.0.0/8"), # Local private network. RFC 1918.
|
@@ -38,26 +34,27 @@ module Coercion
|
|
38
34
|
# require_user - set true to make the URI user a required element
|
39
35
|
# require_password - set true to make the URI password a required element
|
40
36
|
def self.coerce_fn(string_coerce_fn, schema_fn: nil, require_path: false,
|
41
|
-
require_port: false, require_user: false, require_password: false
|
37
|
+
require_port: false, require_user: false, require_password: false,
|
38
|
+
allow_private_ip: false)
|
42
39
|
->(input) do
|
43
40
|
uri = begin
|
44
41
|
::URI.parse(string_coerce_fn.call(input))
|
45
42
|
rescue ::URI::InvalidURIError
|
46
|
-
fail
|
43
|
+
fail Coercive::Error.new("not_valid")
|
47
44
|
end
|
48
45
|
|
49
|
-
fail
|
50
|
-
fail
|
51
|
-
fail
|
52
|
-
fail
|
53
|
-
fail
|
54
|
-
fail
|
46
|
+
fail Coercive::Error.new("no_host") unless uri.host
|
47
|
+
fail Coercive::Error.new("not_resolvable") unless allow_private_ip || resolvable_public_ip?(uri)
|
48
|
+
fail Coercive::Error.new("no_path") if require_path && uri.path.empty?
|
49
|
+
fail Coercive::Error.new("no_port") if require_port && !uri.port
|
50
|
+
fail Coercive::Error.new("no_user") if require_user && !uri.user
|
51
|
+
fail Coercive::Error.new("no_password") if require_password && !uri.password
|
55
52
|
|
56
53
|
if schema_fn
|
57
54
|
begin
|
58
55
|
schema_fn.call(uri.scheme)
|
59
|
-
rescue
|
60
|
-
fail
|
56
|
+
rescue Coercive::Error
|
57
|
+
fail Coercive::Error.new("unsupported_schema")
|
61
58
|
end
|
62
59
|
end
|
63
60
|
|
data/test/coercive.rb
CHANGED
@@ -113,7 +113,7 @@ describe "Coercive" do
|
|
113
113
|
fixnum = 2
|
114
114
|
rational = 2 ** -2
|
115
115
|
bignum = 2 ** 64
|
116
|
-
bigdecimal = BigDecimal
|
116
|
+
bigdecimal = BigDecimal("0.1")
|
117
117
|
|
118
118
|
[fixnum, rational, bignum, bigdecimal].each do |value|
|
119
119
|
attributes = { "foo" => value }
|
data/test/uri.rb
CHANGED
@@ -11,7 +11,7 @@ describe "Coercive::URI" do
|
|
11
11
|
assert_equal errors, e.errors
|
12
12
|
end
|
13
13
|
|
14
|
-
|
14
|
+
before do
|
15
15
|
@coercion = Module.new do
|
16
16
|
extend Coercive
|
17
17
|
|
@@ -39,10 +39,14 @@ describe "Coercive::URI" do
|
|
39
39
|
attribute :require_password,
|
40
40
|
uri(string(min: 1, max: 255), require_password: true),
|
41
41
|
optional
|
42
|
+
|
43
|
+
attribute :allow_private_ip,
|
44
|
+
uri(string(min: 1, max: 255), allow_private_ip: true),
|
45
|
+
optional
|
42
46
|
end
|
43
47
|
end
|
44
48
|
|
45
|
-
|
49
|
+
it "coerces a valid string to a URI" do
|
46
50
|
attributes = {
|
47
51
|
"any" => "http://user:pass@www.example.com:1234/path"
|
48
52
|
}
|
@@ -50,7 +54,7 @@ describe "Coercive::URI" do
|
|
50
54
|
assert_equal attributes, @coercion.call(attributes)
|
51
55
|
end
|
52
56
|
|
53
|
-
|
57
|
+
it "errors if input is an invalid URI" do
|
54
58
|
attributes = { "any" => "%" }
|
55
59
|
|
56
60
|
expected_errors = { "any" => "not_valid" }
|
@@ -58,7 +62,7 @@ describe "Coercive::URI" do
|
|
58
62
|
assert_coercion_error(expected_errors) { @coercion.call(attributes) }
|
59
63
|
end
|
60
64
|
|
61
|
-
|
65
|
+
it "errors if the input is longer than the declared maximum size" do
|
62
66
|
attributes = {
|
63
67
|
"min" => "http://foo.com",
|
64
68
|
"max" => "http://long.url.com",
|
@@ -70,7 +74,7 @@ describe "Coercive::URI" do
|
|
70
74
|
assert_coercion_error(expected_errors) { @coercion.call(attributes) }
|
71
75
|
end
|
72
76
|
|
73
|
-
|
77
|
+
it "errors if the input is shorter than the declared minimum size" do
|
74
78
|
attributes = {
|
75
79
|
"min" => "http://a.com",
|
76
80
|
"max" => "http://bar.com",
|
@@ -82,14 +86,14 @@ describe "Coercive::URI" do
|
|
82
86
|
assert_coercion_error(expected_errors) { @coercion.call(attributes) }
|
83
87
|
end
|
84
88
|
|
85
|
-
|
89
|
+
it "errors if the URI is an empty string" do
|
86
90
|
attributes = { "schema" => "" }
|
87
91
|
expected_errors = { "schema" => "is_empty" }
|
88
92
|
|
89
93
|
assert_coercion_error(expected_errors) { @coercion.call(attributes) }
|
90
94
|
end
|
91
95
|
|
92
|
-
|
96
|
+
it "errors if no host" do
|
93
97
|
attributes = { "any" => "http://" }
|
94
98
|
|
95
99
|
expected_errors = { "any" => "no_host" }
|
@@ -97,7 +101,7 @@ describe "Coercive::URI" do
|
|
97
101
|
assert_coercion_error(expected_errors) { @coercion.call(attributes) }
|
98
102
|
end
|
99
103
|
|
100
|
-
|
104
|
+
it "errors if schema is not supported" do
|
101
105
|
attributes = { "schema" => "foo://example.com" }
|
102
106
|
|
103
107
|
expected_errors = { "schema" => "unsupported_schema" }
|
@@ -105,7 +109,7 @@ describe "Coercive::URI" do
|
|
105
109
|
assert_coercion_error(expected_errors) { @coercion.call(attributes) }
|
106
110
|
end
|
107
111
|
|
108
|
-
|
112
|
+
it "errors if required elements are not provided" do
|
109
113
|
attributes = {
|
110
114
|
"require_path" => "foo://example.com",
|
111
115
|
"require_port" => "foo://example.com",
|
@@ -130,7 +134,7 @@ describe "Coercive::URI" do
|
|
130
134
|
first = first.ipv6? ? "[#{first}]" : first.to_s
|
131
135
|
last = last.ipv6? ? "[#{last}]" : last.to_s
|
132
136
|
|
133
|
-
|
137
|
+
it "errors when the URI host is an IP in the range #{first}..#{last}" do
|
134
138
|
attributes_first = { "schema" => "http://#{first}/path" }
|
135
139
|
attributes_last = { "schema" => "http://#{last}/path" }
|
136
140
|
expected_errors = { "schema" => "not_resolvable" }
|
@@ -138,9 +142,17 @@ describe "Coercive::URI" do
|
|
138
142
|
assert_coercion_error(expected_errors) { @coercion.call(attributes_first) }
|
139
143
|
assert_coercion_error(expected_errors) { @coercion.call(attributes_last) }
|
140
144
|
end
|
145
|
+
|
146
|
+
it "allows overriding private IP address checks" do
|
147
|
+
attributes_first = { "allow_private_ip" => "http://#{first}/path" }
|
148
|
+
attributes_last = { "allow_private_ip" => "http://#{last}/path" }
|
149
|
+
|
150
|
+
assert_equal attributes_first, @coercion.call(attributes_first)
|
151
|
+
assert_equal attributes_last, @coercion.call(attributes_last)
|
152
|
+
end
|
141
153
|
end
|
142
154
|
|
143
|
-
|
155
|
+
it "errors when the URI host is not resolvable" do
|
144
156
|
attributes = {
|
145
157
|
"schema" => "http://bogus-host-that-cant-possibly-exist-here/path"
|
146
158
|
}
|
@@ -150,7 +162,7 @@ describe "Coercive::URI" do
|
|
150
162
|
assert_coercion_error(expected_errors) { @coercion.call(attributes) }
|
151
163
|
end
|
152
164
|
|
153
|
-
|
165
|
+
it "errors when the URI host resolves to an IP in a private range" do
|
154
166
|
attributes = { "schema" => "http://localhost/path" }
|
155
167
|
|
156
168
|
expected_errors = { "schema" => "not_resolvable" }
|
@@ -158,32 +170,32 @@ describe "Coercive::URI" do
|
|
158
170
|
assert_coercion_error(expected_errors) { @coercion.call(attributes) }
|
159
171
|
end
|
160
172
|
|
161
|
-
|
173
|
+
it "allows a URI host to be IP that isn't in a private range" do
|
162
174
|
attributes = { "schema" => "http://8.8.8.8/path" }
|
163
175
|
|
164
176
|
assert_equal attributes, @coercion.call(attributes)
|
165
177
|
end
|
166
178
|
|
167
|
-
|
179
|
+
it "allows a URI host that resolves to an IP not in a private range" do
|
168
180
|
attributes = { "schema" => "http://www.example.com/path" }
|
169
181
|
|
170
182
|
assert_equal attributes, @coercion.call(attributes)
|
171
183
|
end
|
172
184
|
|
173
|
-
|
185
|
+
it "allows a URI with no explicit path component" do
|
174
186
|
attributes = { "schema" => "http://www.example.com" }
|
175
187
|
|
176
188
|
assert_equal attributes, @coercion.call(attributes)
|
177
189
|
end
|
178
190
|
|
179
|
-
|
191
|
+
it "errors for a string that does not pass URI.parse" do
|
180
192
|
attributes = { "schema" => "\\" }
|
181
193
|
expected_errors = { "schema" => "not_valid" }
|
182
194
|
|
183
195
|
assert_coercion_error(expected_errors) { @coercion.call(attributes) }
|
184
196
|
end
|
185
197
|
|
186
|
-
|
198
|
+
it "errors for a URL that passes URI.parse, but is ill-formed" do
|
187
199
|
attributes = { "schema" => "http:example.com/path" }
|
188
200
|
|
189
201
|
begin
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: coercive
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joe McIlvain
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2020-08-
|
12
|
+
date: 2020-08-05 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Coercive is a library to validate and coerce user input
|
15
15
|
email:
|
@@ -21,6 +21,7 @@ extra_rdoc_files: []
|
|
21
21
|
files:
|
22
22
|
- LICENSE
|
23
23
|
- README.md
|
24
|
+
- coercive.gemspec
|
24
25
|
- lib/coercive.rb
|
25
26
|
- lib/coercive/uri.rb
|
26
27
|
- test/coercive.rb
|