http-security 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.rspec +1 -0
- data/.travis.yml +21 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +17 -0
- data/Gemfile +17 -0
- data/LICENSE.txt +20 -0
- data/README.md +90 -0
- data/Rakefile +34 -0
- data/http-security.gemspec +23 -0
- data/lib/http/security.rb +2 -0
- data/lib/http/security/exceptions.rb +8 -0
- data/lib/http/security/headers.rb +12 -0
- data/lib/http/security/headers/cache_control.rb +36 -0
- data/lib/http/security/headers/content_security_policy.rb +71 -0
- data/lib/http/security/headers/content_security_policy_report_only.rb +10 -0
- data/lib/http/security/headers/pragma.rb +24 -0
- data/lib/http/security/headers/public_key_pins.rb +60 -0
- data/lib/http/security/headers/public_key_pins_report_only.rb +10 -0
- data/lib/http/security/headers/set_cookie.rb +75 -0
- data/lib/http/security/headers/strict_transport_security.rb +29 -0
- data/lib/http/security/headers/x_content_type_options.rb +24 -0
- data/lib/http/security/headers/x_frame_options.rb +39 -0
- data/lib/http/security/headers/x_permitted_cross_domain_policies.rb +47 -0
- data/lib/http/security/headers/x_xss_protection.rb +34 -0
- data/lib/http/security/http_date.rb +13 -0
- data/lib/http/security/malformed_header.rb +33 -0
- data/lib/http/security/parsers.rb +14 -0
- data/lib/http/security/parsers/cache_control.rb +62 -0
- data/lib/http/security/parsers/content_security_policy.rb +128 -0
- data/lib/http/security/parsers/content_security_policy_report_only.rb +10 -0
- data/lib/http/security/parsers/expires.rb +19 -0
- data/lib/http/security/parsers/parser.rb +408 -0
- data/lib/http/security/parsers/pragma.rb +25 -0
- data/lib/http/security/parsers/public_key_pins.rb +43 -0
- data/lib/http/security/parsers/public_key_pins_report_only.rb +10 -0
- data/lib/http/security/parsers/set_cookie.rb +62 -0
- data/lib/http/security/parsers/strict_transport_security.rb +42 -0
- data/lib/http/security/parsers/x_content_type_options.rb +19 -0
- data/lib/http/security/parsers/x_frame_options.rb +47 -0
- data/lib/http/security/parsers/x_permitted_cross_domain_policies.rb +33 -0
- data/lib/http/security/parsers/x_xss_protection.rb +27 -0
- data/lib/http/security/response.rb +323 -0
- data/lib/http/security/version.rb +5 -0
- data/spec/data/alexa.csv +100 -0
- data/spec/headers/cache_control_spec.rb +40 -0
- data/spec/headers/content_security_policy_spec.rb +46 -0
- data/spec/headers/pragma_spec.rb +26 -0
- data/spec/headers/public_key_pins_spec.rb +68 -0
- data/spec/headers/set_cookie_spec.rb +122 -0
- data/spec/headers/strict_transport_security_spec.rb +39 -0
- data/spec/headers/x_content_type_options_spec.rb +26 -0
- data/spec/headers/x_frame_options_spec.rb +86 -0
- data/spec/headers/x_permitted_cross_domain_policies_spec.rb +108 -0
- data/spec/headers/x_xss_protection_spec.rb +59 -0
- data/spec/parsers/cache_control_spec.rb +26 -0
- data/spec/parsers/content_security_policy_report_only_spec.rb +48 -0
- data/spec/parsers/content_security_policy_spec.rb +74 -0
- data/spec/parsers/expires_spec.rb +71 -0
- data/spec/parsers/parser_spec.rb +317 -0
- data/spec/parsers/pragma_spec.rb +10 -0
- data/spec/parsers/public_key_pins_spec.rb +81 -0
- data/spec/parsers/set_cookie_spec.rb +55 -0
- data/spec/parsers/strict_transport_security_spec.rb +62 -0
- data/spec/parsers/x_content_type_options_spec.rb +10 -0
- data/spec/parsers/x_frame_options_spec.rb +24 -0
- data/spec/parsers/x_permitted_cross_domain_policies_spec.rb +34 -0
- data/spec/parsers/x_xss_protection_spec.rb +39 -0
- data/spec/response_spec.rb +262 -0
- data/spec/spec_helper.rb +13 -0
- data/tasks/alexa.rb +40 -0
- metadata +171 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "http/security/parsers/strict_transport_security"
|
3
|
+
|
4
|
+
describe Parsers::StrictTransportSecurity do
|
5
|
+
it "accepts only max-age" do
|
6
|
+
header = "max-age=31536000"
|
7
|
+
|
8
|
+
expect(subject.parse(header)).to be == {max_age: 31536000}
|
9
|
+
end
|
10
|
+
|
11
|
+
it "accepts max-age of zero" do
|
12
|
+
header = "max-age=0"
|
13
|
+
|
14
|
+
expect(subject.parse(header)).to eq(max_age: 0)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "accepts max-age then includeSubDomains" do
|
18
|
+
header = "max-age=0; includeSubDomains"
|
19
|
+
|
20
|
+
expect(subject.parse(header)).to be == {
|
21
|
+
max_age: 0,
|
22
|
+
includesubdomains: true
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
it "accepts includeSubDomains then max-age" do
|
27
|
+
header = "includeSubDomains; max-age=0"
|
28
|
+
|
29
|
+
expect(subject.parse(header)).to be == {
|
30
|
+
includesubdomains: true,
|
31
|
+
max_age: 0
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "stp_header_extension" do
|
36
|
+
subject { super().stp_header_extension }
|
37
|
+
|
38
|
+
it "accepts includedSubdomains" do
|
39
|
+
expect(subject.parse('includeSubDomains')).to be == {
|
40
|
+
key: "includeSubDomains"
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
it "accepts an unsupported token" do
|
45
|
+
expect(subject.parse("preload")).to be == {name: 'preload'}
|
46
|
+
end
|
47
|
+
|
48
|
+
it "accepts an unsupported token=token" do
|
49
|
+
expect(subject.parse("foo=bar")).to be == {
|
50
|
+
name: 'foo',
|
51
|
+
value: 'bar'
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
it "accepts token=\"string\"" do
|
56
|
+
expect(subject.parse('foo="string"')).to be == {
|
57
|
+
name: 'foo',
|
58
|
+
value: {string: 'string'}
|
59
|
+
}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "http/security/parsers/x_frame_options"
|
3
|
+
|
4
|
+
describe Parsers::XFrameOptions do
|
5
|
+
it "parses deny" do
|
6
|
+
header = "deny"
|
7
|
+
|
8
|
+
expect(subject.parse(header)).to be == {deny: true}
|
9
|
+
end
|
10
|
+
|
11
|
+
it "parses allow-from" do
|
12
|
+
header = "allow-from http://www.example.com"
|
13
|
+
|
14
|
+
expect(subject.parse(header)).to be == {
|
15
|
+
allow_from: URI("http://www.example.com")
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
it "parses sameorigin" do
|
20
|
+
header = "sameorigin"
|
21
|
+
|
22
|
+
expect(subject.parse(header)).to be == {sameorigin: true}
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "http/security/parsers/x_permitted_cross_domain_policies"
|
3
|
+
|
4
|
+
describe Parsers::XPermittedCrossDomainPolicies do
|
5
|
+
it "accepts none" do
|
6
|
+
header = "none"
|
7
|
+
|
8
|
+
expect(subject.parse(header)).to be == {none: true}
|
9
|
+
end
|
10
|
+
|
11
|
+
it "accepts master-only" do
|
12
|
+
header = "master-only"
|
13
|
+
|
14
|
+
expect(subject.parse(header)).to be == {master_only: true}
|
15
|
+
end
|
16
|
+
|
17
|
+
it "accepts by-content-type" do
|
18
|
+
header = "by-content-type"
|
19
|
+
|
20
|
+
expect(subject.parse(header)).to be == {by_content_type: true}
|
21
|
+
end
|
22
|
+
|
23
|
+
it "accepts by-ftp-filename" do
|
24
|
+
header = "by-ftp-filename"
|
25
|
+
|
26
|
+
expect(subject.parse(header)).to be == {by_ftp_filename: true}
|
27
|
+
end
|
28
|
+
|
29
|
+
it "accepts all" do
|
30
|
+
header = "all"
|
31
|
+
|
32
|
+
expect(subject.parse(header)).to be == {all: true}
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "http/security/parsers/x_xss_protection"
|
3
|
+
|
4
|
+
describe Parsers::XXSSProtection do
|
5
|
+
it "it accepts 1" do
|
6
|
+
header = "1"
|
7
|
+
|
8
|
+
expect(subject.parse(header)).to eq(enabled: true)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "it accepts 0" do
|
12
|
+
header = "0"
|
13
|
+
|
14
|
+
expect(subject.parse(header)).to eq(enabled: false)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "it accepts 1; mode=block" do
|
18
|
+
header = "1; mode=block"
|
19
|
+
|
20
|
+
expect(subject.parse(header)).to eq(enabled: true, mode: 'block')
|
21
|
+
end
|
22
|
+
|
23
|
+
it "it accepts 0; mode=block" do
|
24
|
+
header = "0; mode=block"
|
25
|
+
|
26
|
+
expect(subject.parse(header)).to eq(enabled: false, mode: 'block')
|
27
|
+
end
|
28
|
+
|
29
|
+
it "it accepts 1; mode=block; report=..." do
|
30
|
+
report_uri = "/xss-report/25b8988e-64ff-45a8-b0c6-2700fc1e9abd?source%5Baction%5D=index&source%5Bcontroller%5D=shop&source%5Bsection%5D=storefront"
|
31
|
+
header = "1; mode=block; report=#{report_uri}"
|
32
|
+
|
33
|
+
expect(subject.parse(header)).to eq(
|
34
|
+
enabled: true,
|
35
|
+
mode: 'block',
|
36
|
+
report: report_uri
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,262 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "http/security/response"
|
3
|
+
|
4
|
+
describe Response do
|
5
|
+
subject { described_class }
|
6
|
+
|
7
|
+
let(:response) do
|
8
|
+
{
|
9
|
+
"Cache-Control" => "no-cache, no-store, must-revalidate, pre-check=0, post-check=0",
|
10
|
+
"Content-Length" => "12682",
|
11
|
+
"Content-Security-Policy" => "default-src https:; connect-src https:; font-src https: data:; frame-src https: twitter:; img-src https: data:; media-src https:; object-src https:; script-src 'unsafe-inline' 'unsafe-eval' https:; style-src 'unsafe-inline' https:; report-uri https://twitter.com/i/csp_report?a=NVQWGYLXFVZXO2LGOQ%3D%3D%3D%3D%3D%3D&ro=false;",
|
12
|
+
"Content-Security-Policy-Report-Only" => "default-src https:; connect-src https:; font-src https: data:; frame-src https: twitter:; img-src https: data:; media-src https:; object-src https:; script-src 'unsafe-inline' 'unsafe-eval' https:; style-src 'unsafe-inline' https:; report-uri https://twitter.com/i/csp_report?a=NVQWGYLXFVZXO2LGOQ%3D%3D%3D%3D%3D%3D&ro=false;",
|
13
|
+
"Content-Type" => "text/html;charset=utf-8",
|
14
|
+
"Date" => "Thu, 20 Nov 2014 00:27:36 UTC",
|
15
|
+
"Expires" => "Tue, 31 Mar 1981 05:00:00 GMT",
|
16
|
+
"Last-Modified" => "Thu, 20 Nov 2014 00:27:36 GMT",
|
17
|
+
"Ms" => "A",
|
18
|
+
"Pragma" => "no-cache",
|
19
|
+
"Public-Key-Pins" => "pin-sha256=\"j+rQEAhMMJvg6xmn0rzlpe4WZgr7dz9tc7bVhUTsY4E=\"; pin-sha256=\"klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=\"; pin-sha256=\"6X0iNAQtPIjXKEVcqZBwyMcRwq1yW60549axatu3oDE=\"; max-age=0; includeSubDomains",
|
20
|
+
"Public-Key-Pins-Report-Only" => "pin-sha256=\"j+rQEAhMMJvg6xmn0rzlpe4WZgr7dz9tc7bVhUTsY4E=\"; pin-sha256=\"klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=\"; pin-sha256=\"6X0iNAQtPIjXKEVcqZBwyMcRwq1yW60549axatu3oDE=\"; max-age=0; includeSubDomains",
|
21
|
+
"Server" => "tsa_b",
|
22
|
+
"Set-Cookie" =>
|
23
|
+
"_twitter_sess=BAh7CSIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo%250ASGFzaHsABjoKQHVzZWR7ADoPY3JlYXRlZF9hdGwrCOzcmMpJAToMY3NyZl9p%250AZCIlYmEzNTQ5YzM0MzYwZjAzZWMwMTFmZDY3MzVhMjE0MzM6B2lkIiUxMzI3%250AY2M1OWIyYzM3N2IzMmYxZWZiNmJlN2ZmYzdjZQ%253D%253D--09c51d06332d2b4cf102948a3f0491131ed952fa; Path=/; Domain=.twitter.com; Secure; HTTPOnly, guest_id=v1%3A141644325604142464; Domain=.twitter.com; Path=/; Expires=Sat, 19 Nov 2016 00:27:36 GMT",
|
24
|
+
"Status" => "200 OK",
|
25
|
+
"Strict-Transport-Security" => "max-age=631138519",
|
26
|
+
"X-Connection-Hash" => "f58cf3aa568cfd2abfd6a259c85a453b",
|
27
|
+
"X-Content-Type-Options" => "nosniff",
|
28
|
+
"X-Frame-Options" => "SAMEORIGIN",
|
29
|
+
"X-Transaction" => "a0c1a67d4d799176",
|
30
|
+
"X-Permitted-Cross-Domain-Policies" => "none",
|
31
|
+
"X-Ua-Compatible" => "IE=edge,chrome=1",
|
32
|
+
"X-Xss-Protection" => "1; mode=block"
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
describe ".parse_header" do
|
37
|
+
subject { described_class }
|
38
|
+
|
39
|
+
context "when parsing a valid header" do
|
40
|
+
let(:name) { 'Set-Cookie' }
|
41
|
+
let(:value) { "_twitter_sess=BAh7CSIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo%250ASGFzaHsABjoKQHVzZWR7ADoPY3JlYXRlZF9hdGwrCOzcmMpJAToMY3NyZl9p%250AZCIlYmEzNTQ5YzM0MzYwZjAzZWMwMTFmZDY3MzVhMjE0MzM6B2lkIiUxMzI3%250AY2M1OWIyYzM3N2IzMmYxZWZiNmJlN2ZmYzdjZQ%253D%253D--09c51d06332d2b4cf102948a3f0491131ed952fa; Path=/; Domain=.twitter.com; Secure; HTTPOnly, guest_id=v1%3A141644325604142464; Domain=.twitter.com; Path=/; Expires=Sat, 19 Nov 2016 00:27:36 GMT" }
|
42
|
+
|
43
|
+
subject { super().parse_header(name,value) }
|
44
|
+
|
45
|
+
it "should parse the given header" do
|
46
|
+
expect(subject).to be_kind_of(Headers::SetCookie)
|
47
|
+
|
48
|
+
expect(subject.cookies[0].name).to be == :_twitter_sess
|
49
|
+
expect(subject.cookies[0].value).to be == "BAh7CSIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo%250ASGFzaHsABjoKQHVzZWR7ADoPY3JlYXRlZF9hdGwrCOzcmMpJAToMY3NyZl9p%250AZCIlYmEzNTQ5YzM0MzYwZjAzZWMwMTFmZDY3MzVhMjE0MzM6B2lkIiUxMzI3%250AY2M1OWIyYzM3N2IzMmYxZWZiNmJlN2ZmYzdjZQ%253D%253D--09c51d06332d2b4cf102948a3f0491131ed952fa"
|
50
|
+
expect(subject.cookies[0].path).to be == '/'
|
51
|
+
expect(subject.cookies[0].domain).to be == '.twitter.com'
|
52
|
+
expect(subject.cookies[0]).to be_secure
|
53
|
+
expect(subject.cookies[0]).to be_http_only
|
54
|
+
|
55
|
+
expect(subject.cookies[1].name).to be == :guest_id
|
56
|
+
expect(subject.cookies[1].value).to be == 'v1%3A141644325604142464'
|
57
|
+
expect(subject.cookies[1].domain).to be == '.twitter.com'
|
58
|
+
expect(subject.cookies[1].path).to be == '/'
|
59
|
+
expect(subject.cookies[1].expires).to be == HTTPDate.parse('Sat, 19 Nov 2016 00:27:36 GMT')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "when parsing an invalid header" do
|
64
|
+
let(:name) { 'Set-Cookie' }
|
65
|
+
let(:value) { "foo" }
|
66
|
+
|
67
|
+
it "should raise an InvalidHeader exception" do
|
68
|
+
expect {
|
69
|
+
subject.parse_header(name,value)
|
70
|
+
}.to raise_error(InvalidHeader)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe ".parse" do
|
76
|
+
subject { described_class.parse(response) }
|
77
|
+
|
78
|
+
it "should parse Cache-Control" do
|
79
|
+
expect(subject.cache_control).to be_kind_of(Headers::CacheControl)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should parse Content-Security-Policy" do
|
83
|
+
expect(subject.content_security_policy).to be_kind_of(Headers::ContentSecurityPolicy)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should parse Content-Security-Policy-Report-Only" do
|
87
|
+
expect(subject.content_security_policy_report_only).to be_kind_of(Headers::ContentSecurityPolicyReportOnly)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should parse Expires" do
|
91
|
+
expect(subject.expires).to be_kind_of(HTTPDate)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should parse Pragma" do
|
95
|
+
expect(subject.pragma).to be_kind_of(Headers::Pragma)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should parse Public-Key-Pins" do
|
99
|
+
expect(subject.public_key_pins).to be_kind_of(Headers::PublicKeyPins)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should parse Public-Key-Pins-Report-Only" do
|
103
|
+
expect(subject.public_key_pins_report_only).to be_kind_of(Headers::PublicKeyPinsReportOnly)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should parse Set-Cookie" do
|
107
|
+
expect(subject.set_cookie).to be_kind_of(Headers::SetCookie)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should parse Strict-Transport-Security" do
|
111
|
+
expect(subject.strict_transport_security).to be_kind_of(Headers::StrictTransportSecurity)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should parse X-Content-Type-Options" do
|
115
|
+
expect(subject.x_content_type_options).to be_kind_of(Headers::XContentTypeOptions)
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should parse X-Frame-Options" do
|
119
|
+
expect(subject.x_frame_options).to be_kind_of(Headers::XFrameOptions)
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should parse X-Permitted-Cross-Domain-Policies" do
|
123
|
+
expect(subject.x_permitted_cross_domain_policies).to be_kind_of(Headers::XPermittedCrossDomainPolicies)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should parse X-XSS-Protection" do
|
127
|
+
expect(subject.x_xss_protection).to be_kind_of(Headers::XXSSProtection)
|
128
|
+
end
|
129
|
+
|
130
|
+
context "when parsing a malformed headers" do
|
131
|
+
let(:response) do
|
132
|
+
{
|
133
|
+
"Date"=>"Sat, 24 Jan 2015 01:06:57 GMT",
|
134
|
+
"X-Servedby"=>"ny1-prod6-web022.int.peer1.squarespace.net",
|
135
|
+
"Set-Cookie"=>
|
136
|
+
"JSESSIONID=3bGO1nRYPl_-0xhNbk8nlpt-5ulKdHw9_ofbsM_wsJ-Wcul2ODOLqQ;Path=/;HttpOnly, crumb=ce3ba8cfc3;Path=/, SS_MID=0763cce4-5adf-4332-a545-f5b4ebefd803i5aarqtx;Path=/;Domain=.singularads.com;Expires=Tue, 21-Jan-2025 01:06:57 GMT",
|
137
|
+
"Expires"=>"Thu, 01 Jan 1970 00:00:00 GMT",
|
138
|
+
"Accept-Ranges"=>"bytes",
|
139
|
+
"Content-Type"=>"text/html; charset=UTF-8",
|
140
|
+
"X-Pc-Appver"=>"3070",
|
141
|
+
"Content-Encoding"=>"gzip",
|
142
|
+
"X-Pc-Date"=>"Fri, 23 Jan 2015 21:00:13 GMT",
|
143
|
+
"X-Pc-Host"=>"10.100.101.11",
|
144
|
+
"Etag"=>"W/\"0eaeb395a0f1a569174a067510085a66\"",
|
145
|
+
"X-Pc-Key"=>"m7ZrcPWNTQjYc6jL7aW2oRSLPko-daniel-matalon-vim3",
|
146
|
+
"X-Pc-Hit"=>"true",
|
147
|
+
"Content-Length"=>"27469",
|
148
|
+
"X-Contextid"=>"nLOTffVs/ksjmZDEy",
|
149
|
+
"X-Via"=>"1.1 ny1-prod-echo016.int.peer1.squarespace.net"
|
150
|
+
}
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should map exceptions to MalformedHeader objects" do
|
154
|
+
expect(subject.set_cookie).to be_kind_of(MalformedHeader)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context "Alexa 100", :gauntlet do
|
159
|
+
require 'csv'
|
160
|
+
require 'net/http'
|
161
|
+
|
162
|
+
path = File.expand_path('../data/alexa.csv', __FILE__)
|
163
|
+
csv = CSV.new(open(path), headers: false)
|
164
|
+
|
165
|
+
csv.each do |row|
|
166
|
+
rank, domain = row[0].to_i, row[1].downcase
|
167
|
+
|
168
|
+
context domain do
|
169
|
+
it "should not raise a ParseError" do
|
170
|
+
begin
|
171
|
+
response = Net::HTTP.get_response(URI("http://#{domain}/"))
|
172
|
+
|
173
|
+
expect {
|
174
|
+
described_class.parse(response)
|
175
|
+
}.to_not raise_error(Parslet::ParseError)
|
176
|
+
rescue => error
|
177
|
+
pending error.message
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe ".parse!" do
|
186
|
+
let(:response) do
|
187
|
+
{
|
188
|
+
"Date"=>"Sat, 24 Jan 2015 01:06:57 GMT",
|
189
|
+
"X-Servedby"=>"ny1-prod6-web022.int.peer1.squarespace.net",
|
190
|
+
"Set-Cookie"=>
|
191
|
+
"JSESSIONID=3bGO1nRYPl_-0xhNbk8nlpt-5ulKdHw9_ofbsM_wsJ-Wcul2ODOLqQ;Path=/;HttpOnly, crumb=ce3ba8cfc3;Path=/, SS_MID=0763cce4-5adf-4332-a545-f5b4ebefd803i5aarqtx;Path=/;Domain=.singularads.com;Expires=Tue, 21-Jan-2025 01:06:57 GMT",
|
192
|
+
"Expires"=>"Thu, 01 Jan 1970 00:00:00 GMT",
|
193
|
+
"Accept-Ranges"=>"bytes",
|
194
|
+
"Content-Type"=>"text/html; charset=UTF-8",
|
195
|
+
"X-Pc-Appver"=>"3070",
|
196
|
+
"Content-Encoding"=>"gzip",
|
197
|
+
"X-Pc-Date"=>"Fri, 23 Jan 2015 21:00:13 GMT",
|
198
|
+
"X-Pc-Host"=>"10.100.101.11",
|
199
|
+
"Etag"=>"W/\"0eaeb395a0f1a569174a067510085a66\"",
|
200
|
+
"X-Pc-Key"=>"m7ZrcPWNTQjYc6jL7aW2oRSLPko-daniel-matalon-vim3",
|
201
|
+
"X-Pc-Hit"=>"true",
|
202
|
+
"Content-Length"=>"27469",
|
203
|
+
"X-Contextid"=>"nLOTffVs/ksjmZDEy",
|
204
|
+
"X-Via"=>"1.1 ny1-prod-echo016.int.peer1.squarespace.net"
|
205
|
+
}
|
206
|
+
end
|
207
|
+
|
208
|
+
subject { described_class }
|
209
|
+
|
210
|
+
context "when parsing malformed headers" do
|
211
|
+
it "should raise a Parslet::ParseFailed exception" do
|
212
|
+
expect {
|
213
|
+
subject.parse!(response)
|
214
|
+
}.to raise_error(Parslet::ParseFailed)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
describe "#[]" do
|
220
|
+
subject { described_class.parse(response) }
|
221
|
+
|
222
|
+
it "should retrieve a single header value" do
|
223
|
+
expect(subject['Cache-Control']).to be(subject.cache_control)
|
224
|
+
end
|
225
|
+
|
226
|
+
context "when given an unknown header name" do
|
227
|
+
it "should raise a KeyError" do
|
228
|
+
expect { expect(subject['Foo-Bar']) }.to raise_error(KeyError)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
describe "#each" do
|
234
|
+
subject { described_class.parse(response) }
|
235
|
+
|
236
|
+
context "when given a block" do
|
237
|
+
it "should yield each header name and parsed value" do
|
238
|
+
expect { |b| subject.each(&b) }.to yield_successive_args(
|
239
|
+
['Cache-Control', subject.cache_control],
|
240
|
+
['Content-Security-Policy', subject.content_security_policy],
|
241
|
+
['Content-Security-Policy-Report-Only', subject.content_security_policy_report_only],
|
242
|
+
['Expires', subject.expires],
|
243
|
+
['Pragma', subject.pragma],
|
244
|
+
['Public-Key-Pins', subject.public_key_pins],
|
245
|
+
['Public-Key-Pins-Report-Only', subject.public_key_pins_report_only],
|
246
|
+
['Strict-Transport-Security', subject.strict_transport_security],
|
247
|
+
['Set-Cookie', subject.set_cookie],
|
248
|
+
['X-Content-Type-Options', subject.x_content_type_options],
|
249
|
+
['X-Frame-Options', subject.x_frame_options],
|
250
|
+
['X-Permitted-Cross-Domain-Policies', subject.x_permitted_cross_domain_policies],
|
251
|
+
['X-Xss-Protection', subject.x_xss_protection]
|
252
|
+
)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
context "when no block is given" do
|
257
|
+
it "should return an Enumerator" do
|
258
|
+
expect(subject.each).to be_kind_of(Enumerator)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
if ENV['CODECLIMATE_REPO_TOKEN']
|
2
|
+
require "codeclimate-test-reporter"
|
3
|
+
CodeClimate::TestReporter.start
|
4
|
+
end
|
5
|
+
|
6
|
+
require 'rspec'
|
7
|
+
require 'http/security/version'
|
8
|
+
|
9
|
+
include HTTP::Security
|
10
|
+
|
11
|
+
RSpec.configure do |specs|
|
12
|
+
specs.filter_run_excluding :gauntlet
|
13
|
+
end
|