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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 097f834633f8dfec6c6946ed5a1d6df6eb099dcf
4
+ data.tar.gz: 593a835d602354c2d1f815c211b743c53038e370
5
+ SHA512:
6
+ metadata.gz: 5b2832989c451f162a1b0e5d41c9bca6be583f28a9d0f69e363ceed62e11b35f034d9a15db62ce35735a0209f6a3a48ccabb9ffdd3e781ba16094a7d7a831b7c
7
+ data.tar.gz: f70ba9dd6f347fc95c635ade11b081dd180a2bdd18a648ea20e80ba939d70a1fec4963927d9ab6786c3d6ae694dd45181c706e82d91da64f4cc7c5f0cc790ba3
@@ -0,0 +1,19 @@
1
+ Gemfile.lock
2
+
3
+ *.rbc
4
+ .bundle
5
+ .config
6
+ coverage
7
+ InstalledFiles
8
+ lib/bundler/man
9
+ pkg
10
+ rdoc
11
+ spec/reports
12
+ test/tmp
13
+ test/version_tmp
14
+ tmp
15
+
16
+ # YARD artifacts
17
+ .yardoc
18
+ _yardoc
19
+ doc/
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour --format documentation
@@ -0,0 +1,21 @@
1
+ language: ruby
2
+ sudo: false
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0
6
+ - 2.1
7
+ - ruby-head
8
+ - jruby-19mode
9
+ - jruby-head
10
+ - rbx-2
11
+ matrix:
12
+ allow_failures:
13
+ - rvm: rbx-2
14
+ - rvm: jruby-19mode
15
+ - rvm: jruby-head
16
+ addons:
17
+ code_climate:
18
+ repo_token: 4e8013e9105e3dd8153da54825ce8fab58c7408dce36fa351ef35c51bb9b0b05
19
+ notifications:
20
+ slack:
21
+ secure: awgcYPnPALqyAn3H58d+qzQYezzEyuqr71GmU9AsXuOa3sGM1JVqmUOqgoXeykBcV8EV5DEO+65fCkc+GnBxyPjnyYyvzW7VEcj3xQdxyVgn9fK0a76Mp+ywoWOPG7t4Z8dIK3PCT12QO+XgGWiLA9YG10eRtGRRD1V2PCKv41Y=
@@ -0,0 +1 @@
1
+ --markup markdown --title "Security Headers Documentation" --protected
@@ -0,0 +1,17 @@
1
+ ### 0.1.0 / 2015-07-09
2
+
3
+ * Initial release:
4
+ * `Cache-Control`
5
+ * `Content-Security-Policy`
6
+ * `Content-Security-Policy-Report-Only`
7
+ * `Expires`
8
+ * `Pragma`
9
+ * `Public-Key-Pins`
10
+ * `Public-Key-Pins-Report-Only`
11
+ * `Set-Cookie`
12
+ * `Strict-Transport-Security`
13
+ * `X-Content-Type-Options`
14
+ * `X-Frame-Options`
15
+ * `X-Permitted-Cross-Domain-Policies`
16
+ * `X-XSS-Protection
17
+
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'nokogiri'
7
+
8
+ gem 'rake'
9
+ gem 'rubygems-tasks', '~> 0.2'
10
+
11
+ gem 'rspec', '~> 3.0'
12
+
13
+ gem 'kramdown'
14
+ gem 'yard', '~> 0.8'
15
+ end
16
+
17
+ gem "codeclimate-test-reporter", group: :test, require: nil
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Trail of Bits
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,90 @@
1
+ # HTTP Security
2
+
3
+ * [Source](https://github.com/trailofbits/http-security)
4
+ * [Issues](https://github.com/trailofbits/http-security/issues)
5
+ * [Documentation](https://rubydoc.info/gems/http-security/frames)
6
+
7
+ [![Code Climate](https://codeclimate.com/github/trailofbits/http-security.png)](https://codeclimate.com/github/trailofbits/http-security) [![Build Status](https://travis-ci.org/trailofbits/http-security.svg)](https://travis-ci.org/trailofbits/http-security) [![Test Coverage](https://codeclimate.com/github/trailofbits/http-security/badges/coverage.svg)](https://codeclimate.com/github/trailofbits/http-security)
8
+
9
+ Security Headers is a parser for security-relevant HTTP headers. Each header
10
+ value is parsed and validated according to the syntax specified in its relevant
11
+ RFC.
12
+
13
+ Security Headers relies on [parslet] for constructing its parsing grammar.
14
+
15
+ Currently parsed security headers are:
16
+
17
+ * `Cache-Control`
18
+ * `Content-Security-Policy`
19
+ * `Content-Security-Policy-Report-Only`
20
+ * `Expires`
21
+ * `Pragma`
22
+ * `Public-Key-Pins`
23
+ * `Public-Key-Pins-Report-Only`
24
+ * `Set-Cookie`
25
+ * `Strict-Transport-Security`
26
+ * `X-Content-Type-Options`
27
+ * `X-Frame-Options`
28
+ * `X-Permitted-Cross-Domain-Policies`
29
+ * `X-XSS-Protection
30
+
31
+ ## Example
32
+
33
+ require 'net/https'
34
+ response = Net::HTTP.get_response(URI('https://twitter.com/'))
35
+
36
+ require 'http/security'
37
+ headers = HTTP::Security::Response.parse(response)
38
+
39
+ headers.cache_control
40
+ # => #<HTTP::Security::Headers::CacheControl:0x00000002f65778 @private=nil, @max_age=nil, @no_cache=true>
41
+
42
+ headers.content_security_policy
43
+ # => #<HTTP::Security::Headers::ContentSecurityPolicy:0x00000002d8e238 @default_src="https:"@12, @script_src="'unsafe-inline' 'unsafe-eval' https:"@172, @object_src="https:"@153, @style_src="'unsafe-inline' https:"@220, @img_src="https: blob: data:"@98, @media_src="https: blob:"@128, @frame_src="https: twitter:"@73, @font_src="https: data:"@49, @connect_src="https:"@32, @report_uri=[#<URI::HTTPS:0x00000002d94250 URL:https://twitter.com/i/csp_report?a=NVQWGYLXFVZXO2LGOQ%3D%3D%3D%3D%3D%3D&ro=false;>], @sandbox=nil>
44
+
45
+ headers.expires
46
+ # => #<HTTP::Security::HTTPDate: Tue, 31 Mar 1981 00:00:00 GMT ((2444695j,0s,0n),+0s,2299161j)>
47
+
48
+ headers.pragma
49
+ # => #<HTTP::Security::Headers::Pragma:0x00000002ccc5e8 @no_cache=true>
50
+
51
+ headers.strict_transport_security
52
+ # => #<HTTP::Security::Headers::StrictTransportSecurity:0x00000002c928c0 @max_age=631138519, @include_sub_domains=nil>
53
+
54
+ headers.x_content_type_options
55
+ # => #<HTTP::Security::Headers::XContentTypeOptions:0x00000002a46e40 @no_sniff=true>
56
+
57
+ headers.x_frame_options
58
+ # => #<HTTP::Security::Headers::XFrameOptions:0x000000028163c8 @deny=nil, @same_origin=true, @allow_from=nil, @allow_all=nil>
59
+
60
+ headers.x_permitted_cross_domain_policies
61
+ # => nil
62
+
63
+ headers.x_xss_protection
64
+ # => #<HTTP::Security::Headers::XXSSProtection:0x0000000297a408 @enabled=true, @mode="block"@8, @report=nil>
65
+
66
+ ## Requirements
67
+
68
+ * [ruby] >= 1.9.1
69
+ * [parslet] ~> 1.5
70
+
71
+ ## Install
72
+
73
+ $ gem install http-security
74
+
75
+ ## Testing
76
+
77
+ To run the RSpec tests:
78
+
79
+ $ rake spec
80
+
81
+ To test the parser against the Alexa Top 100:
82
+
83
+ $ rake spec:gauntlet
84
+
85
+ ## License
86
+
87
+ See the {file:LICENSE.txt} file.
88
+
89
+ [ruby]: https://www.ruby-lang.org/
90
+ [parslet]: http://kschiess.github.io/parslet/
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+
5
+ begin
6
+ require 'bundler/setup'
7
+ rescue LoadError => e
8
+ warn e.message
9
+ warn "Run `gem install bundler` to install Bundler."
10
+ exit -1
11
+ end
12
+
13
+ require 'rake'
14
+ require 'rubygems/tasks'
15
+ Gem::Tasks.new
16
+
17
+ require 'rspec/core/rake_task'
18
+ RSpec::Core::RakeTask.new
19
+
20
+ namespace :spec do
21
+ desc "Tests SecurityHeaders::Parser against Alexa Top 500"
22
+ RSpec::Core::RakeTask.new(:gauntlet) do |t|
23
+ t.rspec_opts = '--tag gauntlet'
24
+ end
25
+ end
26
+
27
+ task :test => :spec
28
+ task :default => :spec
29
+
30
+ require 'yard'
31
+ YARD::Rake::YardocTask.new
32
+ task :doc => :yard
33
+
34
+ require_relative 'tasks/alexa'
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'http/security/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "http-security"
8
+ gem.version = HTTP::Security::VERSION
9
+ gem.authors = ["Dominic Owen", "Hal Brodigan"]
10
+ gem.email = ["dwowen20@gmail.com", "hal@trailofbits.com"]
11
+ gem.summary = %q{HTTP Security Header Parser}
12
+ gem.description = %q{HTTP Security Header Parser}
13
+ gem.homepage = "https://github.com/trailofbits/http-security#readme"
14
+ gem.license = "MIT"
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+ gem.required_ruby_version = '>= 1.9.1'
20
+
21
+ gem.add_dependency 'parslet', '~> 1.5'
22
+ gem.add_development_dependency "bundler", "~> 1.0"
23
+ end
@@ -0,0 +1,2 @@
1
+ require 'http/security/version'
2
+ require 'http/security/response'
@@ -0,0 +1,8 @@
1
+ require 'parslet'
2
+
3
+ module HTTP
4
+ module Security
5
+ class InvalidHeader < Parslet::ParseFailed
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,12 @@
1
+ require 'http/security/headers/cache_control'
2
+ require 'http/security/headers/content_security_policy'
3
+ require 'http/security/headers/content_security_policy_report_only'
4
+ require 'http/security/headers/pragma'
5
+ require 'http/security/headers/public_key_pins'
6
+ require 'http/security/headers/public_key_pins_report_only'
7
+ require 'http/security/headers/set_cookie'
8
+ require 'http/security/headers/strict_transport_security'
9
+ require 'http/security/headers/x_content_type_options'
10
+ require 'http/security/headers/x_frame_options'
11
+ require 'http/security/headers/x_permitted_cross_domain_policies'
12
+ require 'http/security/headers/x_xss_protection'
@@ -0,0 +1,36 @@
1
+ module HTTP
2
+ module Security
3
+ module Headers
4
+ class CacheControl
5
+
6
+ attr_reader :max_age
7
+
8
+ def initialize(options={})
9
+ @private = options[:private]
10
+ @max_age = options[:max_age]
11
+ @no_cache = options[:no_cache]
12
+ end
13
+
14
+ def private?
15
+ !!@private
16
+ end
17
+
18
+ def no_cache?
19
+ !!@no_cache
20
+ end
21
+
22
+ def to_s
23
+ directives = []
24
+
25
+
26
+ directives << "private" if @private
27
+ directives << "max-age=#{@max_age}" if @max_age
28
+ directives << "no-cache" if @no_cache
29
+
30
+ return directives.join(', ')
31
+ end
32
+
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,71 @@
1
+ module HTTP
2
+ module Security
3
+ module Headers
4
+ class ContentSecurityPolicy
5
+
6
+ attr_reader :default_src
7
+
8
+ attr_reader :script_src
9
+
10
+ attr_reader :object_src
11
+
12
+ attr_reader :style_src
13
+
14
+ attr_reader :img_src
15
+
16
+ attr_reader :media_src
17
+
18
+ attr_reader :frame_src
19
+
20
+ attr_reader :font_src
21
+
22
+ attr_reader :connect_src
23
+
24
+ # @return [Array<URI>]
25
+ attr_reader :report_uri
26
+
27
+ attr_reader :sandbox
28
+
29
+ def initialize(directives={})
30
+ @default_src = directives[:default_src]
31
+ @script_src = directives[:script_src]
32
+ @object_src = directives[:object_src]
33
+ @style_src = directives[:style_src]
34
+ @img_src = directives[:img_src]
35
+ @media_src = directives[:media_src]
36
+ @frame_src = directives[:frame_src]
37
+ @font_src = directives[:font_src]
38
+ @connect_src = directives[:connect_src]
39
+
40
+ @report_uri = Array(directives[:report_uri])
41
+ @sandbox = directives[:sandbox]
42
+ end
43
+
44
+ def to_s
45
+ directives = []
46
+
47
+ directives << "default-src #{@default_src}" if @default_src
48
+ directives << "script-src #{@script_src}" if @script_src
49
+ directives << "object-src #{@object_src}" if @object_src
50
+ directives << "style-src #{@style_src}" if @style_src
51
+ directives << "img-src #{@img_src}" if @img_src
52
+ directives << "media-src #{@media_src}" if @media_src
53
+ directives << "frame-src #{@frame_src}" if @frame_src
54
+ directives << "font-src #{@font_src}" if @font_src
55
+ directives << "connect-src #{@connect_src}" if @connect_src
56
+
57
+ if @sandbox
58
+ directives << "sandbox #{@sandbox}"
59
+ end
60
+
61
+ unless @report_uri.empty?
62
+ directives << "report-uri #{@report_uri.join(' ')}"
63
+ end
64
+
65
+ return directives.join('; ')
66
+ end
67
+
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,10 @@
1
+ require 'http/security/headers/content_security_policy'
2
+
3
+ module HTTP
4
+ module Security
5
+ module Headers
6
+ class ContentSecurityPolicyReportOnly < ContentSecurityPolicy
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,24 @@
1
+ module HTTP
2
+ module Security
3
+ module Headers
4
+ class Pragma
5
+
6
+ def initialize(directives={})
7
+ @no_cache = directives[:no_cache]
8
+ end
9
+
10
+ def no_cache?
11
+ !!@no_cache
12
+ end
13
+
14
+ def to_s
15
+ str = ''
16
+ str << 'no-cache' if @no_cache
17
+
18
+ return str
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,60 @@
1
+ module HTTP
2
+ module Security
3
+ module Headers
4
+ class PublicKeyPins
5
+
6
+ # @return [Hash{Symbol,String => Array<String>}]
7
+ attr_reader :pin
8
+
9
+ # @return [Integer]
10
+ attr_reader :max_age
11
+
12
+ # @return [URI::HTTP]
13
+ attr_reader :report_uri
14
+
15
+ def initialize(options={})
16
+ @pin = {}
17
+
18
+ options.each do |key,value|
19
+ if (key.kind_of?(Symbol) && key =~ /^pin_/)
20
+ @pin[key[4..-1].to_sym] = Array(value)
21
+ elsif (key.kind_of?(String) && key.start_with?('pin-'))
22
+ @pin[key[4..-1]] = Array(value)
23
+ end
24
+ end
25
+
26
+ @max_age = options[:max_age]
27
+ @include_sub_domains = options[:includesubdomains]
28
+ @report_uri = options[:report_uri]
29
+ @strict = options[:strict]
30
+ end
31
+
32
+ def include_sub_domains?
33
+ !!@include_sub_domains
34
+ end
35
+
36
+ def strict?
37
+ !!@strict
38
+ end
39
+
40
+ def to_s
41
+ directives = []
42
+
43
+ @pin.each do |algorithm,fingerprints|
44
+ Array(fingerprints).each do |fingerprint|
45
+ directives << "pin-#{algorithm}=#{fingerprint.dump}"
46
+ end
47
+ end
48
+
49
+ directives << "max-age=#{@max_age}" if @max_age
50
+ directives << "includeSubdomains" if @include_sub_domains
51
+ directives << "report-uri=\"#{@report_uri}\"" if @report_uri
52
+ directives << "strict" if @strict
53
+
54
+ return directives.join('; ')
55
+ end
56
+
57
+ end
58
+ end
59
+ end
60
+ end