http-security 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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