tynn 1.0.0.rc2 → 1.0.0.rc3
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/lib/tynn/secure_headers.rb
CHANGED
@@ -1,45 +1,44 @@
|
|
1
1
|
class Tynn
|
2
|
-
# Adds security related HTTP headers.
|
2
|
+
# Public: Adds security related HTTP headers.
|
3
3
|
#
|
4
|
-
#
|
5
|
-
# require "tynn"
|
6
|
-
# require "tynn/secure_headers"
|
4
|
+
# Examples
|
7
5
|
#
|
8
|
-
#
|
9
|
-
#
|
6
|
+
# require "tynn"
|
7
|
+
# require "tynn/secure_headers"
|
8
|
+
#
|
9
|
+
# Tynn.helpers(Tynn::SecureHeaders)
|
10
10
|
#
|
11
11
|
# This helper applies the following headers:
|
12
12
|
#
|
13
|
-
# *
|
14
|
-
#
|
13
|
+
# *X-Content-Type-Options:* <tt>"nosniff"</tt>
|
14
|
+
#
|
15
|
+
# Prevents IE and Chrome from
|
16
|
+
# {content type sniffing}[https://msdn.microsoft.com/library/gg622941(v=vs.85).aspx]
|
17
|
+
#
|
18
|
+
# *X-Frame-Options:* <tt>"SAMEORIGIN"</tt>
|
19
|
+
#
|
20
|
+
# Provides {Clickjacking}[https://www.owasp.org/index.php/Clickjacking]
|
21
|
+
# protection.
|
15
22
|
#
|
16
|
-
# *
|
17
|
-
# protection. Check the [X-Frame-Options draft][x-frame-options] for
|
18
|
-
# more information.
|
23
|
+
# *X-Permitted-Cross-Domain-Policies:* <tt>"none"</tt>
|
19
24
|
#
|
20
|
-
#
|
21
|
-
# access to data. Check this [article][pcdp] for more information.
|
25
|
+
# Restricts Adobe Flash Player's access to data.
|
22
26
|
#
|
23
|
-
# *
|
24
|
-
# into IE, Chrome and Safari. This filter is usually enabled by default,
|
25
|
-
# the use of this header is to re-enable it if it was disabled by the user.
|
27
|
+
# *X-XSS-Protection:* <tt>"1; mode=block"</tt>
|
26
28
|
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
# [x-frame-options]: https://tools.ietf.org/html/draft-ietf-websec-x-frame-options-02
|
31
|
-
# [xss]: https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)
|
29
|
+
# Enables the XSS protection filter built into IE, Chrome and Safari.
|
30
|
+
# This filter is usually enabled by default, the use of this header
|
31
|
+
# is to re-enable it if it was disabled by the user.
|
32
32
|
#
|
33
33
|
module SecureHeaders
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
return super.merge(HEADERS)
|
34
|
+
# Internal: Sets the default HTTP secure headers.
|
35
|
+
def self.setup(app)
|
36
|
+
app.settings[:default_headers].update(
|
37
|
+
"X-Content-Type-Options" => "nosniff",
|
38
|
+
"X-Frame-Options" => "SAMEORIGIN",
|
39
|
+
"X-Permitted-Cross-Domain-Policies" => "none",
|
40
|
+
"X-XSS-Protection" => "1; mode=block"
|
41
|
+
)
|
43
42
|
end
|
44
43
|
end
|
45
44
|
end
|
data/lib/tynn/session.rb
CHANGED
@@ -1,87 +1,85 @@
|
|
1
1
|
class Tynn
|
2
|
-
# Adds simple cookie based session management.
|
3
|
-
#
|
4
|
-
# by unauthorized means.
|
2
|
+
# Public: Adds simple cookie based session management. You can pass a secret
|
3
|
+
# token to sign the cookie data, thus unauthorized means can't alter it.
|
5
4
|
#
|
6
|
-
#
|
7
|
-
# require "tynn"
|
8
|
-
# require "tynn/session"
|
5
|
+
# Examples
|
9
6
|
#
|
10
|
-
#
|
7
|
+
# require "tynn"
|
8
|
+
# require "tynn/session"
|
11
9
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
10
|
+
# Tynn.helpers(Tynn::Session, secret: "__change_me__")
|
11
|
+
#
|
12
|
+
# Tynn.define do
|
13
|
+
# root do
|
14
|
+
# res.write(sprintf("hei %s", session[:username]))
|
15
|
+
# end
|
16
16
|
#
|
17
|
-
#
|
18
|
-
#
|
17
|
+
# on(:username) do |username|
|
18
|
+
# session[:username] = username
|
19
|
+
# end
|
19
20
|
# end
|
20
|
-
# end
|
21
|
-
# ```
|
22
21
|
#
|
23
22
|
# The following command generates a cryptographically secure secret ready
|
24
23
|
# to use:
|
25
24
|
#
|
26
|
-
#
|
27
|
-
# $ ruby -r securerandom -e "puts SecureRandom.hex(64)"
|
28
|
-
# ```
|
25
|
+
# $ ruby -r securerandom -e "puts SecureRandom.hex(64)"
|
29
26
|
#
|
30
27
|
# It's important to keep the token secret. Knowing the token allows an
|
31
28
|
# attacker to tamper the data. So, it's recommended to load the token
|
32
29
|
# from the environment.
|
33
30
|
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
# Under the hood, Tynn::Session uses the
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
# [cookie-secure]: https://www.owasp.org/index.php/Session_Management_Cheat_Sheet#Secure_Attribute
|
65
|
-
# [rack-session]: http://www.rubydoc.info/gems/rack/Rack/Session/Cookie
|
31
|
+
# Examples
|
32
|
+
#
|
33
|
+
# Tynn.helpers(Tynn::Session, secret: ENV["SESSION_SECRET"])
|
34
|
+
#
|
35
|
+
# Under the hood, Tynn::Session uses the +Rack::Session::Cookie+ middleware.
|
36
|
+
# Thus, supports all the options available for this middleware:
|
37
|
+
#
|
38
|
+
# key - The name of the cookie. Defaults to <tt>"rack.session"</tt>.
|
39
|
+
#
|
40
|
+
# httponly - If +true+, sets the +HttpOnly+ flag. This mitigates the
|
41
|
+
# risk of client side scripting accessing the cookie. Defaults
|
42
|
+
# to +true+.
|
43
|
+
#
|
44
|
+
# secure - If +true+, sets the +Secure+ flag. This tells the browser
|
45
|
+
# to only transmit the cookie over HTTPS. Defaults to `false`.
|
46
|
+
#
|
47
|
+
# expire_after - The lifespan of the cookie. If +nil+, the session cookie
|
48
|
+
# is temporary and is no retained after the browser is
|
49
|
+
# closed. Defaults to +nil+.
|
50
|
+
#
|
51
|
+
# Examples
|
52
|
+
#
|
53
|
+
# Tynn.helpers(
|
54
|
+
# Tynn::Session,
|
55
|
+
# key: "app",
|
56
|
+
# secret: ENV["SESSION_SECRET"],
|
57
|
+
# expire_after: 36_000, # seconds
|
58
|
+
# httponly: true,
|
59
|
+
# secure: true
|
60
|
+
# )
|
66
61
|
#
|
67
62
|
module Session
|
68
|
-
|
63
|
+
# Internal: Configures Rack::Session::Cookie middleware.
|
64
|
+
def self.setup(app, options = {})
|
65
|
+
defaults = { secure: app.settings[:ssl] }
|
69
66
|
|
70
|
-
|
71
|
-
app.use(Rack::Session::Cookie, options)
|
67
|
+
app.use(Rack::Session::Cookie, defaults.merge(options))
|
72
68
|
end
|
73
69
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
70
|
+
module InstanceMethods
|
71
|
+
# Public: Returns the session hash.
|
72
|
+
#
|
73
|
+
# Examples
|
74
|
+
#
|
75
|
+
# session # => {}
|
76
|
+
#
|
77
|
+
# session[:foo] = "foo"
|
78
|
+
# session[:foo] # => "foo"
|
79
|
+
#
|
80
|
+
def session
|
81
|
+
return env["rack.session".freeze]
|
82
|
+
end
|
85
83
|
end
|
86
84
|
end
|
87
85
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Tynn
|
2
|
+
module Settings # :nodoc: all
|
3
|
+
def self.deepclone(hash)
|
4
|
+
default_proc, hash.default_proc = hash.default_proc, nil
|
5
|
+
|
6
|
+
return Marshal.load(Marshal.dump(hash))
|
7
|
+
ensure
|
8
|
+
hash.default_proc = default_proc
|
9
|
+
end
|
10
|
+
|
11
|
+
module InstanceMethods # :nodoc: all
|
12
|
+
def settings
|
13
|
+
return self.class.settings
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods # :nodoc: all
|
18
|
+
def inherited(subclass)
|
19
|
+
subclass.settings.replace(Tynn::Settings.deepclone(settings))
|
20
|
+
subclass.settings.default_proc = proc { |h, k| h[k] = settings[k] }
|
21
|
+
end
|
22
|
+
|
23
|
+
def settings
|
24
|
+
return @settings ||= {}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/tynn/static.rb
CHANGED
@@ -1,33 +1,36 @@
|
|
1
1
|
class Tynn
|
2
|
-
# Adds support for static files (javascript files, images,
|
2
|
+
# Public: Adds support for static files (javascript files, images,
|
3
|
+
# stylesheets, etc).
|
3
4
|
#
|
4
|
-
#
|
5
|
-
# require "tynn"
|
6
|
-
# require "tynn/static"
|
5
|
+
# Examples
|
7
6
|
#
|
8
|
-
#
|
9
|
-
#
|
7
|
+
# require "tynn"
|
8
|
+
# require "tynn/static"
|
10
9
|
#
|
11
|
-
#
|
12
|
-
# `public` in the current directory (e.g. `public/js/*`, `public/css/*`). You
|
13
|
-
# can change the default by passing the `:root` option.
|
10
|
+
# Tynn.helpers(Tynn::Static, ["/js", "/css"])
|
14
11
|
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
12
|
+
# By default, serves all requests beginning with the given paths from
|
13
|
+
# the folder +public+ in the current directory (e.g. +public/js/*+,
|
14
|
+
# +public/css/*+). You can change the default by passing the +:root+
|
15
|
+
# option.
|
18
16
|
#
|
19
|
-
#
|
20
|
-
# Thus, supports all the options available for this middleware.
|
17
|
+
# Examples
|
21
18
|
#
|
22
|
-
#
|
23
|
-
# Tynn.helpers(Tynn::Static, ["/js", "/css"], index: "index.html")
|
24
|
-
# ```
|
19
|
+
# Tynn.helpers(Tynn::Static, ["/js", "/css"], root: "assets")
|
25
20
|
#
|
26
|
-
#
|
21
|
+
# Under the hood, it uses the +Rack::Static+ middleware. Thus,
|
22
|
+
# supports all the options available by the middleware. Check
|
23
|
+
# {Rack::Static}[http://www.rubydoc.info/gems/rack/Rack/Static]
|
24
|
+
# for more information.
|
25
|
+
#
|
26
|
+
# Examples
|
27
|
+
#
|
28
|
+
# Tynn.helpers(Tynn::Static, ["/js", "/css"], index: "index.html")
|
27
29
|
#
|
28
30
|
module Static
|
29
|
-
|
30
|
-
|
31
|
+
# Internal: Configures Rack::Static middleware.
|
32
|
+
def self.setup(app, urls, opts = {})
|
33
|
+
options = opts.dup
|
31
34
|
|
32
35
|
options[:urls] ||= urls
|
33
36
|
options[:root] ||= File.expand_path("public", Dir.pwd)
|
data/lib/tynn/test.rb
CHANGED
@@ -1,50 +1,49 @@
|
|
1
1
|
require "rack/test"
|
2
2
|
|
3
3
|
class Tynn
|
4
|
-
# A simple helper class
|
5
|
-
# to your application.
|
4
|
+
# Public: A simple helper class to simulate requests to your application.
|
6
5
|
#
|
7
|
-
#
|
8
|
-
# require "tynn"
|
9
|
-
# require "tynn/test"
|
6
|
+
# Examples
|
10
7
|
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
# res.write("hei")
|
14
|
-
# end
|
15
|
-
# end
|
8
|
+
# require "tynn"
|
9
|
+
# require "tynn/test"
|
16
10
|
#
|
17
|
-
#
|
18
|
-
#
|
11
|
+
# Tynn.define do
|
12
|
+
# root do
|
13
|
+
# res.write("hei")
|
14
|
+
# end
|
15
|
+
# end
|
19
16
|
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
# ```
|
17
|
+
# app = Tynn::Test.new
|
18
|
+
# app.get("/")
|
23
19
|
#
|
24
|
-
#
|
25
|
-
#
|
20
|
+
# 200 == app.res.status # => true
|
21
|
+
# "hei" == app.res.body # => true
|
26
22
|
#
|
27
|
-
#
|
23
|
+
# In order to use this plugin, you need to install
|
24
|
+
# {rack-test}[https://rubygems.org/gems/rack-test].
|
28
25
|
#
|
29
26
|
class Test
|
30
27
|
include Rack::Test::Methods
|
31
28
|
|
32
|
-
#
|
29
|
+
# Internal: Returns the application class that handles the
|
30
|
+
# mock requests. Required by Rack::Test::Methods.
|
31
|
+
attr_reader :app
|
32
|
+
|
33
|
+
# Public: Initializes a new Tynn::Test object.
|
33
34
|
#
|
34
|
-
#
|
35
|
-
# class API < Tynn
|
36
|
-
# end
|
35
|
+
# app - The application class to test (default: Tynn).
|
37
36
|
#
|
38
|
-
#
|
39
|
-
# app.get("/json")
|
40
|
-
# ```
|
37
|
+
# Examples
|
41
38
|
#
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
39
|
+
# class API < Tynn
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# app = Tynn::Test.new(API)
|
43
|
+
# app.get("/json")
|
44
|
+
#
|
45
|
+
def initialize(app = Tynn)
|
46
|
+
@app = app
|
48
47
|
end
|
49
48
|
|
50
49
|
alias_method :res, :last_response
|
data/lib/tynn/version.rb
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
test "default headers" do
|
2
|
+
Tynn.set(:default_headers, "Content-Type" => "text/plain")
|
3
|
+
|
4
|
+
Tynn.define do
|
5
|
+
root do
|
6
|
+
res.write("hei")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
app = Tynn::Test.new(Tynn)
|
11
|
+
app.get("/")
|
12
|
+
|
13
|
+
assert_equal "text/plain", app.res.headers["Content-Type"]
|
14
|
+
end
|
data/test/environment_test.rb
CHANGED
@@ -1,31 +1,27 @@
|
|
1
1
|
require_relative "../lib/tynn/environment"
|
2
2
|
|
3
|
-
test "default" do
|
4
|
-
Tynn.helpers(Tynn::Environment)
|
5
|
-
|
6
|
-
assert_equal(:development, Tynn.environment)
|
7
|
-
end
|
8
|
-
|
9
|
-
test "helpers" do
|
10
|
-
Tynn.helpers(Tynn::Environment)
|
11
|
-
|
12
|
-
assert Tynn.development?
|
13
|
-
assert !Tynn.test?
|
14
|
-
assert !Tynn.production?
|
15
|
-
end
|
16
|
-
|
17
3
|
test "use RACK_ENV by default" do
|
18
|
-
|
4
|
+
begin
|
5
|
+
old, ENV["RACK_ENV"] = ENV["RACK_ENV"], "production"
|
6
|
+
|
7
|
+
Tynn.helpers(Tynn::Environment)
|
19
8
|
|
20
|
-
|
9
|
+
assert_equal(:production, Tynn.environment)
|
21
10
|
|
22
|
-
|
11
|
+
assert !Tynn.development?
|
12
|
+
assert !Tynn.test?
|
13
|
+
assert Tynn.production?
|
23
14
|
|
24
|
-
|
15
|
+
ensure
|
16
|
+
ENV["RACK_ENV"] = old
|
17
|
+
end
|
25
18
|
end
|
26
19
|
|
27
20
|
test "use custom value" do
|
28
21
|
Tynn.helpers(Tynn::Environment, env: "development")
|
29
22
|
|
30
23
|
assert_equal(:development, Tynn.environment)
|
24
|
+
assert Tynn.development?
|
25
|
+
assert !Tynn.test?
|
26
|
+
assert !Tynn.production?
|
31
27
|
end
|