diff --git a/Gemfile b/Gemfile index 567d4c09..d6e4e4ae 100644 --- a/Gemfile +++ b/Gemfile @@ -11,6 +11,7 @@ group :app do gem 'rack' gem 'sinatra' gem 'sinatra-contrib' + gem 'rack-ssl-enforcer' gem 'thin' gem 'sprockets' gem 'sprockets-helpers' diff --git a/Gemfile.lock b/Gemfile.lock index eb4a3210..0e177c4d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -63,6 +63,7 @@ GEM rack (2.0.5) rack-protection (2.0.4) rack + rack-ssl-enforcer (0.2.9) rack-test (1.1.0) rack (>= 1.0, < 3) rake (12.3.1) @@ -140,6 +141,7 @@ DEPENDENCIES progress_bar pry (~> 0.11.0) rack + rack-ssl-enforcer rack-test rake rr diff --git a/assets/javascripts/lib/page.coffee b/assets/javascripts/lib/page.coffee index 8df7422e..5d3f6c88 100644 --- a/assets/javascripts/lib/page.coffee +++ b/assets/javascripts/lib/page.coffee @@ -190,7 +190,7 @@ isSameOrigin = (url) -> updateCanonicalLink = -> @canonicalLink ||= document.head.querySelector('link[rel="canonical"]') - @canonicalLink.setAttribute('href', "http://#{location.host}#{location.pathname}") + @canonicalLink.setAttribute('href', "https://#{location.host}#{location.pathname}") trackers = [] diff --git a/assets/javascripts/templates/pages/root_tmpl.coffee.erb b/assets/javascripts/templates/pages/root_tmpl.coffee.erb index ef5ee8e6..b5369403 100644 --- a/assets/javascripts/templates/pages/root_tmpl.coffee.erb +++ b/assets/javascripts/templates/pages/root_tmpl.coffee.erb @@ -72,14 +72,3 @@ app.templates.androidWarning = """

To install DevDocs on your phone, visit devdocs.io in Chrome and select "Add to home screen" in the menu. """ - -app.templates.httpWarning = """ -

-

Hi there!

-

DevDocs is migrating to HTTPS. -

Please update your bookmarks to point to https://devdocs.io. -

When using the HTTPS version, your preferences will carry over automatically, but your offline data will be reset. Simply re-download documentation in the Offline area, and you'll be all set to use DevDocs securely offline. -

Sorry for the inconvenience. This migration is needed because browsers are removing support for certain DOM APIs that power DevDocs's offline mode over non-secure origins. -

Thanks for using DevDocs, and happy coding! -

-""" diff --git a/assets/javascripts/views/content/root_page.coffee b/assets/javascripts/views/content/root_page.coffee index e442887a..b48a1df3 100644 --- a/assets/javascripts/views/content/root_page.coffee +++ b/assets/javascripts/views/content/root_page.coffee @@ -19,10 +19,6 @@ class app.views.RootPage extends app.View else 'intro' - # temporary - if location.host is 'devdocs.io' and location.protocol is 'http:' - tmpl = 'httpWarning' - @append @tmpl(tmpl) return diff --git a/lib/app.rb b/lib/app.rb index b5015b3a..f8ef0454 100644 --- a/lib/app.rb +++ b/lib/app.rb @@ -12,6 +12,8 @@ class App < Sinatra::Application Rack::Mime::MIME_TYPES['.webapp'] = 'application/x-web-app-manifest+json' configure do + use Rack::SslEnforcer, only_environments: ['production', 'test'], hsts: false, force_secure_cookies: false + set :sentry_dsn, ENV['SENTRY_DSN'] set :protection, except: [:frame_options, :xss_header] diff --git a/test/app_test.rb b/test/app_test.rb index 5401e3ae..4e836a19 100644 --- a/test/app_test.rb +++ b/test/app_test.rb @@ -11,6 +11,16 @@ class AppTest < MiniTest::Spec App end + before do + current_session.env('HTTPS', 'on') + end + + it 'redirects to HTTPS' do + get 'http://example.com/test?q=1', {}, 'HTTPS' => 'off' + assert last_response.redirect? + assert_equal 'https://example.com/test?q=1', last_response['Location'] + end + describe "/" do it "works" do get '/' @@ -20,13 +30,13 @@ class AppTest < MiniTest::Spec it "redirects to /#q= when there is a 'q' query param" do get '/search', q: 'foo' assert last_response.redirect? - assert_equal 'http://example.org/#q=foo', last_response['Location'] + assert_equal 'https://example.org/#q=foo', last_response['Location'] end it "redirects without the query string" do get '/', foo: 'bar' assert last_response.redirect? - assert_equal 'http://example.org/', last_response['Location'] + assert_equal 'https://example.org/', last_response['Location'] end it "sets default size" do @@ -52,7 +62,7 @@ class AppTest < MiniTest::Spec %w(offline about news help).each do |page| get "/#{page}", {}, 'HTTP_USER_AGENT' => MODERN_BROWSER assert last_response.redirect? - assert_equal "http://example.org/#/#{page}", last_response['Location'] + assert_equal "https://example.org/#/#{page}", last_response['Location'] end end @@ -61,7 +71,7 @@ class AppTest < MiniTest::Spec set_cookie('foo=bar') get "/#{page}", {}, 'HTTP_USER_AGENT' => MODERN_BROWSER assert last_response.redirect? - assert_equal 'http://example.org/', last_response['Location'] + assert_equal 'https://example.org/', last_response['Location'] assert last_response['Set-Cookie'].start_with?("initial_path=%2F#{page}; path=/; expires=") end end @@ -71,11 +81,11 @@ class AppTest < MiniTest::Spec it "redirects to /#q=" do get '/search' assert last_response.redirect? - assert_equal 'http://example.org/#q=', last_response['Location'] + assert_equal 'https://example.org/#q=', last_response['Location'] get '/search', q: 'foo' assert last_response.redirect? - assert_equal 'http://example.org/#q=foo', last_response['Location'] + assert_equal 'https://example.org/#q=foo', last_response['Location'] end end @@ -148,7 +158,7 @@ class AppTest < MiniTest::Spec set_cookie('docs=html~5') get '/html~5/', {}, 'HTTP_USER_AGENT' => MODERN_BROWSER assert last_response.redirect? - assert_equal 'http://example.org/', last_response['Location'] + assert_equal 'https://example.org/', last_response['Location'] assert last_response['Set-Cookie'].start_with?("initial_path=%2Fhtml%7E5%2F; path=/; expires=") end @@ -161,13 +171,13 @@ class AppTest < MiniTest::Spec set_cookie('docs=html~5') get '/html/', {}, 'HTTP_USER_AGENT' => MODERN_BROWSER assert last_response.redirect? - assert_equal 'http://example.org/', last_response['Location'] + assert_equal 'https://example.org/', last_response['Location'] assert last_response['Set-Cookie'].start_with?("initial_path=%2Fhtml%2F; path=/; expires=") end it "renders when the doc exists and is enabled, and the request is from Googlebot" do set_cookie('docs=html') - get '/html/', {}, 'HTTP_USER_AGENT' => 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)' + get '/html/', {}, 'HTTP_USER_AGENT' => 'Mozilla/5.0 (compatible; Googlebot/2.1; +https://www.google.com/bot.html)' assert last_response.ok? end @@ -187,17 +197,17 @@ class AppTest < MiniTest::Spec it "redirects with trailing slash" do get '/html' assert last_response.redirect? - assert_equal 'http://example.org/html/', last_response['Location'] + assert_equal 'https://example.org/html/', last_response['Location'] get '/html', bar: 'baz' assert last_response.redirect? - assert_equal 'http://example.org/html/?bar=baz', last_response['Location'] + assert_equal 'https://example.org/html/?bar=baz', last_response['Location'] end it "redirects old docs" do get '/iojs/' assert last_response.redirect? - assert_equal 'http://example.org/node/', last_response['Location'] + assert_equal 'https://example.org/node/', last_response['Location'] end end @@ -232,17 +242,17 @@ class AppTest < MiniTest::Spec it "redirects with trailing slash" do get '/css-foo' assert last_response.redirect? - assert_equal 'http://example.org/css-foo/', last_response['Location'] + assert_equal 'https://example.org/css-foo/', last_response['Location'] get '/css-foo', bar: 'baz' assert last_response.redirect? - assert_equal 'http://example.org/css-foo/?bar=baz', last_response['Location'] + assert_equal 'https://example.org/css-foo/?bar=baz', last_response['Location'] end it "redirects old docs" do get '/yii1-foo/' assert last_response.redirect? - assert_equal 'http://example.org/yii~1.1-foo/', last_response['Location'] + assert_equal 'https://example.org/yii~1.1-foo/', last_response['Location'] end end @@ -263,17 +273,17 @@ class AppTest < MiniTest::Spec it "redirects without trailing slash" do get '/css/foo/' assert last_response.redirect? - assert_equal 'http://example.org/css/foo', last_response['Location'] + assert_equal 'https://example.org/css/foo', last_response['Location'] get '/css/foo/', bar: 'baz' assert last_response.redirect? - assert_equal 'http://example.org/css/foo?bar=baz', last_response['Location'] + assert_equal 'https://example.org/css/foo?bar=baz', last_response['Location'] end it "redirects old docs" do get '/python2/foo' assert last_response.redirect? - assert_equal 'http://example.org/python~2.7/foo', last_response['Location'] + assert_equal 'https://example.org/python~2.7/foo', last_response['Location'] end end @@ -281,7 +291,7 @@ class AppTest < MiniTest::Spec it "returns to the asset path" do get '/docs.json' assert last_response.redirect? - assert_equal 'http://example.org/assets/docs.json', last_response['Location'] + assert_equal 'https://example.org/assets/docs.json', last_response['Location'] end end @@ -289,7 +299,7 @@ class AppTest < MiniTest::Spec it "returns to the asset path" do get '/application.js' assert last_response.redirect? - assert_equal 'http://example.org/assets/application.js', last_response['Location'] + assert_equal 'https://example.org/assets/application.js', last_response['Location'] end end @@ -297,7 +307,7 @@ class AppTest < MiniTest::Spec it "returns to the asset path" do get '/application.css' assert last_response.redirect? - assert_equal 'http://example.org/assets/application.css', last_response['Location'] + assert_equal 'https://example.org/assets/application.css', last_response['Location'] end end