coercive 1.0.0 → 1.0.1

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
  SHA256:
3
- metadata.gz: d1b6a873b4a86ca962a22d60630a4de5355b278b529fccbd2615a794a7a94448
4
- data.tar.gz: 62f47d1f35c9d931935138872dace70a7c39d3eb090529f1f2886448dca2e4ad
3
+ metadata.gz: 867918b9a3ee09bdfc89a10e802032e229fdf4ab250f80727a68197f8144d615
4
+ data.tar.gz: 9d1dcfcc05db65d1669049c87f36bf9a252fe91ce68b9298a7578ce630aeae50
5
5
  SHA512:
6
- metadata.gz: d2d5f401306d119e14a761aa1937f6c5777f69458d9db1367d35bd2a02fcea8a2d41876db4331a0086af8ec49db00b3f892d3c76dbcbacd6d297edc836cfb3db
7
- data.tar.gz: e5e4ffc4e9e7874807a2b3af33fb3f846752acce0e9748d06c79247e54f7120ffb38ac5a764c33a629ee05009c62154864f41f975a7fa19985f77b96caa5d53e
6
+ metadata.gz: 2dc34142c2553b747290451db7ca631dd12b31e9e7488502e4a54b3fc1cf372a7dd498c951498f8c3b86d519172bb879fb7f650e97448260dd8fad2c5c5c8662
7
+ data.tar.gz: 513cb240285c58fbf784d3a61fae066bf7c768836cdc06281357b92f8c7a62ad72ea2c0114f3ba6645da29b4ed01fbabff616f10db91f93b8194c587d2f9c623
data/README.md CHANGED
@@ -1,10 +1,20 @@
1
- # coercive
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
@@ -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
@@ -1,16 +1,12 @@
1
1
  require "ipaddr"
2
2
  require "uri"
3
3
 
4
- module Coercion
4
+ module Coercive
5
5
  module URI
6
- # Setting this `true` allows outbound connections to private IP addresses,
7
- # bypassing the security check that the IP address is public. This is designed
8
- # to be used in devlopment so that the tests can connect to local services.
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 Coercion::Error.new("not_valid")
43
+ fail Coercive::Error.new("not_valid")
47
44
  end
48
45
 
49
- fail Coercion::Error.new("no_host") unless uri.host
50
- fail Coercion::Error.new("not_resolvable") unless resolvable_public_ip?(uri) || ALLOW_PRIVATE_IP_CONNECTIONS
51
- fail Coercion::Error.new("no_path") if require_path && uri.path.empty?
52
- fail Coercion::Error.new("no_port") if require_port && !uri.port
53
- fail Coercion::Error.new("no_user") if require_user && !uri.user
54
- fail Coercion::Error.new("no_password") if require_password && !uri.password
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 Coercion::Error
60
- fail Coercion::Error.new("unsupported_schema")
56
+ rescue Coercive::Error
57
+ fail Coercive::Error.new("unsupported_schema")
61
58
  end
62
59
  end
63
60
 
@@ -113,7 +113,7 @@ describe "Coercive" do
113
113
  fixnum = 2
114
114
  rational = 2 ** -2
115
115
  bignum = 2 ** 64
116
- bigdecimal = BigDecimal.new("0.1")
116
+ bigdecimal = BigDecimal("0.1")
117
117
 
118
118
  [fixnum, rational, bignum, bigdecimal].each do |value|
119
119
  attributes = { "foo" => value }
@@ -11,7 +11,7 @@ describe "Coercive::URI" do
11
11
  assert_equal errors, e.errors
12
12
  end
13
13
 
14
- setup do
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
- test "coerces a valid string to a URI" do
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
- test "errors if input is an invalid URI" do
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
- test "errors if the input is longer than the declared maximum size" do
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
- test "errors if the input is shorter than the declared minimum size" do
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
- test "errors if the URI is an empty string" do
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
- test "errors if no host" do
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
- test "errors if schema is not supported" do
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
- test "errors if required elements are not provided" do
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
- test "errors when the URI host is an IP in the range #{first}..#{last}" do
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
- test "errors when the URI host is not resolvable" do
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
- test "errors when the URI host resolves to an IP in a private range" do
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
- test "allows a URI host to be IP that isn't in a private range" do
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
- test "allows a URI host that resolves to an IP not in a private range" do
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
- test "allows a URI with no explicit path component" do
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
- test "errors for a string that does not pass URI.parse" do
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
- test "errors for a URL that passes URI.parse, but is ill-formed" do
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.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-04 00:00:00.000000000 Z
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