tynn 0.0.2 → 0.0.3
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.
- 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
|