http-security 0.1.0

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.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +21 -0
  5. data/.yardopts +1 -0
  6. data/ChangeLog.md +17 -0
  7. data/Gemfile +17 -0
  8. data/LICENSE.txt +20 -0
  9. data/README.md +90 -0
  10. data/Rakefile +34 -0
  11. data/http-security.gemspec +23 -0
  12. data/lib/http/security.rb +2 -0
  13. data/lib/http/security/exceptions.rb +8 -0
  14. data/lib/http/security/headers.rb +12 -0
  15. data/lib/http/security/headers/cache_control.rb +36 -0
  16. data/lib/http/security/headers/content_security_policy.rb +71 -0
  17. data/lib/http/security/headers/content_security_policy_report_only.rb +10 -0
  18. data/lib/http/security/headers/pragma.rb +24 -0
  19. data/lib/http/security/headers/public_key_pins.rb +60 -0
  20. data/lib/http/security/headers/public_key_pins_report_only.rb +10 -0
  21. data/lib/http/security/headers/set_cookie.rb +75 -0
  22. data/lib/http/security/headers/strict_transport_security.rb +29 -0
  23. data/lib/http/security/headers/x_content_type_options.rb +24 -0
  24. data/lib/http/security/headers/x_frame_options.rb +39 -0
  25. data/lib/http/security/headers/x_permitted_cross_domain_policies.rb +47 -0
  26. data/lib/http/security/headers/x_xss_protection.rb +34 -0
  27. data/lib/http/security/http_date.rb +13 -0
  28. data/lib/http/security/malformed_header.rb +33 -0
  29. data/lib/http/security/parsers.rb +14 -0
  30. data/lib/http/security/parsers/cache_control.rb +62 -0
  31. data/lib/http/security/parsers/content_security_policy.rb +128 -0
  32. data/lib/http/security/parsers/content_security_policy_report_only.rb +10 -0
  33. data/lib/http/security/parsers/expires.rb +19 -0
  34. data/lib/http/security/parsers/parser.rb +408 -0
  35. data/lib/http/security/parsers/pragma.rb +25 -0
  36. data/lib/http/security/parsers/public_key_pins.rb +43 -0
  37. data/lib/http/security/parsers/public_key_pins_report_only.rb +10 -0
  38. data/lib/http/security/parsers/set_cookie.rb +62 -0
  39. data/lib/http/security/parsers/strict_transport_security.rb +42 -0
  40. data/lib/http/security/parsers/x_content_type_options.rb +19 -0
  41. data/lib/http/security/parsers/x_frame_options.rb +47 -0
  42. data/lib/http/security/parsers/x_permitted_cross_domain_policies.rb +33 -0
  43. data/lib/http/security/parsers/x_xss_protection.rb +27 -0
  44. data/lib/http/security/response.rb +323 -0
  45. data/lib/http/security/version.rb +5 -0
  46. data/spec/data/alexa.csv +100 -0
  47. data/spec/headers/cache_control_spec.rb +40 -0
  48. data/spec/headers/content_security_policy_spec.rb +46 -0
  49. data/spec/headers/pragma_spec.rb +26 -0
  50. data/spec/headers/public_key_pins_spec.rb +68 -0
  51. data/spec/headers/set_cookie_spec.rb +122 -0
  52. data/spec/headers/strict_transport_security_spec.rb +39 -0
  53. data/spec/headers/x_content_type_options_spec.rb +26 -0
  54. data/spec/headers/x_frame_options_spec.rb +86 -0
  55. data/spec/headers/x_permitted_cross_domain_policies_spec.rb +108 -0
  56. data/spec/headers/x_xss_protection_spec.rb +59 -0
  57. data/spec/parsers/cache_control_spec.rb +26 -0
  58. data/spec/parsers/content_security_policy_report_only_spec.rb +48 -0
  59. data/spec/parsers/content_security_policy_spec.rb +74 -0
  60. data/spec/parsers/expires_spec.rb +71 -0
  61. data/spec/parsers/parser_spec.rb +317 -0
  62. data/spec/parsers/pragma_spec.rb +10 -0
  63. data/spec/parsers/public_key_pins_spec.rb +81 -0
  64. data/spec/parsers/set_cookie_spec.rb +55 -0
  65. data/spec/parsers/strict_transport_security_spec.rb +62 -0
  66. data/spec/parsers/x_content_type_options_spec.rb +10 -0
  67. data/spec/parsers/x_frame_options_spec.rb +24 -0
  68. data/spec/parsers/x_permitted_cross_domain_policies_spec.rb +34 -0
  69. data/spec/parsers/x_xss_protection_spec.rb +39 -0
  70. data/spec/response_spec.rb +262 -0
  71. data/spec/spec_helper.rb +13 -0
  72. data/tasks/alexa.rb +40 -0
  73. metadata +171 -0
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+ require 'http/security/headers/x_content_type_options'
3
+
4
+ describe HTTP::Security::Headers::XContentTypeOptions do
5
+ subject { described_class.new(nosniff: true) }
6
+
7
+ describe "no_sniff?" do
8
+ context "when nosniff: was true" do
9
+ subject { described_class.new(nosniff: true) }
10
+
11
+ it { expect(subject.no_sniff?).to be true }
12
+ end
13
+
14
+ context "when nosniff: was false" do
15
+ subject { described_class.new(nosniff: false) }
16
+
17
+ it { expect(subject.no_sniff?).to be false }
18
+ end
19
+ end
20
+
21
+ describe "#to_s" do
22
+ it "should return a string" do
23
+ expect(subject.to_s).to be == "nosniff"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+ require 'http/security/headers/x_frame_options'
3
+
4
+ require 'uri'
5
+
6
+ describe HTTP::Security::Headers::XFrameOptions do
7
+ let(:allow_from) { URI('http://example.com/') }
8
+
9
+ subject { described_class.new(allow_from: allow_from) }
10
+
11
+ describe "#initialize" do
12
+ context "when allow_from: is passed" do
13
+ it "should set allow_from" do
14
+ expect(subject.allow_from).to be allow_from
15
+ end
16
+ end
17
+ end
18
+
19
+ describe "deny?" do
20
+ context "when deny: was true" do
21
+ subject { described_class.new(deny: true) }
22
+
23
+ it { expect(subject.deny?).to be true }
24
+ end
25
+
26
+ context "when deny: was false" do
27
+ subject { described_class.new(deny: false) }
28
+
29
+ it { expect(subject.deny?).to be false }
30
+ end
31
+ end
32
+
33
+ describe "same_origin?" do
34
+ context "when sameorigin: was true" do
35
+ subject { described_class.new(sameorigin: true) }
36
+
37
+ it { expect(subject.same_origin?).to be true }
38
+ end
39
+
40
+ context "when sameorigin: was false" do
41
+ subject { described_class.new(sameorigin: false) }
42
+
43
+ it { expect(subject.same_origin?).to be false }
44
+ end
45
+ end
46
+
47
+ describe "allow_all?" do
48
+ context "when allowall: was true" do
49
+ subject { described_class.new(allowall: true) }
50
+
51
+ it { expect(subject.allow_all?).to be true }
52
+ end
53
+
54
+ context "when sameorigin: was false" do
55
+ subject { described_class.new(allowall: false) }
56
+
57
+ it { expect(subject.allow_all?).to be false }
58
+ end
59
+ end
60
+
61
+ describe "#to_s" do
62
+ context "when deny: was true" do
63
+ subject { described_class.new(deny: true) }
64
+
65
+ it { expect(subject.to_s).to be == 'DENY' }
66
+ end
67
+
68
+ context "when same_origin: was true" do
69
+ subject { described_class.new(sameorigin: true) }
70
+
71
+ it { expect(subject.to_s).to be == 'SAMEORIGIN' }
72
+ end
73
+
74
+ context "when allow_from: was specified" do
75
+ subject { described_class.new(allow_from: allow_from) }
76
+
77
+ it { expect(subject.to_s).to be == "ALLOW-FROM #{allow_from}" }
78
+ end
79
+
80
+ context "when allowall: was specified" do
81
+ subject { described_class.new(allowall: allow_from) }
82
+
83
+ it { expect(subject.to_s).to be == 'ALLOWALL' }
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,108 @@
1
+ require 'spec_helper'
2
+ require 'http/security/headers/x_permitted_cross_domain_policies'
3
+
4
+ require 'uri'
5
+
6
+ describe HTTP::Security::Headers::XPermittedCrossDomainPolicies do
7
+ describe "#none?" do
8
+ context "when none: was true" do
9
+ subject { described_class.new(none: true) }
10
+
11
+ it { expect(subject.none?).to be true }
12
+ end
13
+
14
+ context "when none: was false" do
15
+ subject { described_class.new(none: false) }
16
+
17
+ it { expect(subject.none?).to be false }
18
+ end
19
+ end
20
+
21
+ describe "#master_only?" do
22
+ context "when master_only: was true" do
23
+ subject { described_class.new(master_only: true) }
24
+
25
+ it { expect(subject.master_only?).to be true }
26
+ end
27
+
28
+ context "when master_only: was false" do
29
+ subject { described_class.new(master_only: false) }
30
+
31
+ it { expect(subject.master_only?).to be false }
32
+ end
33
+ end
34
+
35
+ describe "#by_content_type?" do
36
+ context "when by_content_type: was true" do
37
+ subject { described_class.new(by_content_type: true) }
38
+
39
+ it { expect(subject.by_content_type?).to be true }
40
+ end
41
+
42
+ context "when by_content_type: was false" do
43
+ subject { described_class.new(by_content_type: false) }
44
+
45
+ it { expect(subject.by_content_type?).to be false }
46
+ end
47
+ end
48
+
49
+ describe "#by_ftp_filename?" do
50
+ context "when by_ftp_filename: was true" do
51
+ subject { described_class.new(by_ftp_filename: true) }
52
+
53
+ it { expect(subject.by_ftp_filename?).to be true }
54
+ end
55
+
56
+ context "when by_ftp_filename: was false" do
57
+ subject { described_class.new(by_ftp_filename: false) }
58
+
59
+ it { expect(subject.by_ftp_filename?).to be false }
60
+ end
61
+ end
62
+
63
+ describe "#all?" do
64
+ context "when all: was true" do
65
+ subject { described_class.new(all: true) }
66
+
67
+ it { expect(subject.all?).to be true }
68
+ end
69
+
70
+ context "when all: was false" do
71
+ subject { described_class.new(all: false) }
72
+
73
+ it { expect(subject.all?).to be false }
74
+ end
75
+ end
76
+
77
+ describe "#to_s" do
78
+ context "when none: was true" do
79
+ subject { described_class.new(none: true) }
80
+
81
+ it { expect(subject.to_s).to be == 'none' }
82
+ end
83
+
84
+ context "when master_only: was true" do
85
+ subject { described_class.new(master_only: true) }
86
+
87
+ it { expect(subject.to_s).to be == 'master-only' }
88
+ end
89
+
90
+ context "when by_content_type: was true" do
91
+ subject { described_class.new(by_content_type: true) }
92
+
93
+ it { expect(subject.to_s).to be == 'by-content-type' }
94
+ end
95
+
96
+ context "when by_ftp_filename: was true" do
97
+ subject { described_class.new(by_ftp_filename: true) }
98
+
99
+ it { expect(subject.to_s).to be == 'by-ftp-filename' }
100
+ end
101
+
102
+ context "when all: was true" do
103
+ subject { described_class.new(all: true) }
104
+
105
+ it { expect(subject.to_s).to be == 'all' }
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+ require 'http/security/headers/x_xss_protection'
3
+
4
+ describe HTTP::Security::Headers::XXSSProtection do
5
+ let(:mode) { 'block' }
6
+ let(:report_uri) do
7
+ "/xss-report/25b8988e-64ff-45a8-b0c6-2700fc1e9abd?source%5Baction%5D=index&source%5Bcontroller%5D=shop&source%5Bsection%5D=storefront"
8
+ end
9
+
10
+ subject do
11
+ described_class.new(
12
+ enabled: true,
13
+ mode: mode,
14
+ report: report_uri
15
+ )
16
+ end
17
+
18
+ describe "#initialize" do
19
+ it "should set mode" do
20
+ expect(subject.mode).to be mode
21
+ end
22
+
23
+ it "should set report" do
24
+ expect(subject.report).to be report_uri
25
+ end
26
+ end
27
+
28
+ describe "#enabled?" do
29
+ context "when enabled: was true" do
30
+ subject { described_class.new(enabled: true) }
31
+
32
+ it { expect(subject.enabled?).to be true }
33
+ end
34
+
35
+ context "when enabled: was false" do
36
+ subject { described_class.new(enabled: false) }
37
+
38
+ it { expect(subject.enabled?).to be false }
39
+ end
40
+ end
41
+
42
+ describe "#to_s" do
43
+ it "should return a string" do
44
+ expect(subject.to_s).to be == "1; mode=#{mode}; report=#{report_uri}"
45
+ end
46
+
47
+ context "when enabled: was true" do
48
+ subject { described_class.new(enabled: true) }
49
+
50
+ it { expect(subject.to_s).to be == '1' }
51
+ end
52
+
53
+ context "when enabled: was false" do
54
+ subject { described_class.new(enabled: false) }
55
+
56
+ it { expect(subject.to_s).to be == '0' }
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,26 @@
1
+ require "spec_helper"
2
+ require "http/security/parsers/cache_control"
3
+
4
+ describe Parsers::CacheControl do
5
+ it "accepts private" do
6
+ header = 'private'
7
+
8
+ expect(subject.parse(header)).to be == {private: true}
9
+ end
10
+
11
+ it "accepts public, max-age=1" do
12
+ header = "public, max-age=1"
13
+
14
+ expect(subject.parse(header)).to be == {public: true, max_age: 1}
15
+ end
16
+
17
+ it "accepts all recommended value: private, max-age=0, no-cache" do
18
+ header = "private, max-age=0, no-cache"
19
+
20
+ expect(subject.parse(header)).to be == {
21
+ private: true,
22
+ max_age: 0,
23
+ no_cache: true
24
+ }
25
+ end
26
+ end
@@ -0,0 +1,48 @@
1
+ require "spec_helper"
2
+ require "http/security/parsers/content_security_policy_report_only"
3
+
4
+ describe Parsers::ContentSecurityPolicyReportOnly do
5
+ it "accepts default-src 'self'" do
6
+ header = "default-src 'self';"
7
+
8
+ expect(subject.parse(header)).to be == {
9
+ default_src: "'self'"
10
+ }
11
+ end
12
+
13
+ it "accepts default-src 'self'; script-src 'self';" do
14
+ header = "default-src 'self'; script-src 'self';"
15
+
16
+ expect(subject.parse(header)).to be == {
17
+ default_src: "'self'",
18
+ script_src: "'self'"
19
+ }
20
+ end
21
+
22
+ it "accepts a domain" do
23
+ header = "default-src 'self' trustedscripts.foo.com"
24
+
25
+ expect(subject.parse(header)).to be == {
26
+ default_src: "'self' trustedscripts.foo.com"
27
+ }
28
+ end
29
+
30
+ it "accepts img-src and media-src" do
31
+ header = "default-src 'self'; img-src 'self' data:; media-src mediastream:"
32
+
33
+ expect(subject.parse(header)).to be == {
34
+ default_src: "'self'",
35
+ img_src: "'self' data:",
36
+ media_src: "mediastream:"
37
+ }
38
+ end
39
+
40
+ it "accepts report URLs" do
41
+ header = "default-src 'self'; report-uri http://www.example1.com http://www.example2.com"
42
+
43
+ expect(subject.parse(header)).to be == {
44
+ default_src: "'self'",
45
+ report_uri: [ URI("http://www.example1.com"), URI("http://www.example2.com") ]
46
+ }
47
+ end
48
+ end
@@ -0,0 +1,74 @@
1
+ require "spec_helper"
2
+ require "http/security/parsers/content_security_policy"
3
+
4
+ describe Parsers::ContentSecurityPolicy do
5
+ it "accepts default-src 'self'" do
6
+ header = "default-src 'self';"
7
+
8
+ expect(subject.parse(header)).to be == {
9
+ default_src: "'self'"
10
+ }
11
+ end
12
+
13
+ it "accepts default-src 'self'; script-src 'self';" do
14
+ header = "default-src 'self'; script-src 'self';"
15
+
16
+ expect(subject.parse(header)).to be == {
17
+ default_src: "'self'",
18
+ script_src: "'self'"
19
+ }
20
+ end
21
+
22
+ it "accepts a domain" do
23
+ header = "default-src 'self' trustedscripts.foo.com"
24
+
25
+ expect(subject.parse(header)).to be == {
26
+ default_src: "'self' trustedscripts.foo.com"
27
+ }
28
+ end
29
+
30
+ it "accepts img-src and media-src" do
31
+ header = "default-src 'self'; img-src 'self' data:; media-src mediastream:"
32
+
33
+ expect(subject.parse(header)).to be == {
34
+ default_src: "'self'",
35
+ img_src: "'self' data:",
36
+ media_src: "mediastream:"
37
+ }
38
+ end
39
+
40
+ it "accepts wildcard domains" do
41
+ header = "default-src 'self'; img-src *; object-src media1.example.com media2.example.com *.cdn.example.com; script-src trustedscripts.example.com"
42
+
43
+ expect(subject.parse(header)).to be == {
44
+ default_src: "'self'",
45
+ img_src: "*",
46
+ object_src: "media1.example.com media2.example.com *.cdn.example.com",
47
+ script_src: "trustedscripts.example.com"
48
+ }
49
+ end
50
+
51
+ it "parses unsafe-inline and unsafe-eval" do
52
+ header = "default-src https: 'unsafe-inline' 'unsafe-eval'"
53
+ response = subject.parse(header)
54
+
55
+ expect(subject.parse(header)).to be == {
56
+ default_src: "https: 'unsafe-inline' 'unsafe-eval'"
57
+ }
58
+ end
59
+
60
+ it "parses sandbox" do
61
+ header = "sandbox token1 token2 token3"
62
+ response = subject.parse(header)
63
+
64
+ expect(subject.parse(header)).to be == {
65
+ sandbox: "token1 token2 token3"
66
+ }
67
+ end
68
+
69
+ describe "specific URI paths" do
70
+ header = "default-src default-src 'self'; script-src https://example.com/js/"
71
+ it "is not inlcuded in CSP 1.0. Enable ext_host_source to parse specific paths."
72
+ end
73
+
74
+ end
@@ -0,0 +1,71 @@
1
+ require "spec_helper"
2
+ require "http/security/parsers/expires"
3
+
4
+ describe Parsers::Expires do
5
+ it "parses negative integers" do
6
+ header = "-1"
7
+
8
+ expect(subject.parse(header)).to eq(-1)
9
+ end
10
+
11
+ it "parses 0" do
12
+ header = "0"
13
+
14
+ expect(subject.parse(header)).to eq(0)
15
+ end
16
+
17
+ it "parses positive integers" do
18
+ header = "100"
19
+
20
+ expect(subject.parse(header)).to eq(100)
21
+ end
22
+
23
+ it "parses rfc1123-date" do
24
+ header = "Thu, 04 Dec 2015 16:00:00 GMT"
25
+ date = Date.parse(header)
26
+
27
+ expect(subject.parse(header)).to be == Date.parse(header)
28
+ end
29
+
30
+ it "parses rfc850-date" do
31
+ header = "Thursday, 04-Dec-15 16:00:00 GMT"
32
+
33
+ expect(subject.parse(header)).to be == Date.parse(header)
34
+ end
35
+
36
+ it "parses asctime-date format #1" do
37
+ header = "Thu Dec 04 16:00:00 2015"
38
+
39
+ expect(subject.parse(header)).to be == Date.parse(header)
40
+ end
41
+
42
+ it "parses asctime-date format #2" do
43
+ header = "Thu Dec 4 16:00:00 2015"
44
+
45
+ expect(subject.parse(header)).to be == Date.parse(header)
46
+ end
47
+
48
+ it "parses rfc1123-date" do
49
+ header = "Thu, 04 Dec 2015 16:00:00 GMT"
50
+
51
+ expect(subject.parse(header)).to be == Date.parse(header)
52
+ end
53
+
54
+ it "parses rfc850-date" do
55
+ header = "Thursday, 04-Dec-15 16:00:00 GMT"
56
+
57
+ expect(subject.parse(header)).to be == Date.parse(header)
58
+ end
59
+
60
+ it "parses asctime-date format #1" do
61
+ header = "Thu Dec 04 16:00:00 2015"
62
+
63
+ expect(subject.parse(header)).to be == Date.parse(header)
64
+ end
65
+
66
+ it "parses asctime-date format #2" do
67
+ header = "Thu Dec 4 16:00:00 2015"
68
+
69
+ expect(subject.parse(header)).to be == Date.parse(header)
70
+ end
71
+ end