tynn 1.0.0.rc2 → 1.0.0.rc3
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/README.md +1 -3
- data/lib/tynn.rb +61 -102
- data/lib/tynn/all_methods.rb +11 -9
- data/lib/tynn/environment.rb +13 -13
- data/lib/tynn/force_ssl.rb +55 -0
- data/lib/tynn/hmote.rb +34 -0
- data/lib/tynn/hsts.rb +64 -0
- data/lib/tynn/json.rb +34 -30
- data/lib/tynn/matchers.rb +50 -48
- data/lib/tynn/not_found.rb +11 -9
- data/lib/tynn/protection.rb +29 -18
- data/lib/tynn/render.rb +24 -23
- data/lib/tynn/response.rb +138 -126
- data/lib/tynn/secure_headers.rb +29 -30
- data/lib/tynn/session.rb +62 -64
- data/lib/tynn/settings.rb +28 -0
- data/lib/tynn/static.rb +23 -20
- data/lib/tynn/test.rb +30 -31
- data/lib/tynn/version.rb +1 -1
- data/test/default_headers_test.rb +14 -0
- data/test/environment_test.rb +14 -18
- data/test/force_ssl_test.rb +32 -0
- data/test/hmote_test.rb +78 -0
- data/test/hsts_test.rb +29 -0
- data/test/protection_test.rb +0 -30
- data/test/render_test.rb +14 -1
- data/test/secure_headers_test.rb +8 -1
- metadata +15 -22
- data/lib/tynn/erubis.rb +0 -17
- data/lib/tynn/ssl.rb +0 -73
- data/test/erubis_test.rb +0 -20
- data/test/ssl_test.rb +0 -82
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative "../lib/tynn/force_ssl"
|
2
|
+
|
3
|
+
setup do
|
4
|
+
Tynn::Test.new
|
5
|
+
end
|
6
|
+
|
7
|
+
test "redirects to https" do |app|
|
8
|
+
Tynn.helpers(Tynn::ForceSSL)
|
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::ForceSSL)
|
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 200, app.res.status
|
31
|
+
assert_equal "secure", app.res.body
|
32
|
+
end
|
data/test/hmote_test.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require_relative "../lib/tynn/hmote"
|
2
|
+
|
3
|
+
setup do
|
4
|
+
Tynn.helpers(Tynn::HMote, views: File.expand_path("./test/views"))
|
5
|
+
|
6
|
+
Tynn::Test.new
|
7
|
+
end
|
8
|
+
|
9
|
+
test "partial" do |app|
|
10
|
+
Tynn.define do
|
11
|
+
on "partial" do
|
12
|
+
res.write(partial("partial", name: "mote"))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
app.get("/partial")
|
17
|
+
|
18
|
+
assert_equal "mote", app.res.body.strip
|
19
|
+
end
|
20
|
+
|
21
|
+
test "view" do |app|
|
22
|
+
Tynn.define do
|
23
|
+
on "view" do
|
24
|
+
res.write(view("view", title: "tynn", name: "mote"))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
app.get("/view")
|
29
|
+
|
30
|
+
assert_equal "tynn / mote", app.res.body.strip
|
31
|
+
end
|
32
|
+
|
33
|
+
test "render" do |app|
|
34
|
+
Tynn.define do
|
35
|
+
on "render" do
|
36
|
+
render("view", title: "tynn", name: "mote")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
app.get("/render")
|
41
|
+
|
42
|
+
assert_equal 200, app.res.status
|
43
|
+
assert_equal "text/html", app.res.headers["Content-Type"]
|
44
|
+
assert_equal "tynn / mote", app.res.body.strip
|
45
|
+
end
|
46
|
+
|
47
|
+
test "404" do |app|
|
48
|
+
Tynn.define do
|
49
|
+
on "404" do
|
50
|
+
res.status = 404
|
51
|
+
|
52
|
+
render("view", title: "tynn", name: "mote")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
app.get("/404")
|
57
|
+
|
58
|
+
assert_equal 404, app.res.status
|
59
|
+
assert_equal "text/html", app.res.headers["Content-Type"]
|
60
|
+
assert_equal "tynn / mote", app.res.body.strip
|
61
|
+
end
|
62
|
+
|
63
|
+
test "custom layout" do
|
64
|
+
class App < Tynn
|
65
|
+
set(:layout, "custom_layout")
|
66
|
+
end
|
67
|
+
|
68
|
+
App.define do
|
69
|
+
root do
|
70
|
+
render("view", title: "tynn", name: "mote")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
app = Tynn::Test.new(App)
|
75
|
+
app.get("/")
|
76
|
+
|
77
|
+
assert_equal "custom / tynn / mote", app.res.body.strip
|
78
|
+
end
|
data/test/hsts_test.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative "../lib/tynn/hsts"
|
2
|
+
|
3
|
+
test "hsts header" do |app|
|
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 header options" do |app|
|
18
|
+
Tynn.helpers(Tynn::HSTS, expires: 1, subdomains: false, 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; preload", header
|
29
|
+
end
|
data/test/protection_test.rb
CHANGED
@@ -1,18 +1,6 @@
|
|
1
1
|
require_relative "../lib/tynn/protection"
|
2
2
|
require_relative "../lib/tynn/session"
|
3
3
|
|
4
|
-
test "includes secure headers" do
|
5
|
-
Tynn.helpers(Tynn::Protection)
|
6
|
-
|
7
|
-
assert Tynn.include?(Tynn::SecureHeaders)
|
8
|
-
end
|
9
|
-
|
10
|
-
test "includes ssl helper if ssl is true" do
|
11
|
-
Tynn.helpers(Tynn::Protection, ssl: true)
|
12
|
-
|
13
|
-
assert Tynn.include?(Tynn::SSL)
|
14
|
-
end
|
15
|
-
|
16
4
|
test "supports hsts options" do
|
17
5
|
hsts = { expires: 100, subdomains: false, preload: true }
|
18
6
|
|
@@ -46,21 +34,3 @@ test "adds secure flag to session cookie" do
|
|
46
34
|
|
47
35
|
assert(/;\s*secure\s*(;|$)/i === session)
|
48
36
|
end
|
49
|
-
|
50
|
-
test "if session uses secure flag" do
|
51
|
-
Tynn.helpers(Tynn::Protection, ssl: true)
|
52
|
-
Tynn.helpers(Tynn::Session, secret: "_this_must_be_random_", secure: true)
|
53
|
-
|
54
|
-
Tynn.define do
|
55
|
-
root do
|
56
|
-
session[:foo] = "foo"
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
app = Tynn::Test.new
|
61
|
-
app.get("/", {}, "HTTPS" => "on")
|
62
|
-
|
63
|
-
session, _ = app.res.headers["Set-Cookie"].split("\n")
|
64
|
-
|
65
|
-
assert(/;\s*secure\s*(;|$)/i === session)
|
66
|
-
end
|
data/test/render_test.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require "
|
1
|
+
require "erubis"
|
2
2
|
require_relative "../lib/tynn/render"
|
3
3
|
|
4
4
|
setup do
|
@@ -77,3 +77,16 @@ test "custom layout" do
|
|
77
77
|
|
78
78
|
assert_equal "custom / tynn / erb", app.res.body.strip
|
79
79
|
end
|
80
|
+
|
81
|
+
test "escapes by default" do
|
82
|
+
Tynn.define do
|
83
|
+
root do
|
84
|
+
res.write(partial("partial", name: "<a></a>"))
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
app = Tynn::Test.new
|
89
|
+
app.get("/")
|
90
|
+
|
91
|
+
assert_equal "<a></a>", app.res.body.strip
|
92
|
+
end
|
data/test/secure_headers_test.rb
CHANGED
@@ -12,9 +12,16 @@ test "secure headers" do
|
|
12
12
|
app = Tynn::Test.new
|
13
13
|
app.get("/")
|
14
14
|
|
15
|
+
secure_headers = {
|
16
|
+
"X-Content-Type-Options" => "nosniff",
|
17
|
+
"X-Frame-Options" => "SAMEORIGIN",
|
18
|
+
"X-Permitted-Cross-Domain-Policies" => "none",
|
19
|
+
"X-XSS-Protection" => "1; mode=block"
|
20
|
+
}
|
21
|
+
|
15
22
|
headers = app.res.headers
|
16
23
|
|
17
|
-
|
24
|
+
secure_headers.each do |header, value|
|
18
25
|
assert_equal(value, headers[header])
|
19
26
|
end
|
20
27
|
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: 1.0.0.
|
4
|
+
version: 1.0.0.rc3
|
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-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -24,20 +24,6 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.6'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: seteable
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '1.1'
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '1.1'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: syro
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -134,7 +120,9 @@ files:
|
|
134
120
|
- lib/tynn.rb
|
135
121
|
- lib/tynn/all_methods.rb
|
136
122
|
- lib/tynn/environment.rb
|
137
|
-
- lib/tynn/
|
123
|
+
- lib/tynn/force_ssl.rb
|
124
|
+
- lib/tynn/hmote.rb
|
125
|
+
- lib/tynn/hsts.rb
|
138
126
|
- lib/tynn/json.rb
|
139
127
|
- lib/tynn/matchers.rb
|
140
128
|
- lib/tynn/not_found.rb
|
@@ -144,15 +132,18 @@ files:
|
|
144
132
|
- lib/tynn/response.rb
|
145
133
|
- lib/tynn/secure_headers.rb
|
146
134
|
- lib/tynn/session.rb
|
147
|
-
- lib/tynn/
|
135
|
+
- lib/tynn/settings.rb
|
148
136
|
- lib/tynn/static.rb
|
149
137
|
- lib/tynn/test.rb
|
150
138
|
- lib/tynn/version.rb
|
151
139
|
- test/all_methods_test.rb
|
152
140
|
- test/core_test.rb
|
141
|
+
- test/default_headers_test.rb
|
153
142
|
- test/environment_test.rb
|
154
|
-
- test/
|
143
|
+
- test/force_ssl_test.rb
|
155
144
|
- test/helper.rb
|
145
|
+
- test/hmote_test.rb
|
146
|
+
- test/hsts_test.rb
|
156
147
|
- test/json_test.rb
|
157
148
|
- test/matchers_test.rb
|
158
149
|
- test/middleware_test.rb
|
@@ -161,7 +152,6 @@ files:
|
|
161
152
|
- test/render_test.rb
|
162
153
|
- test/secure_headers_test.rb
|
163
154
|
- test/session_test.rb
|
164
|
-
- test/ssl_test.rb
|
165
155
|
- test/static_test.rb
|
166
156
|
homepage: https://github.com/frodsan/tynn
|
167
157
|
licenses:
|
@@ -190,9 +180,12 @@ summary: Thin library to create web applications
|
|
190
180
|
test_files:
|
191
181
|
- test/all_methods_test.rb
|
192
182
|
- test/core_test.rb
|
183
|
+
- test/default_headers_test.rb
|
193
184
|
- test/environment_test.rb
|
194
|
-
- test/
|
185
|
+
- test/force_ssl_test.rb
|
195
186
|
- test/helper.rb
|
187
|
+
- test/hmote_test.rb
|
188
|
+
- test/hsts_test.rb
|
196
189
|
- test/json_test.rb
|
197
190
|
- test/matchers_test.rb
|
198
191
|
- test/middleware_test.rb
|
@@ -201,5 +194,5 @@ test_files:
|
|
201
194
|
- test/render_test.rb
|
202
195
|
- test/secure_headers_test.rb
|
203
196
|
- test/session_test.rb
|
204
|
-
- test/ssl_test.rb
|
205
197
|
- test/static_test.rb
|
198
|
+
has_rdoc:
|
data/lib/tynn/erubis.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
require "erubis"
|
2
|
-
require_relative "render"
|
3
|
-
|
4
|
-
class Tynn
|
5
|
-
module Erubis
|
6
|
-
def self.setup(app, options = {}) # :nodoc:
|
7
|
-
options = options.dup
|
8
|
-
|
9
|
-
options[:options] ||= {}
|
10
|
-
options[:options] = {
|
11
|
-
escape_html: true
|
12
|
-
}.merge!(options[:options])
|
13
|
-
|
14
|
-
app.helpers(Tynn::Render, options)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
data/lib/tynn/ssl.rb
DELETED
@@ -1,73 +0,0 @@
|
|
1
|
-
class Tynn
|
2
|
-
module SSL
|
3
|
-
def self.setup(app, hsts: {}) # :nodoc:
|
4
|
-
app.use(Tynn::SSL::Middleware, hsts: hsts)
|
5
|
-
end
|
6
|
-
|
7
|
-
class Middleware # :nodoc:
|
8
|
-
def initialize(app, hsts: {})
|
9
|
-
@app = app
|
10
|
-
@hsts_header = build_hsts_header(hsts)
|
11
|
-
end
|
12
|
-
|
13
|
-
def call(env)
|
14
|
-
request = Rack::Request.new(env)
|
15
|
-
|
16
|
-
unless request.ssl?
|
17
|
-
return [301, redirect_headers(request), []]
|
18
|
-
end
|
19
|
-
|
20
|
-
result = @app.call(env)
|
21
|
-
|
22
|
-
set_hsts_header(result[1])
|
23
|
-
set_cookies_as_secure(result[1])
|
24
|
-
|
25
|
-
return result
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
HSTS_HEADER = "Strict-Transport-Security".freeze
|
31
|
-
HSTS_EXPIRE = 15_552_000 # 180 days
|
32
|
-
|
33
|
-
def build_hsts_header(options)
|
34
|
-
header = sprintf("max-age=%i", options.fetch(:expires, HSTS_EXPIRE))
|
35
|
-
header << "; includeSubdomains" if options.fetch(:subdomains, true)
|
36
|
-
header << "; preload" if options[:preload]
|
37
|
-
|
38
|
-
return header
|
39
|
-
end
|
40
|
-
|
41
|
-
def redirect_headers(request)
|
42
|
-
return {
|
43
|
-
"Content-Type" => "text/html",
|
44
|
-
"Location" => https_location(request)
|
45
|
-
}
|
46
|
-
end
|
47
|
-
|
48
|
-
HTTPS_LOCATION = "https://%s%s".freeze
|
49
|
-
|
50
|
-
def https_location(request)
|
51
|
-
return sprintf(HTTPS_LOCATION, request.host, request.fullpath)
|
52
|
-
end
|
53
|
-
|
54
|
-
def set_hsts_header(headers)
|
55
|
-
headers[HSTS_HEADER] = @hsts_header
|
56
|
-
end
|
57
|
-
|
58
|
-
COOKIE_HEADER = "Set-Cookie".freeze
|
59
|
-
COOKIE_SEPARATOR = "\n".freeze
|
60
|
-
COOKIE_REGEXP = /;\s*secure\s*(;|$)/i
|
61
|
-
|
62
|
-
def set_cookies_as_secure(headers)
|
63
|
-
return unless cookies = headers[COOKIE_HEADER]
|
64
|
-
|
65
|
-
cookies = cookies.split(COOKIE_SEPARATOR).map do |cookie|
|
66
|
-
(cookie !~ COOKIE_REGEXP) ? "#{ cookie }; secure" : cookie
|
67
|
-
end
|
68
|
-
|
69
|
-
headers[COOKIE_HEADER] = cookies.join(COOKIE_SEPARATOR)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
data/test/erubis_test.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
require_relative "../lib/tynn/erubis"
|
2
|
-
|
3
|
-
setup do
|
4
|
-
Tynn.helpers(Tynn::Erubis, views: File.expand_path("./test/views"))
|
5
|
-
|
6
|
-
Tynn::Test.new
|
7
|
-
end
|
8
|
-
|
9
|
-
test "escapes" do |app|
|
10
|
-
Tynn.define do
|
11
|
-
root do
|
12
|
-
res.write(partial("partial", name: "<a></a>"))
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
app = Tynn::Test.new
|
17
|
-
app.get("/")
|
18
|
-
|
19
|
-
assert_equal "<a></a>", app.res.body.strip
|
20
|
-
end
|
data/test/ssl_test.rb
DELETED
@@ -1,82 +0,0 @@
|
|
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
|