cors 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 +7 -0
- data/.gitignore +1 -0
- data/Gemfile.lock +28 -14
- data/README.md +13 -6
- data/cors.gemspec +9 -2
- data/lib/cors.rb +1 -0
- data/lib/cors/policy.rb +16 -7
- data/lib/cors/policy/s3.rb +1 -2
- data/lib/cors/policy/s3_post.rb +66 -0
- data/lib/cors/rules.rb +10 -0
- data/lib/cors/version.rb +1 -1
- data/spec/policies/s3_post_spec.rb +60 -0
- data/spec/policies/s3_spec.rb +10 -12
- data/spec/{cors_spec.rb → policy_spec.rb} +26 -6
- data/spec/rules_spec.rb +38 -16
- metadata +48 -42
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6e34544029bab24989baf0b992473455fe72c74dc16f8abd9fae3bd48dad3738
|
4
|
+
data.tar.gz: 2b86a4a89e1577d08051d5599d44bcb43f2fd90fc04da1688c196cc4b4ad161d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 347db7c99d84fff7342fc6f5c57574caf02135b6a4a87bdd2886afcaa9f1d4d6191a5c187a7b6454161786ea42198ed058329ea7a6ede863b6cae951f45a5b7f
|
7
|
+
data.tar.gz: e38b7fbb900d6507acc746348b85b8979264be0619f58f2e1611e77539994300f4ccc5e0bc8e91385e856a3df7168ec8ea781d8dfea1caf5ee295a77f38ee647
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,24 +1,38 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
cors (1.0.1)
|
5
|
+
multi_json
|
6
|
+
|
1
7
|
GEM
|
2
|
-
remote:
|
8
|
+
remote: https://rubygems.org/
|
3
9
|
specs:
|
4
|
-
diff-lcs (1.
|
5
|
-
|
6
|
-
|
7
|
-
rspec (
|
8
|
-
rspec-core (~>
|
9
|
-
rspec-expectations (~>
|
10
|
-
rspec-mocks (~>
|
11
|
-
rspec-core (
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
diff-lcs (1.3)
|
11
|
+
multi_json (1.13.1)
|
12
|
+
rake (12.3.3)
|
13
|
+
rspec (3.8.0)
|
14
|
+
rspec-core (~> 3.8.0)
|
15
|
+
rspec-expectations (~> 3.8.0)
|
16
|
+
rspec-mocks (~> 3.8.0)
|
17
|
+
rspec-core (3.8.2)
|
18
|
+
rspec-support (~> 3.8.0)
|
19
|
+
rspec-expectations (3.8.4)
|
20
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
21
|
+
rspec-support (~> 3.8.0)
|
22
|
+
rspec-mocks (3.8.1)
|
23
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
24
|
+
rspec-support (~> 3.8.0)
|
25
|
+
rspec-support (3.8.2)
|
26
|
+
yard (0.9.20)
|
16
27
|
|
17
28
|
PLATFORMS
|
18
29
|
ruby
|
19
30
|
|
20
31
|
DEPENDENCIES
|
32
|
+
cors!
|
21
33
|
rake
|
22
|
-
redcarpet
|
23
34
|
rspec
|
24
35
|
yard
|
36
|
+
|
37
|
+
BUNDLED WITH
|
38
|
+
1.16.6
|
data/README.md
CHANGED
@@ -1,15 +1,22 @@
|
|
1
|
-
# CORS policy validation- and signing library
|
1
|
+
# CORS policy validation- and signing library
|
2
2
|
|
3
|
-
[](http://travis-ci.org/varvet/cors)
|
4
4
|
|
5
5
|
Cross-origin resource sharing (CORS) is great; it allows your visitors to asynchronously upload files to
|
6
|
-
e.g.
|
6
|
+
e.g. Amazon S3, without the files having to round-trip through your web server. Unfortunately,
|
7
7
|
giving your users complete write access to your online storage also exposes you to malicious intent.
|
8
8
|
|
9
9
|
To combat harmful usage, good upload services that allow client-side upload, support a mechanism that allows
|
10
10
|
you to validate and sign all upload requests to your online storage. By validating every request, you can
|
11
11
|
give your visitors a nice upload experience, while keeping the bad visitors at bay.
|
12
12
|
|
13
|
+
## Deprecation
|
14
|
+
|
15
|
+
The functionality of CORS is now provided by the ruby AWS SDK. We recommend
|
16
|
+
using that instead, as CORS will no longer receive updates:
|
17
|
+
|
18
|
+
[Amazon S3 SDK PresignedPost](https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/PresignedPost.html)
|
19
|
+
|
13
20
|
## Usage
|
14
21
|
|
15
22
|
```ruby
|
@@ -36,10 +43,10 @@ end
|
|
36
43
|
|
37
44
|
## Supported services
|
38
45
|
|
39
|
-
Out-of-the box, the CORS library comes with support for the Amazon S3 REST API.
|
40
|
-
for Filepicker is planned.
|
46
|
+
Out-of-the box, the CORS library comes with support for the Amazon S3 REST API.
|
41
47
|
|
42
|
-
- [Amazon S3 REST API](http://docs.amazonwebservices.com/
|
48
|
+
- [Amazon S3 REST API Authentication Header](http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAuthentication.html#ConstructingTheAuthenticationHeader)
|
49
|
+
- [Amazon S3 REST API POST Upload Policy](http://docs.amazonwebservices.com/AmazonS3/latest/dev/HTTPPOSTForms.html#HTTPPOSTConstructPolicy)
|
43
50
|
|
44
51
|
## License
|
45
52
|
|
data/cors.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |gem|
|
|
10
10
|
gem.email = ["kim@burgestrand.se"]
|
11
11
|
gem.homepage = "http://github.com/elabs/cors"
|
12
12
|
gem.summary = "CORS policy validation- and signing library for Amazon S3 REST API."
|
13
|
-
gem.description =
|
13
|
+
gem.description = <<~DESCRIPTION
|
14
14
|
Cross-origin resource sharing (CORS) is great; it allows your visitors to
|
15
15
|
asynchronously upload files to e.g. Filepicker or Amazon S3, without the
|
16
16
|
files having to round-trip through your web server. Unfortunately, giving
|
@@ -25,11 +25,18 @@ Gem::Specification.new do |gem|
|
|
25
25
|
|
26
26
|
The CORS gem comes with support for the Amazon S3 REST API.
|
27
27
|
DESCRIPTION
|
28
|
+
gem.post_install_message = <<~POST_INSTALL_MESSAGE
|
29
|
+
[DEPRECATED] The CORS gem is deprecated and will not receive further updates.
|
30
|
+
The functionality of CORS is now provided by the ruby AWS SDK:
|
31
|
+
|
32
|
+
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/PresignedPost.html
|
33
|
+
POST_INSTALL_MESSAGE
|
28
34
|
|
29
35
|
gem.files = `git ls-files`.split($/)
|
30
36
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
31
37
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
32
38
|
gem.require_paths = ["lib"]
|
33
39
|
|
34
|
-
gem.
|
40
|
+
gem.add_dependency "multi_json"
|
41
|
+
gem.add_development_dependency "rspec"
|
35
42
|
end
|
data/lib/cors.rb
CHANGED
data/lib/cors/policy.rb
CHANGED
@@ -71,7 +71,7 @@ module CORS
|
|
71
71
|
# @see errors
|
72
72
|
# @see valid?
|
73
73
|
def initialize(attributes)
|
74
|
-
self.attributes = Hash[attributes
|
74
|
+
self.attributes = Hash[normalize_attributes(attributes)]
|
75
75
|
self.errors = rules.validate(self.attributes)
|
76
76
|
end
|
77
77
|
|
@@ -94,16 +94,25 @@ module CORS
|
|
94
94
|
errors.empty?
|
95
95
|
end
|
96
96
|
|
97
|
-
#
|
98
|
-
#
|
99
|
-
|
100
|
-
|
97
|
+
# Signs the manifest, but only if it is {#valid?}.
|
98
|
+
#
|
99
|
+
# @note should not be overridden by the includers!
|
100
|
+
# @return (see #sign!)
|
101
|
+
def sign(*args, &block)
|
102
|
+
sign!(*args, &block) if valid?
|
101
103
|
end
|
102
104
|
|
103
|
-
# @note
|
105
|
+
# @note should be overridden by includers!
|
104
106
|
# @return [String] signature derived from the manifest
|
105
|
-
def sign(*)
|
107
|
+
def sign!(*)
|
106
108
|
raise NotImplementedError, "#sign has not been defined on #{inspect}"
|
107
109
|
end
|
110
|
+
|
111
|
+
protected
|
112
|
+
|
113
|
+
def normalize_attributes(attributes)
|
114
|
+
attributes = attributes.map { |k, v| [k.to_s.downcase, v] }
|
115
|
+
attributes.select { |(k, _)| rules[k] }
|
116
|
+
end
|
108
117
|
end
|
109
118
|
end
|
data/lib/cors/policy/s3.rb
CHANGED
@@ -28,8 +28,7 @@ module CORS::Policy
|
|
28
28
|
#
|
29
29
|
# @param [String] access_key
|
30
30
|
# @param [String] secret_access_key
|
31
|
-
def sign(access_key, secret_access_key)
|
32
|
-
return if not valid?
|
31
|
+
def sign!(access_key, secret_access_key)
|
33
32
|
digest = OpenSSL::HMAC.digest("sha1", secret_access_key, manifest)
|
34
33
|
signature = Base64.strict_encode64(digest)
|
35
34
|
"AWS #{access_key}:#{signature}"
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "multi_json"
|
3
|
+
require "base64"
|
4
|
+
require "time"
|
5
|
+
require "set"
|
6
|
+
|
7
|
+
module CORS::Policy
|
8
|
+
# POST form upload policy for Amazon S3.
|
9
|
+
class S3Post
|
10
|
+
include CORS::Policy
|
11
|
+
|
12
|
+
# Generate the policy used to sign the request.
|
13
|
+
#
|
14
|
+
# @param [Time] expiration
|
15
|
+
# @return [Hash] policy as a hash
|
16
|
+
def policy(expiration)
|
17
|
+
conditions = []
|
18
|
+
properties = attributes.dup
|
19
|
+
|
20
|
+
if properties.has_key?("content-length")
|
21
|
+
length = escape(properties.delete("content-length"))
|
22
|
+
conditions << [ "content-length-range", length, length ]
|
23
|
+
end
|
24
|
+
|
25
|
+
{
|
26
|
+
"expiration" => expiration.gmtime.iso8601,
|
27
|
+
"conditions" => conditions + properties.map do |(name, value)|
|
28
|
+
[ "eq", "$#{escape(name)}", escape(value) ]
|
29
|
+
end
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
# Generate a policy and encode it in URL-safe Base64 encoding.
|
34
|
+
#
|
35
|
+
# @param [Time] expiration
|
36
|
+
# @return [String] the policy, in JSON, in URL-safe base64 encoding.
|
37
|
+
def policy_base64(expiration)
|
38
|
+
json = MultiJson.dump(policy(expiration))
|
39
|
+
Base64.urlsafe_encode64(json)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Sign the {#policy} for the given expiration.
|
43
|
+
#
|
44
|
+
# @param [String] secret_access_key
|
45
|
+
# @param [Time] expiration
|
46
|
+
# @return [String] signed policy in Base64-encoding.
|
47
|
+
def sign!(secret_access_key, expiration)
|
48
|
+
digest = OpenSSL::HMAC.digest("sha1", secret_access_key, policy_base64(expiration))
|
49
|
+
Base64.strict_encode64(digest)
|
50
|
+
end
|
51
|
+
|
52
|
+
protected
|
53
|
+
|
54
|
+
def escape(string)
|
55
|
+
return string unless string.is_a?(String)
|
56
|
+
|
57
|
+
unicode_safe = string.codepoints.each_with_object("") do |i, result|
|
58
|
+
result << if i <= 127 then i.chr else
|
59
|
+
"\\u#{i.to_s(16).rjust(4, "0")}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
unicode_safe.sub(/\A$/, "\$")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/cors/rules.rb
CHANGED
@@ -15,6 +15,7 @@ module CORS
|
|
15
15
|
# @yieldparam [Rules] self
|
16
16
|
def initialize
|
17
17
|
@rules = []
|
18
|
+
@rules_map = Hash.new { |h, k| h[k] = [] }
|
18
19
|
yield self if block_given?
|
19
20
|
end
|
20
21
|
|
@@ -40,6 +41,14 @@ module CORS
|
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
44
|
+
# Retrieve a list of rules for a given attribute.
|
45
|
+
#
|
46
|
+
# @param name same name as given to {#required} or {#optional}
|
47
|
+
# @return [Array<Hash<:name, :matcher, :required>>, nil] list of rules for attribute, or nil.
|
48
|
+
def [](name)
|
49
|
+
@rules_map.fetch(name, nil)
|
50
|
+
end
|
51
|
+
|
43
52
|
# Declare a required rule; the value must be present, and it must
|
44
53
|
# match the given constraints or block matcher.
|
45
54
|
#
|
@@ -76,6 +85,7 @@ module CORS
|
|
76
85
|
|
77
86
|
{ name: name, matcher: matcher, required: true }.tap do |rule|
|
78
87
|
@rules << rule
|
88
|
+
@rules_map[name] << rule
|
79
89
|
end
|
80
90
|
end
|
81
91
|
|
data/lib/cors/version.rb
CHANGED
@@ -0,0 +1,60 @@
|
|
1
|
+
describe CORS::Policy::S3Post do
|
2
|
+
let(:now) do
|
3
|
+
Time.at(1351696066)
|
4
|
+
end
|
5
|
+
|
6
|
+
let(:valid_attributes) do
|
7
|
+
{
|
8
|
+
"bucket" => "shokunin",
|
9
|
+
"key" => "upload/BEEFBEEFBEEF.jpg",
|
10
|
+
"x-amz-meta-filename" => "$dollar$.jpg",
|
11
|
+
"acl" => "public-read",
|
12
|
+
"content-type" => "image/jpeg",
|
13
|
+
"content-length" => 678,
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:rules) do
|
18
|
+
lambda do |manifest|
|
19
|
+
manifest.required "bucket", "shokunin"
|
20
|
+
manifest.required "key", %r!\Aupload/[a-f0-9A-F]{12}\.(jpg|png)!
|
21
|
+
manifest.required "acl", "public-read"
|
22
|
+
manifest.optional "x-amz-meta-filename", //
|
23
|
+
manifest.required "content-type", %r|\Aimage/|
|
24
|
+
manifest.required "content-length" do |bytesize|
|
25
|
+
(0..1024).cover?(bytesize)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
let(:manifest) { CORS::Policy::S3Post.create(&rules) }
|
31
|
+
|
32
|
+
describe "#manifest" do
|
33
|
+
it "generates the policy document as a hash" do
|
34
|
+
policy = manifest.new(valid_attributes).policy(now)
|
35
|
+
expect(policy["conditions"]).to eq([
|
36
|
+
[ "content-length-range", 678, 678 ],
|
37
|
+
[ "eq", "$bucket", "shokunin" ],
|
38
|
+
[ "eq", "$key", "upload/BEEFBEEFBEEF.jpg" ],
|
39
|
+
[ "eq", "$x-amz-meta-filename", "\$dollar$.jpg" ],
|
40
|
+
[ "eq", "$acl", "public-read" ],
|
41
|
+
[ "eq", "$content-type", "image/jpeg" ],
|
42
|
+
])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#policy" do
|
47
|
+
it "returns the Base64-encoded manifest" do
|
48
|
+
request = manifest.new(valid_attributes)
|
49
|
+
decoded = Base64.urlsafe_decode64(request.policy_base64(now))
|
50
|
+
expect(MultiJson.load(decoded)).to eq(request.policy(now))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "#sign!" do
|
55
|
+
it "unconditionally signs the manifest" do
|
56
|
+
request = manifest.new(valid_attributes)
|
57
|
+
expect(request.sign!("properties", now)).to eq("U1YETIAOqT9mEuCebm5B6BM6feQ=")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/spec/policies/s3_spec.rb
CHANGED
@@ -7,7 +7,7 @@ describe CORS::Policy::S3 do
|
|
7
7
|
"x-amz-meta-filename" => "roflcopter.gif",
|
8
8
|
"x-amz-date" => "2012-10-22T16:10:47+02:00",
|
9
9
|
"x-amz-meta-ROFLCOPTER" => ["yes", "no", "maybe"],
|
10
|
-
"x-
|
10
|
+
"x-amz-meta-ignored" => "I am ignored",
|
11
11
|
"filename" => "uploads/roflcopter.gif"
|
12
12
|
}
|
13
13
|
end
|
@@ -22,6 +22,10 @@ describe CORS::Policy::S3 do
|
|
22
22
|
manifest.required "x-amz-date" do |date|
|
23
23
|
"2012-10-22T16:10:47+02:00" == date
|
24
24
|
end
|
25
|
+
manifest.optional "x-amz-meta-filename", //
|
26
|
+
manifest.optional "x-amz-meta-roflcopter" do
|
27
|
+
true
|
28
|
+
end
|
25
29
|
manifest.required "filename", %r|uploads/|
|
26
30
|
end
|
27
31
|
end
|
@@ -31,7 +35,7 @@ describe CORS::Policy::S3 do
|
|
31
35
|
describe "#manifest" do
|
32
36
|
it "is built according to specifications" do
|
33
37
|
manifest = CORS::Policy::S3.create(&rules).new(valid_attributes)
|
34
|
-
manifest.manifest.
|
38
|
+
expect(manifest.manifest).to eq <<-MANIFEST.gsub(/^ +/, "").rstrip
|
35
39
|
PUT
|
36
40
|
CCummMp6o4ZgypU7ePh7QA==
|
37
41
|
image/jpeg
|
@@ -44,16 +48,10 @@ describe CORS::Policy::S3 do
|
|
44
48
|
end
|
45
49
|
end
|
46
50
|
|
47
|
-
describe "#sign" do
|
48
|
-
it "signs the manifest
|
49
|
-
|
50
|
-
|
51
|
-
end
|
52
|
-
|
53
|
-
it "does not sign if the manifest is invalid" do
|
54
|
-
manifest = CORS::Policy::S3.create(&rules).new(valid_attributes)
|
55
|
-
manifest.should_receive(:valid?).and_return(false)
|
56
|
-
manifest.sign("LAWL", "HELLO").should be_nil
|
51
|
+
describe "#sign!" do
|
52
|
+
it "unconditionally signs the manifest" do
|
53
|
+
policy = manifest.new(valid_attributes)
|
54
|
+
expect(policy.sign!("LAWL", "HELLO")).to eq "AWS LAWL:WZGsk2VzLz85B6oU19a5+fvzxXM="
|
57
55
|
end
|
58
56
|
end
|
59
57
|
end
|
@@ -27,11 +27,15 @@ describe CORS::Policy do
|
|
27
27
|
end
|
28
28
|
|
29
29
|
it "normalizes the attribute keys" do
|
30
|
-
manifest.new(
|
30
|
+
expect(manifest.new(anYTHIng: :Yo).attributes).to eq({ "anything" => :Yo })
|
31
|
+
end
|
32
|
+
|
33
|
+
it "removes attributes not covered by any rules" do
|
34
|
+
expect(manifest.new(cOOl: :Yo).attributes).to eq({})
|
31
35
|
end
|
32
36
|
|
33
37
|
it "populates the hash of errors" do
|
34
|
-
manifest.new({}).errors.
|
38
|
+
expect(manifest.new({}).errors).to_not be_empty
|
35
39
|
end
|
36
40
|
end
|
37
41
|
|
@@ -41,20 +45,36 @@ describe CORS::Policy do
|
|
41
45
|
end
|
42
46
|
|
43
47
|
it "returns the raw rules" do
|
44
|
-
manifest.rules.
|
48
|
+
expect(manifest.rules).to be_a CORS::Rules
|
45
49
|
end
|
46
50
|
end
|
47
51
|
|
48
52
|
describe "#valid?" do
|
49
53
|
it "returns true if validation succeeds" do
|
50
54
|
manifest.new(valid_attributes).tap do |manifest|
|
51
|
-
manifest.
|
52
|
-
manifest.errors.
|
55
|
+
expect(manifest).to be_valid
|
56
|
+
expect(manifest.errors).to eq({})
|
53
57
|
end
|
54
58
|
end
|
55
59
|
|
56
60
|
it "returns false if validation fails" do
|
57
|
-
manifest.new({}).
|
61
|
+
expect(manifest.new({})).to_not be_valid
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#sign" do
|
66
|
+
it "delegates to sign! if the manifest is valid" do
|
67
|
+
request = manifest.new(valid_attributes)
|
68
|
+
allow(request).to receive(:sign!).and_return("OK")
|
69
|
+
expect(request.sign(:a, :b)).to eq "OK"
|
70
|
+
expect(request).to have_received(:sign!)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "does nothing if the manifest is not valid" do
|
74
|
+
request = manifest.new({})
|
75
|
+
allow(request).to receive(:sign!)
|
76
|
+
expect(request).not_to have_received(:sign!)
|
77
|
+
expect(request.sign(:a, :b)).to be_nil
|
58
78
|
end
|
59
79
|
end
|
60
80
|
end
|
data/spec/rules_spec.rb
CHANGED
@@ -9,11 +9,33 @@ describe CORS::Rules do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
it "is enumerable" do
|
12
|
-
rules.each_with_object([]) { |rule, result| result << rule }.
|
12
|
+
expect(rules.each_with_object([]) { |rule, result| result << rule }).to eq(list)
|
13
13
|
end
|
14
14
|
|
15
15
|
it "returns an enumerator without a block" do
|
16
|
-
rules.each.with_object([]) { |rule, result| result << rule }.
|
16
|
+
expect(rules.each.with_object([]) { |rule, result| result << rule }).to eq(list)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#[]" do
|
21
|
+
let(:yay) { [] }
|
22
|
+
let(:boo) { [] }
|
23
|
+
|
24
|
+
let(:rules) do
|
25
|
+
CORS::Rules.new do |r|
|
26
|
+
yay << r.required("yay", //)
|
27
|
+
yay << r.required("yay", /./)
|
28
|
+
boo << r.optional("boo", //)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "returns the list of rules for a property" do
|
33
|
+
expect(rules["yay"]).to eq yay
|
34
|
+
expect(rules["boo"]).to eq boo
|
35
|
+
end
|
36
|
+
|
37
|
+
it "returns nil if there are no rules for the property" do
|
38
|
+
expect(rules["does_not_exist"]).to be_nil
|
17
39
|
end
|
18
40
|
end
|
19
41
|
|
@@ -26,37 +48,37 @@ describe CORS::Rules do
|
|
26
48
|
rules = CORS::Rules.new { |r| r.required "method", // }
|
27
49
|
errors = rules.validate({})
|
28
50
|
|
29
|
-
errors.
|
51
|
+
expect(errors).to eq({ "method" => [:required, rules.first] })
|
30
52
|
end
|
31
53
|
|
32
54
|
it "results in an error when the value does not match" do
|
33
55
|
rules = CORS::Rules.new { |r| r.required "content-type", %r|image/jpe?g| }
|
34
56
|
errors = rules.validate({ "content-type" => "image/png" })
|
35
57
|
|
36
|
-
errors.
|
58
|
+
expect(errors).to eq({ "content-type" => [:match, rules.first] })
|
37
59
|
end
|
38
60
|
|
39
61
|
it "can match a regexp" do
|
40
62
|
rules = CORS::Rules.new { |r| r.required "content-type", %r|image/jpe?g| }
|
41
63
|
|
42
|
-
rules.validate({ "content-type" => "image/jpeg" }).
|
43
|
-
rules.validate({ "content-type" => "image/jpg" }).
|
44
|
-
rules.validate({ "content-type" => "image/png" }).
|
64
|
+
expect(rules.validate({ "content-type" => "image/jpeg" })).to be_empty
|
65
|
+
expect(rules.validate({ "content-type" => "image/jpg" })).to be_empty
|
66
|
+
expect(rules.validate({ "content-type" => "image/png" })).to_not be_empty
|
45
67
|
end
|
46
68
|
|
47
69
|
it "can match a literal string" do
|
48
70
|
rules = CORS::Rules.new { |r| r.required "content-type", "image/jpeg" }
|
49
71
|
|
50
|
-
rules.validate({ "content-type" => "image/jpeg" }).
|
51
|
-
rules.validate({ "content-type" => "image/jpg" }).
|
72
|
+
expect(rules.validate({ "content-type" => "image/jpeg" })).to be_empty
|
73
|
+
expect(rules.validate({ "content-type" => "image/jpg" })).to_not be_empty
|
52
74
|
end
|
53
75
|
|
54
76
|
it "can match an array" do
|
55
77
|
rules = CORS::Rules.new { |r| r.required "content-type", ["image/jpeg", "image/png"] }
|
56
78
|
|
57
|
-
rules.validate({ "content-type" => "image/jpeg" }).
|
58
|
-
rules.validate({ "content-type" => "image/png" }).
|
59
|
-
rules.validate({ "content-type" => "image/jpg" }).
|
79
|
+
expect(rules.validate({ "content-type" => "image/jpeg" })).to be_empty
|
80
|
+
expect(rules.validate({ "content-type" => "image/png" })).to be_empty
|
81
|
+
expect(rules.validate({ "content-type" => "image/jpg" })).to_not be_empty
|
60
82
|
end
|
61
83
|
|
62
84
|
it "can match a block" do
|
@@ -66,8 +88,8 @@ describe CORS::Rules do
|
|
66
88
|
end
|
67
89
|
end
|
68
90
|
|
69
|
-
rules.validate({ "content-type" => "image/jpeg" }).
|
70
|
-
rules.validate({ "content-type" => "image/png" }).
|
91
|
+
expect(rules.validate({ "content-type" => "image/jpeg" })).to be_empty
|
92
|
+
expect(rules.validate({ "content-type" => "image/png" })).to_not be_empty
|
71
93
|
end
|
72
94
|
end
|
73
95
|
|
@@ -76,14 +98,14 @@ describe CORS::Rules do
|
|
76
98
|
rules = CORS::Rules.new { |r| r.optional "method", // }
|
77
99
|
errors = rules.validate({})
|
78
100
|
|
79
|
-
errors.
|
101
|
+
expect(errors).to be_empty
|
80
102
|
end
|
81
103
|
|
82
104
|
it "results in an error when the value is present but does not match" do
|
83
105
|
rules = CORS::Rules.new { |r| r.optional "content-type", %r|image/jpe?g| }
|
84
106
|
errors = rules.validate({ "content-type" => "image/png" })
|
85
107
|
|
86
|
-
errors.
|
108
|
+
expect(errors).to eq({ "content-type" => [:match, rules.first] })
|
87
109
|
end
|
88
110
|
end
|
89
111
|
end
|
metadata
CHANGED
@@ -1,65 +1,65 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cors
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
5
|
-
prerelease:
|
4
|
+
version: 1.0.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Kim Burgestrand
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2019-08-08 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: multi_json
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
14
27
|
- !ruby/object:Gem::Dependency
|
15
28
|
name: rspec
|
16
29
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
30
|
requirements:
|
19
|
-
- -
|
31
|
+
- - ">="
|
20
32
|
- !ruby/object:Gem::Version
|
21
|
-
version: '
|
33
|
+
version: '0'
|
22
34
|
type: :development
|
23
35
|
prerelease: false
|
24
36
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
37
|
requirements:
|
27
|
-
- -
|
38
|
+
- - ">="
|
28
39
|
- !ruby/object:Gem::Version
|
29
|
-
version: '
|
30
|
-
description:
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
youruserscompletewriteaccesstoyouronlinestoragealsoexposesyouto
|
37
|
-
|
38
|
-
maliciousintent.
|
39
|
-
|
40
|
-
|
41
|
-
Tocombatharmfulusage,gooduploadservicesthatallowclient-side
|
42
|
-
|
43
|
-
upload,supportamechanismthatallowsyoutovalidateandsignallupload
|
44
|
-
|
45
|
-
requeststoyouronlinestorage.Byvalidatingeveryrequest,youcangive
|
46
|
-
|
47
|
-
yourvisitorsaniceuploadexperience,whilekeepingthebadvisitorsat
|
40
|
+
version: '0'
|
41
|
+
description: |
|
42
|
+
Cross-origin resource sharing (CORS) is great; it allows your visitors to
|
43
|
+
asynchronously upload files to e.g. Filepicker or Amazon S3, without the
|
44
|
+
files having to round-trip through your web server. Unfortunately, giving
|
45
|
+
your users complete write access to your online storage also exposes you to
|
46
|
+
malicious intent.
|
48
47
|
|
48
|
+
To combat harmful usage, good upload services that allow client-side
|
49
|
+
upload, support a mechanism that allows you to validate and sign all upload
|
50
|
+
requests to your online storage. By validating every request, you can give
|
51
|
+
your visitors a nice upload experience, while keeping the bad visitors at
|
49
52
|
bay.
|
50
53
|
|
51
|
-
|
52
|
-
TheCORSgemcomeswithsupportfortheAmazonS3RESTAPI.
|
53
|
-
|
54
|
-
'
|
54
|
+
The CORS gem comes with support for the Amazon S3 REST API.
|
55
55
|
email:
|
56
56
|
- kim@burgestrand.se
|
57
57
|
executables: []
|
58
58
|
extensions: []
|
59
59
|
extra_rdoc_files: []
|
60
60
|
files:
|
61
|
-
- .gitignore
|
62
|
-
- .rspec
|
61
|
+
- ".gitignore"
|
62
|
+
- ".rspec"
|
63
63
|
- Gemfile
|
64
64
|
- Gemfile.lock
|
65
65
|
- README.md
|
@@ -68,38 +68,44 @@ files:
|
|
68
68
|
- lib/cors.rb
|
69
69
|
- lib/cors/policy.rb
|
70
70
|
- lib/cors/policy/s3.rb
|
71
|
+
- lib/cors/policy/s3_post.rb
|
71
72
|
- lib/cors/rules.rb
|
72
73
|
- lib/cors/version.rb
|
73
|
-
- spec/
|
74
|
+
- spec/policies/s3_post_spec.rb
|
74
75
|
- spec/policies/s3_spec.rb
|
76
|
+
- spec/policy_spec.rb
|
75
77
|
- spec/rules_spec.rb
|
76
78
|
- spec/spec_helper.rb
|
77
79
|
homepage: http://github.com/elabs/cors
|
78
80
|
licenses: []
|
79
|
-
|
81
|
+
metadata: {}
|
82
|
+
post_install_message: |
|
83
|
+
[DEPRECATED] The CORS gem is deprecated and will not receive further updates.
|
84
|
+
The functionality of CORS is now provided by the ruby AWS SDK:
|
85
|
+
|
86
|
+
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/PresignedPost.html
|
80
87
|
rdoc_options: []
|
81
88
|
require_paths:
|
82
89
|
- lib
|
83
90
|
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
-
none: false
|
85
91
|
requirements:
|
86
|
-
- -
|
92
|
+
- - ">="
|
87
93
|
- !ruby/object:Gem::Version
|
88
94
|
version: '0'
|
89
95
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
-
none: false
|
91
96
|
requirements:
|
92
|
-
- -
|
97
|
+
- - ">="
|
93
98
|
- !ruby/object:Gem::Version
|
94
99
|
version: '0'
|
95
100
|
requirements: []
|
96
101
|
rubyforge_project:
|
97
|
-
rubygems_version:
|
102
|
+
rubygems_version: 2.7.6
|
98
103
|
signing_key:
|
99
|
-
specification_version:
|
104
|
+
specification_version: 4
|
100
105
|
summary: CORS policy validation- and signing library for Amazon S3 REST API.
|
101
106
|
test_files:
|
102
|
-
- spec/
|
107
|
+
- spec/policies/s3_post_spec.rb
|
103
108
|
- spec/policies/s3_spec.rb
|
109
|
+
- spec/policy_spec.rb
|
104
110
|
- spec/rules_spec.rb
|
105
111
|
- spec/spec_helper.rb
|