tynn 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/examples/protection.ru +18 -0
- data/lib/tynn.rb +2 -2
- data/lib/tynn/csrf.rb +48 -0
- data/lib/tynn/environment.rb +18 -1
- data/lib/tynn/json.rb +1 -1
- data/lib/tynn/json_parser.rb +3 -1
- data/lib/tynn/matchers.rb +40 -0
- data/lib/tynn/not_found.rb +18 -0
- data/lib/tynn/protection.rb +13 -0
- data/lib/tynn/secure_headers.rb +7 -0
- data/lib/tynn/session.rb +3 -0
- data/lib/tynn/ssl.rb +71 -0
- data/lib/tynn/version.rb +1 -1
- data/makefile +4 -1
- data/test/{core.rb → core_test.rb} +0 -0
- data/test/csrf_test.rb +98 -0
- data/test/{environment.rb → environment_test.rb} +0 -0
- data/test/{erubis.rb → erubis_test.rb} +0 -0
- data/test/{hmote.rb → hmote_test.rb} +0 -0
- data/test/{json_parser.rb → json_parser_test.rb} +0 -0
- data/test/{json.rb → json_test.rb} +0 -0
- data/test/matchers_test.rb +37 -0
- data/test/not_found_test.rb +19 -0
- data/test/protection_test.rb +29 -0
- data/test/{render.rb → render_test.rb} +0 -0
- data/test/{secure_headers.rb → secure_headers_test.rb} +0 -0
- data/test/{send_file.rb → send_file_test.rb} +0 -0
- data/test/{session.rb → session_test.rb} +5 -2
- data/test/ssl_test.rb +82 -0
- data/test/{static.rb → static_test.rb} +1 -1
- metadata +25 -17
- data/lib/tynn/default_matcher.rb +0 -7
- data/lib/tynn/hsts.rb +0 -23
- data/test/default_matcher.rb +0 -17
- data/test/hsts.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0c2e697419542ac01be6106e60a3bd2a6221d3b
|
4
|
+
data.tar.gz: 917cac7cbbd5a4123c1f70ca431dd818aa17efcc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70b7c35be68be33da92b5b95808b4e241081912c588b0d4e29a2f07a87413e5b5907f3adbe02ebc1aabee06d0d61071768cf471f05bb2e59e8f4fe132fd2a45a
|
7
|
+
data.tar.gz: 765f7512ccae32a7b6e92db62f2acc1a1b6370bcd05d0e14c6d1dd6da2cbe765dc9808baa269b1067e564b1beed0496082e6eb1859478455657303f89b15e7c6
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
doc/
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative "../lib/tynn"
|
2
|
+
require_relative "../lib/tynn/environment"
|
3
|
+
require_relative "../lib/tynn/protection"
|
4
|
+
require_relative "../lib/tynn/session"
|
5
|
+
|
6
|
+
Tynn.helpers(Tynn::Environment)
|
7
|
+
|
8
|
+
Tynn.helpers(Tynn::Protection, ssl: Tynn.production?)
|
9
|
+
|
10
|
+
Tynn.helpers(Tynn::Session, secret: SecureRandom.hex(64))
|
11
|
+
|
12
|
+
Tynn.define do
|
13
|
+
root do
|
14
|
+
res.write("use protection")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
run(Tynn)
|
data/lib/tynn.rb
CHANGED
@@ -9,7 +9,7 @@ class Tynn < Syro::Deck
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def self.use(_middleware, *args, &block)
|
12
|
-
middleware
|
12
|
+
middleware << (Proc.new { |app| _middleware.new(app, *args, &block) })
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.helpers(helper, *args)
|
@@ -34,7 +34,7 @@ class Tynn < Syro::Deck
|
|
34
34
|
if middleware.empty?
|
35
35
|
return @syro
|
36
36
|
else
|
37
|
-
return middleware.inject(@syro) { |a, m| m.call(a) }
|
37
|
+
return middleware.reverse.inject(@syro) { |a, m| m.call(a) }
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
data/lib/tynn/csrf.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
module Tynn::CSRF
|
2
|
+
def csrf
|
3
|
+
@csrf ||= Tynn::CSRF::Helper.new(self)
|
4
|
+
end
|
5
|
+
|
6
|
+
class Helper
|
7
|
+
CSRF_HEADER = "HTTP_X_CSRF_TOKEN".freeze
|
8
|
+
|
9
|
+
def initialize(app)
|
10
|
+
@app = app
|
11
|
+
@req = app.req
|
12
|
+
end
|
13
|
+
|
14
|
+
def token
|
15
|
+
return session[:csrf_token] ||= SecureRandom.base64(32)
|
16
|
+
end
|
17
|
+
|
18
|
+
def reset!
|
19
|
+
session.delete(:csrf_token)
|
20
|
+
end
|
21
|
+
|
22
|
+
def safe?
|
23
|
+
return @req.get? || @req.head? || verify_token
|
24
|
+
end
|
25
|
+
|
26
|
+
def unsafe?
|
27
|
+
return !safe?
|
28
|
+
end
|
29
|
+
|
30
|
+
def form_tag
|
31
|
+
return %Q(<input type="hidden" name="csrf_token" value="#{ token }">)
|
32
|
+
end
|
33
|
+
|
34
|
+
def meta_tag
|
35
|
+
return %Q(<meta name="csrf_token" content="#{ token }">)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def verify_token
|
41
|
+
return @req[:csrf_token] == token || @req.env[CSRF_HEADER] == token
|
42
|
+
end
|
43
|
+
|
44
|
+
def session
|
45
|
+
return @app.session
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/tynn/environment.rb
CHANGED
@@ -1,9 +1,26 @@
|
|
1
|
+
# Adds helper methods to get and check the current environment.
|
2
|
+
#
|
3
|
+
# require "tynn"
|
4
|
+
# require "tynn/environment"
|
5
|
+
#
|
6
|
+
# Tynn.helpers(Tynn::Environment)
|
7
|
+
#
|
8
|
+
# Tynn.environment # => :development
|
9
|
+
#
|
10
|
+
# Tynn.development? # => true
|
11
|
+
# Tynn.production? # => false
|
12
|
+
# Tynn.test? # => false
|
13
|
+
#
|
14
|
+
# By default, the environment is based on `ENV["RACK_ENV"]`.
|
15
|
+
#
|
16
|
+
# Tynn.helpers(Tynn::Environment, env: ENV["RACK_ENV"])
|
17
|
+
#
|
1
18
|
module Tynn::Environment
|
2
19
|
def self.setup(app, env: ENV["RACK_ENV"]) # :nodoc:
|
3
20
|
app.settings[:environment] = (env || :development).to_sym
|
4
21
|
end
|
5
22
|
|
6
|
-
module ClassMethods
|
23
|
+
module ClassMethods # :nodoc:
|
7
24
|
def environment
|
8
25
|
return settings[:environment]
|
9
26
|
end
|
data/lib/tynn/json.rb
CHANGED
data/lib/tynn/json_parser.rb
CHANGED
@@ -2,7 +2,7 @@ require "json"
|
|
2
2
|
|
3
3
|
module Tynn::JSONParser
|
4
4
|
def self.setup(app) # :nodoc:
|
5
|
-
app.use(Middleware)
|
5
|
+
app.use(Tynn::JSONParser::Middleware)
|
6
6
|
end
|
7
7
|
|
8
8
|
class Middleware # :nodoc:
|
@@ -27,6 +27,8 @@ module Tynn::JSONParser
|
|
27
27
|
return @app.call(request.env)
|
28
28
|
end
|
29
29
|
|
30
|
+
private
|
31
|
+
|
30
32
|
def json?(request)
|
31
33
|
return request.media_type == CONTENT_TYPE
|
32
34
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# Adds extra matchers to Tynn.
|
2
|
+
#
|
3
|
+
# require "tynn"
|
4
|
+
# require "tynn/matchers"
|
5
|
+
#
|
6
|
+
# Tynn.helpers(Tynn::Matchers)
|
7
|
+
#
|
8
|
+
module Tynn::Matchers
|
9
|
+
# A catch-all matcher.
|
10
|
+
#
|
11
|
+
# Tynn.define do
|
12
|
+
# authenticated? do
|
13
|
+
# # ...
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# default do # on true
|
17
|
+
# # ...
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# :call-seq: default(&block)
|
22
|
+
#
|
23
|
+
def default
|
24
|
+
yield
|
25
|
+
|
26
|
+
halt(res.finish)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Match if the given `params` are present.
|
30
|
+
#
|
31
|
+
# Tynn.define do
|
32
|
+
# on param?(:token) do
|
33
|
+
# # ...
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
def param?(*params)
|
38
|
+
return params.all? { |param| (v = req[param]) && !v.empty? }
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Tynn::NotFound
|
2
|
+
def call(env, inbox) # :nodoc:
|
3
|
+
result = super(env, inbox)
|
4
|
+
|
5
|
+
status, _, body = result
|
6
|
+
|
7
|
+
if status == 404 && body.empty?
|
8
|
+
not_found
|
9
|
+
|
10
|
+
return res.finish
|
11
|
+
else
|
12
|
+
return result
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def not_found # :nodoc:
|
17
|
+
end
|
18
|
+
end
|
data/lib/tynn/secure_headers.rb
CHANGED
data/lib/tynn/session.rb
CHANGED
data/lib/tynn/ssl.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
module Tynn::SSL
|
2
|
+
def self.setup(app, options = {}) # :nodoc:
|
3
|
+
app.use(Tynn::SSL::Middleware, options)
|
4
|
+
end
|
5
|
+
|
6
|
+
class Middleware # :nodoc:
|
7
|
+
def initialize(app, hsts: {})
|
8
|
+
@app = app
|
9
|
+
@hsts_header = build_hsts_header(hsts)
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
request = Rack::Request.new(env)
|
14
|
+
|
15
|
+
unless request.ssl?
|
16
|
+
return [301, redirect_headers(request), []]
|
17
|
+
end
|
18
|
+
|
19
|
+
result = @app.call(env)
|
20
|
+
|
21
|
+
set_hsts_header(result[1])
|
22
|
+
set_cookies_as_secure(result[1])
|
23
|
+
|
24
|
+
return result
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
HSTS_HEADER = "Strict-Transport-Security".freeze
|
30
|
+
HSTS_EXPIRE = 15_552_000 # 180 days
|
31
|
+
|
32
|
+
def build_hsts_header(options)
|
33
|
+
header = sprintf("max-age=%i", options.fetch(:expires, HSTS_EXPIRE))
|
34
|
+
header << "; includeSubdomains" if options.fetch(:subdomains, true)
|
35
|
+
header << "; preload" if options[:preload]
|
36
|
+
|
37
|
+
return header
|
38
|
+
end
|
39
|
+
|
40
|
+
def redirect_headers(request)
|
41
|
+
return {
|
42
|
+
"Content-Type" => "text/html",
|
43
|
+
"Location" => https_location(request)
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
HTTPS_LOCATION = "https://%s%s".freeze
|
48
|
+
|
49
|
+
def https_location(request)
|
50
|
+
return sprintf(HTTPS_LOCATION, request.host, request.fullpath)
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_hsts_header(headers)
|
54
|
+
headers[HSTS_HEADER] = @hsts_header
|
55
|
+
end
|
56
|
+
|
57
|
+
COOKIE_HEADER = "Set-Cookie".freeze
|
58
|
+
COOKIE_SEPARATOR = "\n".freeze
|
59
|
+
COOKIE_REGEXP = /;\s*secure\s*(;|$)/i
|
60
|
+
|
61
|
+
def set_cookies_as_secure(headers)
|
62
|
+
return unless cookies = headers[COOKIE_HEADER]
|
63
|
+
|
64
|
+
cookies = cookies.split(COOKIE_SEPARATOR).map do |cookie|
|
65
|
+
(cookie !~ COOKIE_REGEXP) ? "#{ cookie }; secure" : cookie
|
66
|
+
end
|
67
|
+
|
68
|
+
headers[COOKIE_HEADER] = cookies.join(COOKIE_SEPARATOR)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/tynn/version.rb
CHANGED
data/makefile
CHANGED
File without changes
|
data/test/csrf_test.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require "securerandom"
|
2
|
+
require_relative "../lib/tynn/csrf"
|
3
|
+
require_relative "../lib/tynn/session"
|
4
|
+
|
5
|
+
setup do
|
6
|
+
Tynn.helpers(Tynn::CSRF)
|
7
|
+
Tynn.helpers(Tynn::Session, secret: SecureRandom.hex(64))
|
8
|
+
|
9
|
+
Tynn::Test.new
|
10
|
+
end
|
11
|
+
|
12
|
+
test "get should be safe" do |app|
|
13
|
+
Tynn.define do
|
14
|
+
get do
|
15
|
+
res.write(csrf.safe?)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
app.get("/")
|
20
|
+
|
21
|
+
assert_equal "true", app.res.body
|
22
|
+
end
|
23
|
+
|
24
|
+
test "head should be safe" do |app|
|
25
|
+
Tynn.define do
|
26
|
+
on req.head? do
|
27
|
+
res.write(csrf.safe?)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
app.head("/")
|
32
|
+
|
33
|
+
assert_equal "true", app.res.body
|
34
|
+
end
|
35
|
+
|
36
|
+
test "invalid csrf token" do |app|
|
37
|
+
Tynn.define do
|
38
|
+
post do
|
39
|
+
res.write(csrf.unsafe?)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
app.post("/")
|
44
|
+
|
45
|
+
assert_equal "true", app.res.body
|
46
|
+
end
|
47
|
+
|
48
|
+
test "valid csrf token" do |app|
|
49
|
+
token = SecureRandom.hex(64)
|
50
|
+
|
51
|
+
Tynn.define do
|
52
|
+
post do
|
53
|
+
session[:csrf_token] = token
|
54
|
+
|
55
|
+
res.write(csrf.safe?)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
app.post("/", csrf_token: token)
|
60
|
+
|
61
|
+
assert_equal "true", app.res.body
|
62
|
+
end
|
63
|
+
|
64
|
+
test "resets token" do |app|
|
65
|
+
token = SecureRandom.hex(64)
|
66
|
+
|
67
|
+
Tynn.define do
|
68
|
+
post do
|
69
|
+
session[:csrf_token] = token
|
70
|
+
|
71
|
+
if csrf.unsafe?
|
72
|
+
csrf.reset!
|
73
|
+
end
|
74
|
+
|
75
|
+
res.write(csrf.token)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
app.post("/", csrf_token: "nonsense")
|
80
|
+
|
81
|
+
assert token != app.res.body
|
82
|
+
end
|
83
|
+
|
84
|
+
test "http header" do |app|
|
85
|
+
token = SecureRandom.hex(64)
|
86
|
+
|
87
|
+
Tynn.define do
|
88
|
+
post do
|
89
|
+
session[:csrf_token] = token
|
90
|
+
|
91
|
+
res.write(csrf.safe?)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
app.post("/", {}, "HTTP_X_CSRF_TOKEN" => token)
|
96
|
+
|
97
|
+
assert_equal "true", app.res.body
|
98
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative "../lib/tynn/matchers"
|
2
|
+
|
3
|
+
setup do
|
4
|
+
Tynn.helpers(Tynn::Matchers)
|
5
|
+
|
6
|
+
Tynn::Test.new
|
7
|
+
end
|
8
|
+
|
9
|
+
test "default" do |app|
|
10
|
+
Tynn.define do
|
11
|
+
default do
|
12
|
+
res.write("foo")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
app.get("/")
|
17
|
+
|
18
|
+
assert_equal 200, app.res.status
|
19
|
+
assert_equal "foo", app.res.body
|
20
|
+
end
|
21
|
+
|
22
|
+
test "param?" do |app|
|
23
|
+
Tynn.define do
|
24
|
+
on param?(:key) do
|
25
|
+
res.write(req[:key])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
app.get("/")
|
30
|
+
|
31
|
+
assert_equal 404, app.res.status
|
32
|
+
|
33
|
+
app.get("/", key: "foo")
|
34
|
+
|
35
|
+
assert_equal 200, app.res.status
|
36
|
+
assert_equal "foo", app.res.body
|
37
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative "../lib/tynn/not_found"
|
2
|
+
|
3
|
+
test "not found" do
|
4
|
+
Tynn.helpers(Tynn::NotFound)
|
5
|
+
|
6
|
+
class Tynn
|
7
|
+
def not_found
|
8
|
+
res.write("not found")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
Tynn.define do
|
13
|
+
end
|
14
|
+
|
15
|
+
app = Tynn::Test.new
|
16
|
+
app.get("/notfound")
|
17
|
+
|
18
|
+
assert_equal "not found", app.res.body
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative "../lib/tynn/protection"
|
2
|
+
|
3
|
+
test "includes secure headers" do
|
4
|
+
Tynn.helpers(Tynn::Protection)
|
5
|
+
|
6
|
+
assert Tynn.include?(Tynn::SecureHeaders)
|
7
|
+
end
|
8
|
+
|
9
|
+
test "includes ssl helper if ssl is true" do
|
10
|
+
Tynn.helpers(Tynn::Protection, ssl: true)
|
11
|
+
|
12
|
+
assert Tynn.include?(Tynn::SSL)
|
13
|
+
end
|
14
|
+
|
15
|
+
test "supports hsts options" do
|
16
|
+
hsts = { expires: 100, subdomains: false, preload: true }
|
17
|
+
|
18
|
+
Tynn.helpers(Tynn::Protection, ssl: true, hsts: hsts)
|
19
|
+
|
20
|
+
Tynn.define do
|
21
|
+
end
|
22
|
+
|
23
|
+
app = Tynn::Test.new
|
24
|
+
app.get("/", {}, "HTTPS" => "on")
|
25
|
+
|
26
|
+
hsts = app.res.headers["Strict-Transport-Security"]
|
27
|
+
|
28
|
+
assert_equal "max-age=100; preload", hsts
|
29
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
@@ -15,8 +15,11 @@ test "session" do
|
|
15
15
|
app = Tynn::Test.new
|
16
16
|
app.get("/")
|
17
17
|
|
18
|
-
env = app.
|
18
|
+
env = app.req.env
|
19
|
+
session = env["rack.session"]
|
20
|
+
session_options = env["rack.session.options"]
|
19
21
|
|
20
22
|
assert_equal "foo", app.res.body
|
21
|
-
assert_equal "foo",
|
23
|
+
assert_equal "foo", session["foo"]
|
24
|
+
assert_equal true, session_options[:http_only]
|
22
25
|
end
|
data/test/ssl_test.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require_relative "../lib/tynn/ssl"
|
2
|
+
|
3
|
+
setup do
|
4
|
+
Tynn::Test.new
|
5
|
+
end
|
6
|
+
|
7
|
+
test "redirects to https" do |app|
|
8
|
+
Tynn.helpers(Tynn::SSL)
|
9
|
+
|
10
|
+
Tynn.define do
|
11
|
+
end
|
12
|
+
|
13
|
+
app.get("/")
|
14
|
+
|
15
|
+
assert_equal 301, app.res.status
|
16
|
+
assert_equal "https://example.org/", app.res.location
|
17
|
+
end
|
18
|
+
|
19
|
+
test "https request" do |app|
|
20
|
+
Tynn.helpers(Tynn::SSL)
|
21
|
+
|
22
|
+
Tynn.define do
|
23
|
+
root do
|
24
|
+
res.write("secure")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
app.get("/", {}, "HTTPS" => "on")
|
29
|
+
|
30
|
+
assert_equal "secure", app.res.body
|
31
|
+
end
|
32
|
+
|
33
|
+
test "hsts header" do |app|
|
34
|
+
Tynn.helpers(Tynn::SSL)
|
35
|
+
|
36
|
+
Tynn.define do
|
37
|
+
end
|
38
|
+
|
39
|
+
app = Tynn::Test.new
|
40
|
+
app.get("/", {}, "HTTPS" => "on")
|
41
|
+
|
42
|
+
header = app.res.headers["Strict-Transport-Security"]
|
43
|
+
|
44
|
+
assert_equal "max-age=15552000; includeSubdomains", header
|
45
|
+
end
|
46
|
+
|
47
|
+
test "hsts header options" do |app|
|
48
|
+
Tynn.helpers(Tynn::SSL, hsts: {
|
49
|
+
expires: 1,
|
50
|
+
subdomains: false,
|
51
|
+
preload: true
|
52
|
+
})
|
53
|
+
|
54
|
+
Tynn.define do
|
55
|
+
end
|
56
|
+
|
57
|
+
app = Tynn::Test.new
|
58
|
+
app.get("/", {}, "HTTPS" => "on")
|
59
|
+
|
60
|
+
header = app.res.headers["Strict-Transport-Security"]
|
61
|
+
|
62
|
+
assert_equal "max-age=1; preload", header
|
63
|
+
end
|
64
|
+
|
65
|
+
test "secure cookies" do |app|
|
66
|
+
Tynn.helpers(Tynn::SSL)
|
67
|
+
|
68
|
+
Tynn.define do
|
69
|
+
get do
|
70
|
+
res.set_cookie("first", "cookie")
|
71
|
+
res.set_cookie("other", "cookie")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
app = Tynn::Test.new
|
76
|
+
app.get("/", {}, "HTTPS" => "on")
|
77
|
+
|
78
|
+
first, other = app.res.headers["Set-Cookie"].split("\n")
|
79
|
+
|
80
|
+
assert_equal "first=cookie; secure", first
|
81
|
+
assert_equal "other=cookie; secure", other
|
82
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tynn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Francesco Rodríguez
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-10-
|
11
|
+
date: 2015-10-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -116,43 +116,51 @@ extensions: []
|
|
116
116
|
extra_rdoc_files: []
|
117
117
|
files:
|
118
118
|
- ".gems"
|
119
|
+
- ".gitignore"
|
119
120
|
- LICENSE
|
120
121
|
- README.md
|
121
122
|
- examples/composition.ru
|
122
123
|
- examples/hello.ru
|
124
|
+
- examples/protection.ru
|
123
125
|
- examples/render.ru
|
124
126
|
- examples/views/home.erb
|
125
127
|
- examples/views/layout.erb
|
126
128
|
- lib/tynn.rb
|
127
|
-
- lib/tynn/
|
129
|
+
- lib/tynn/csrf.rb
|
128
130
|
- lib/tynn/environment.rb
|
129
131
|
- lib/tynn/erubis.rb
|
130
132
|
- lib/tynn/hmote.rb
|
131
|
-
- lib/tynn/hsts.rb
|
132
133
|
- lib/tynn/json.rb
|
133
134
|
- lib/tynn/json_parser.rb
|
135
|
+
- lib/tynn/matchers.rb
|
136
|
+
- lib/tynn/not_found.rb
|
137
|
+
- lib/tynn/protection.rb
|
134
138
|
- lib/tynn/render.rb
|
135
139
|
- lib/tynn/secure_headers.rb
|
136
140
|
- lib/tynn/send_file.rb
|
137
141
|
- lib/tynn/session.rb
|
142
|
+
- lib/tynn/ssl.rb
|
138
143
|
- lib/tynn/static.rb
|
139
144
|
- lib/tynn/test.rb
|
140
145
|
- lib/tynn/version.rb
|
141
146
|
- makefile
|
142
|
-
- test/
|
143
|
-
- test/
|
144
|
-
- test/
|
145
|
-
- test/
|
147
|
+
- test/core_test.rb
|
148
|
+
- test/csrf_test.rb
|
149
|
+
- test/environment_test.rb
|
150
|
+
- test/erubis_test.rb
|
146
151
|
- test/helper.rb
|
147
|
-
- test/
|
148
|
-
- test/
|
149
|
-
- test/
|
150
|
-
- test/
|
151
|
-
- test/
|
152
|
-
- test/
|
153
|
-
- test/
|
154
|
-
- test/
|
155
|
-
- test/
|
152
|
+
- test/hmote_test.rb
|
153
|
+
- test/json_parser_test.rb
|
154
|
+
- test/json_test.rb
|
155
|
+
- test/matchers_test.rb
|
156
|
+
- test/not_found_test.rb
|
157
|
+
- test/protection_test.rb
|
158
|
+
- test/render_test.rb
|
159
|
+
- test/secure_headers_test.rb
|
160
|
+
- test/send_file_test.rb
|
161
|
+
- test/session_test.rb
|
162
|
+
- test/ssl_test.rb
|
163
|
+
- test/static_test.rb
|
156
164
|
- test/views/custom_layout.erb
|
157
165
|
- test/views/custom_layout.mote
|
158
166
|
- test/views/layout.erb
|
data/lib/tynn/default_matcher.rb
DELETED
data/lib/tynn/hsts.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
module Tynn::HSTS
|
2
|
-
HSTS_HEADER = "Strict-Transport-Security".freeze # :nodoc:
|
3
|
-
HSTS_EXPIRE = 15_552_000 # 180 days # :nodoc:
|
4
|
-
|
5
|
-
def self.setup(app, options = {}) # :nodoc:
|
6
|
-
max_age = options.fetch(:max_age, HSTS_EXPIRE)
|
7
|
-
subdomains = options.fetch(:subdomains, true)
|
8
|
-
preload = options.fetch(:preload, false)
|
9
|
-
|
10
|
-
header = sprintf("max-age=%i", max_age)
|
11
|
-
header << "; includeSubdomains" if subdomains
|
12
|
-
header << "; preload" if preload
|
13
|
-
|
14
|
-
app.settings[:hsts] = header
|
15
|
-
end
|
16
|
-
|
17
|
-
def call(env, inbox) # :nodoc:
|
18
|
-
result = super(env, inbox)
|
19
|
-
result[1][HSTS_HEADER] = settings[:hsts]
|
20
|
-
|
21
|
-
return result
|
22
|
-
end
|
23
|
-
end
|
data/test/default_matcher.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
require_relative "../lib/tynn/default_matcher"
|
2
|
-
|
3
|
-
test "default" do
|
4
|
-
Tynn.helpers(Tynn::DefaultMatcher)
|
5
|
-
|
6
|
-
Tynn.define do
|
7
|
-
default do
|
8
|
-
res.write("foo")
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
app = Tynn::Test.new
|
13
|
-
app.get("/")
|
14
|
-
|
15
|
-
assert_equal 200, app.res.status
|
16
|
-
assert_equal "foo", app.res.body
|
17
|
-
end
|
data/test/hsts.rb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
require_relative "../lib/tynn/hsts"
|
2
|
-
|
3
|
-
test "hsts" do
|
4
|
-
Tynn.helpers(Tynn::HSTS)
|
5
|
-
|
6
|
-
Tynn.define do
|
7
|
-
end
|
8
|
-
|
9
|
-
app = Tynn::Test.new
|
10
|
-
app.get("/")
|
11
|
-
|
12
|
-
header = app.res.headers["Strict-Transport-Security"]
|
13
|
-
|
14
|
-
assert_equal "max-age=15552000; includeSubdomains", header
|
15
|
-
end
|
16
|
-
|
17
|
-
test "hsts with options" do
|
18
|
-
Tynn.helpers(Tynn::HSTS, max_age: 1, preload: true)
|
19
|
-
|
20
|
-
Tynn.define do
|
21
|
-
end
|
22
|
-
|
23
|
-
app = Tynn::Test.new
|
24
|
-
app.get("/")
|
25
|
-
|
26
|
-
header = app.res.headers["Strict-Transport-Security"]
|
27
|
-
|
28
|
-
assert_equal "max-age=1; includeSubdomains; preload", header
|
29
|
-
end
|