rack-protection 1.5.3 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack-protection might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile +13 -0
- data/License +4 -1
- data/README.md +16 -2
- data/Rakefile +21 -5
- data/lib/rack/protection.rb +38 -24
- data/lib/rack/protection/authenticity_token.rb +107 -6
- data/lib/rack/protection/base.rb +5 -0
- data/lib/rack/protection/content_security_policy.rb +80 -0
- data/lib/rack/protection/cookie_tossing.rb +75 -0
- data/lib/rack/protection/escaped_params.rb +2 -0
- data/lib/rack/protection/form_token.rb +1 -1
- data/lib/rack/protection/http_origin.rb +8 -0
- data/lib/rack/protection/json_csrf.rb +26 -4
- data/lib/rack/protection/remote_token.rb +1 -1
- data/lib/rack/protection/strict_transport.rb +39 -0
- data/lib/rack/protection/version.rb +1 -12
- data/lib/rack/protection/xss_header.rb +1 -1
- data/rack-protection.gemspec +11 -104
- metadata +16 -81
- data/spec/authenticity_token_spec.rb +0 -48
- data/spec/base_spec.rb +0 -40
- data/spec/escaped_params_spec.rb +0 -43
- data/spec/form_token_spec.rb +0 -33
- data/spec/frame_options_spec.rb +0 -39
- data/spec/http_origin_spec.rb +0 -38
- data/spec/ip_spoofing_spec.rb +0 -35
- data/spec/json_csrf_spec.rb +0 -58
- data/spec/path_traversal_spec.rb +0 -41
- data/spec/protection_spec.rb +0 -105
- data/spec/remote_referrer_spec.rb +0 -31
- data/spec/remote_token_spec.rb +0 -42
- data/spec/session_hijacking_spec.rb +0 -55
- data/spec/spec_helper.rb +0 -163
- data/spec/xss_header_spec.rb +0 -56
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rack/protection'
|
2
2
|
require 'rack/utils'
|
3
|
+
require 'tempfile'
|
3
4
|
|
4
5
|
begin
|
5
6
|
require 'escape_utils'
|
@@ -66,6 +67,7 @@ module Rack
|
|
66
67
|
when Hash then escape_hash(object)
|
67
68
|
when Array then object.map { |o| escape(o) }
|
68
69
|
when String then escape_string(object)
|
70
|
+
when Tempfile then object
|
69
71
|
else nil
|
70
72
|
end
|
71
73
|
end
|
@@ -13,7 +13,7 @@ module Rack
|
|
13
13
|
# This middleware is not used when using the Rack::Protection collection,
|
14
14
|
# since it might be a security issue, depending on your application
|
15
15
|
#
|
16
|
-
# Compatible with
|
16
|
+
# Compatible with rack-csrf.
|
17
17
|
class FormToken < AuthenticityToken
|
18
18
|
def accepts?(env)
|
19
19
|
env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" or super
|
@@ -10,9 +10,16 @@ module Rack
|
|
10
10
|
#
|
11
11
|
# Does not accept unsafe HTTP requests when value of Origin HTTP request header
|
12
12
|
# does not match default or whitelisted URIs.
|
13
|
+
#
|
14
|
+
# If you want to whitelist a specific domain, you can pass in as the `:origin_whitelist` option:
|
15
|
+
#
|
16
|
+
# use Rack::Protection, origin_whitelist: ["http://localhost:3000", "http://127.0.01:3000"]
|
17
|
+
#
|
18
|
+
# The `:allow_if` option can also be set to a proc to use custom allow/deny logic.
|
13
19
|
class HttpOrigin < Base
|
14
20
|
DEFAULT_PORTS = { 'http' => 80, 'https' => 443, 'coffee' => 80 }
|
15
21
|
default_reaction :deny
|
22
|
+
default_options :allow_if => nil
|
16
23
|
|
17
24
|
def base_url(env)
|
18
25
|
request = Rack::Request.new(env)
|
@@ -24,6 +31,7 @@ module Rack
|
|
24
31
|
return true if safe? env
|
25
32
|
return true unless origin = env['HTTP_ORIGIN']
|
26
33
|
return true if base_url(env) == origin
|
34
|
+
return true if options[:allow_if] && options[:allow_if].call(env)
|
27
35
|
Array(options[:origin_whitelist]).include? origin
|
28
36
|
end
|
29
37
|
|
@@ -5,21 +5,30 @@ module Rack
|
|
5
5
|
##
|
6
6
|
# Prevented attack:: CSRF
|
7
7
|
# Supported browsers:: all
|
8
|
-
# More infos:: http://flask.pocoo.org/docs/security/#json-security
|
8
|
+
# More infos:: http://flask.pocoo.org/docs/0.10/security/#json-security
|
9
|
+
# http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
|
9
10
|
#
|
10
|
-
# JSON GET APIs are vulnerable to being embedded as JavaScript
|
11
|
+
# JSON GET APIs are vulnerable to being embedded as JavaScript when the
|
11
12
|
# Array prototype has been patched to track data. Checks the referrer
|
12
13
|
# even on GET requests if the content type is JSON.
|
14
|
+
#
|
15
|
+
# If request includes Origin HTTP header, defers to HttpOrigin to determine
|
16
|
+
# if the request is safe. Please refer to the documentation for more info.
|
17
|
+
#
|
18
|
+
# The `:allow_if` option can be set to a proc to use custom allow/deny logic.
|
13
19
|
class JsonCsrf < Base
|
20
|
+
default_options :allow_if => nil
|
21
|
+
|
14
22
|
alias react deny
|
15
23
|
|
16
24
|
def call(env)
|
17
25
|
request = Request.new(env)
|
18
26
|
status, headers, body = app.call(env)
|
19
27
|
|
20
|
-
if has_vector?
|
28
|
+
if has_vector?(request, headers)
|
21
29
|
warn env, "attack prevented by #{self.class}"
|
22
|
-
|
30
|
+
|
31
|
+
react_and_close(env, body) or [status, headers, body]
|
23
32
|
else
|
24
33
|
[status, headers, body]
|
25
34
|
end
|
@@ -27,9 +36,22 @@ module Rack
|
|
27
36
|
|
28
37
|
def has_vector?(request, headers)
|
29
38
|
return false if request.xhr?
|
39
|
+
return false if options[:allow_if] && options[:allow_if].call(request.env)
|
30
40
|
return false unless headers['Content-Type'].to_s.split(';', 2).first =~ /^\s*application\/json\s*$/
|
31
41
|
origin(request.env).nil? and referrer(request.env) != request.host
|
32
42
|
end
|
43
|
+
|
44
|
+
def react_and_close(env, body)
|
45
|
+
reaction = react(env)
|
46
|
+
|
47
|
+
close_body(body) if reaction
|
48
|
+
|
49
|
+
reaction
|
50
|
+
end
|
51
|
+
|
52
|
+
def close_body(body)
|
53
|
+
body.close if body.respond_to?(:close)
|
54
|
+
end
|
33
55
|
end
|
34
56
|
end
|
35
57
|
end
|
@@ -10,7 +10,7 @@ module Rack
|
|
10
10
|
# Only accepts unsafe HTTP requests if a given access token matches the token
|
11
11
|
# included in the session *or* the request comes from the same origin.
|
12
12
|
#
|
13
|
-
# Compatible with
|
13
|
+
# Compatible with rack-csrf.
|
14
14
|
class RemoteToken < AuthenticityToken
|
15
15
|
default_reaction :deny
|
16
16
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'rack/protection'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module Protection
|
5
|
+
##
|
6
|
+
# Prevented attack:: Protects against against protocol downgrade attacks and cookie hijacking.
|
7
|
+
# Supported browsers:: all
|
8
|
+
# More infos:: https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security
|
9
|
+
#
|
10
|
+
# browser will prevent any communications from being sent over HTTP
|
11
|
+
# to the specified domain and will instead send all communications over HTTPS.
|
12
|
+
# It also prevents HTTPS click through prompts on browsers.
|
13
|
+
#
|
14
|
+
# Options:
|
15
|
+
#
|
16
|
+
# max_age:: How long future requests to the domain should go over HTTPS; specified in seconds
|
17
|
+
# include_subdomains:: If all present and future subdomains will be HTTPS
|
18
|
+
# preload:: Allow this domain to be included in browsers HSTS preload list. See https://hstspreload.appspot.com/
|
19
|
+
|
20
|
+
class StrictTransport < Base
|
21
|
+
default_options :max_age => 31_536_000, :include_subdomains => false, :preload => false
|
22
|
+
|
23
|
+
def strict_transport
|
24
|
+
@strict_transport ||= begin
|
25
|
+
strict_transport = 'max-age=' + options[:max_age].to_s
|
26
|
+
strict_transport += '; includeSubDomains' if options[:include_subdomains]
|
27
|
+
strict_transport += '; preload' if options[:preload]
|
28
|
+
strict_transport.to_str
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def call(env)
|
33
|
+
status, headers, body = @app.call(env)
|
34
|
+
headers['Strict-Transport-Security'] ||= strict_transport
|
35
|
+
[status, headers, body]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -1,16 +1,5 @@
|
|
1
1
|
module Rack
|
2
2
|
module Protection
|
3
|
-
|
4
|
-
VERSION
|
5
|
-
end
|
6
|
-
|
7
|
-
SIGNATURE = [1, 5, 3]
|
8
|
-
VERSION = SIGNATURE.join('.')
|
9
|
-
|
10
|
-
VERSION.extend Comparable
|
11
|
-
def VERSION.<=>(other)
|
12
|
-
other = other.split('.').map { |i| i.to_i } if other.respond_to? :split
|
13
|
-
SIGNATURE <=> Array(other)
|
14
|
-
end
|
3
|
+
VERSION = '2.0.0'
|
15
4
|
end
|
16
5
|
end
|
@@ -4,7 +4,7 @@ module Rack
|
|
4
4
|
module Protection
|
5
5
|
##
|
6
6
|
# Prevented attack:: Non-permanent XSS
|
7
|
-
# Supported browsers:: Internet Explorer 8 and
|
7
|
+
# Supported browsers:: Internet Explorer 8+ and Chrome
|
8
8
|
# More infos:: http://blogs.msdn.com/b/ie/archive/2008/07/01/ie8-security-part-iv-the-xss-filter.aspx
|
9
9
|
#
|
10
10
|
# Sets X-XSS-Protection header to tell the browser to block attacks.
|
data/rack-protection.gemspec
CHANGED
@@ -1,118 +1,25 @@
|
|
1
|
-
|
1
|
+
version = File.read(File.expand_path("../../VERSION", __FILE__)).strip
|
2
|
+
|
2
3
|
Gem::Specification.new do |s|
|
3
4
|
# general infos
|
4
5
|
s.name = "rack-protection"
|
5
|
-
s.version =
|
6
|
-
s.description = "
|
7
|
-
s.homepage = "http://github.com/
|
6
|
+
s.version = version
|
7
|
+
s.description = "Protect against typical web attacks, works with all Rack apps, including Rails."
|
8
|
+
s.homepage = "http://github.com/sinatra/sinatra/tree/master/rack-protection"
|
8
9
|
s.summary = s.description
|
9
10
|
s.license = 'MIT'
|
10
|
-
|
11
|
-
|
12
|
-
s.
|
13
|
-
"Konstantin Haase",
|
14
|
-
"Alex Rodionov",
|
15
|
-
"Patrick Ellis",
|
16
|
-
"Jason Staten",
|
17
|
-
"ITO Nobuaki",
|
18
|
-
"Jeff Welling",
|
19
|
-
"Matteo Centenaro",
|
20
|
-
"Egor Homakov",
|
21
|
-
"Florian Gilcher",
|
22
|
-
"Fojas",
|
23
|
-
"Igor Bochkariov",
|
24
|
-
"Mael Clerambault",
|
25
|
-
"Martin Mauch",
|
26
|
-
"Renne Nissinen",
|
27
|
-
"SAKAI, Kazuaki",
|
28
|
-
"Stanislav Savulchik",
|
29
|
-
"Steve Agalloco",
|
30
|
-
"TOBY",
|
31
|
-
"Thais Camilo and Konstantin Haase",
|
32
|
-
"Vipul A M",
|
33
|
-
"Akzhan Abdulin",
|
34
|
-
"brookemckim",
|
35
|
-
"Bj\u{f8}rge N\u{e6}ss",
|
36
|
-
"Chris Heald",
|
37
|
-
"Chris Mytton",
|
38
|
-
"Corey Ward",
|
39
|
-
"Dario Cravero",
|
40
|
-
"David Kellum"
|
41
|
-
]
|
42
|
-
|
43
|
-
# generated from git shortlog -sne
|
44
|
-
s.email = [
|
45
|
-
"konstantin.mailinglists@googlemail.com",
|
46
|
-
"p0deje@gmail.com",
|
47
|
-
"jstaten07@gmail.com",
|
48
|
-
"patrick@soundcloud.com",
|
49
|
-
"jeff.welling@gmail.com",
|
50
|
-
"bugant@gmail.com",
|
51
|
-
"daydream.trippers@gmail.com",
|
52
|
-
"florian.gilcher@asquera.de",
|
53
|
-
"developer@fojasaur.us",
|
54
|
-
"ujifgc@gmail.com",
|
55
|
-
"mael@clerambault.fr",
|
56
|
-
"martin.mauch@gmail.com",
|
57
|
-
"rennex@iki.fi",
|
58
|
-
"kaz.july.7@gmail.com",
|
59
|
-
"s.savulchik@gmail.com",
|
60
|
-
"steve.agalloco@gmail.com",
|
61
|
-
"toby.net.info.mail+git@gmail.com",
|
62
|
-
"dev+narwen+rkh@rkh.im",
|
63
|
-
"vipulnsward@gmail.com",
|
64
|
-
"akzhan.abdulin@gmail.com",
|
65
|
-
"brooke@digitalocean.com",
|
66
|
-
"bjoerge@bengler.no",
|
67
|
-
"cheald@gmail.com",
|
68
|
-
"self@hecticjeff.net",
|
69
|
-
"coreyward@me.com",
|
70
|
-
"dario@uxtemple.com",
|
71
|
-
"dek-oss@gravitext.com",
|
72
|
-
"homakov@gmail.com"
|
73
|
-
]
|
74
|
-
|
75
|
-
# generated from git ls-files
|
76
|
-
s.files = [
|
11
|
+
s.authors = ["https://github.com/sinatra/sinatra/graphs/contributors"]
|
12
|
+
s.email = "sinatrarb@googlegroups.com"
|
13
|
+
s.files = Dir["lib/**/*.rb"] + [
|
77
14
|
"License",
|
78
15
|
"README.md",
|
79
16
|
"Rakefile",
|
80
|
-
"
|
81
|
-
"
|
82
|
-
"lib/rack/protection/authenticity_token.rb",
|
83
|
-
"lib/rack/protection/base.rb",
|
84
|
-
"lib/rack/protection/escaped_params.rb",
|
85
|
-
"lib/rack/protection/form_token.rb",
|
86
|
-
"lib/rack/protection/frame_options.rb",
|
87
|
-
"lib/rack/protection/http_origin.rb",
|
88
|
-
"lib/rack/protection/ip_spoofing.rb",
|
89
|
-
"lib/rack/protection/json_csrf.rb",
|
90
|
-
"lib/rack/protection/path_traversal.rb",
|
91
|
-
"lib/rack/protection/remote_referrer.rb",
|
92
|
-
"lib/rack/protection/remote_token.rb",
|
93
|
-
"lib/rack/protection/session_hijacking.rb",
|
94
|
-
"lib/rack/protection/version.rb",
|
95
|
-
"lib/rack/protection/xss_header.rb",
|
96
|
-
"rack-protection.gemspec",
|
97
|
-
"spec/authenticity_token_spec.rb",
|
98
|
-
"spec/base_spec.rb",
|
99
|
-
"spec/escaped_params_spec.rb",
|
100
|
-
"spec/form_token_spec.rb",
|
101
|
-
"spec/frame_options_spec.rb",
|
102
|
-
"spec/http_origin_spec.rb",
|
103
|
-
"spec/ip_spoofing_spec.rb",
|
104
|
-
"spec/json_csrf_spec.rb",
|
105
|
-
"spec/path_traversal_spec.rb",
|
106
|
-
"spec/protection_spec.rb",
|
107
|
-
"spec/remote_referrer_spec.rb",
|
108
|
-
"spec/remote_token_spec.rb",
|
109
|
-
"spec/session_hijacking_spec.rb",
|
110
|
-
"spec/spec_helper.rb",
|
111
|
-
"spec/xss_header_spec.rb"
|
17
|
+
"Gemfile",
|
18
|
+
"rack-protection.gemspec"
|
112
19
|
]
|
113
20
|
|
114
21
|
# dependencies
|
115
22
|
s.add_dependency "rack"
|
116
23
|
s.add_development_dependency "rack-test"
|
117
|
-
s.add_development_dependency "rspec", "~>
|
24
|
+
s.add_development_dependency "rspec", "~> 3.0.0"
|
118
25
|
end
|
metadata
CHANGED
@@ -1,41 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-protection
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
8
|
-
- Alex Rodionov
|
9
|
-
- Patrick Ellis
|
10
|
-
- Jason Staten
|
11
|
-
- ITO Nobuaki
|
12
|
-
- Jeff Welling
|
13
|
-
- Matteo Centenaro
|
14
|
-
- Egor Homakov
|
15
|
-
- Florian Gilcher
|
16
|
-
- Fojas
|
17
|
-
- Igor Bochkariov
|
18
|
-
- Mael Clerambault
|
19
|
-
- Martin Mauch
|
20
|
-
- Renne Nissinen
|
21
|
-
- SAKAI, Kazuaki
|
22
|
-
- Stanislav Savulchik
|
23
|
-
- Steve Agalloco
|
24
|
-
- TOBY
|
25
|
-
- Thais Camilo and Konstantin Haase
|
26
|
-
- Vipul A M
|
27
|
-
- Akzhan Abdulin
|
28
|
-
- brookemckim
|
29
|
-
- Bjørge Næss
|
30
|
-
- Chris Heald
|
31
|
-
- Chris Mytton
|
32
|
-
- Corey Ward
|
33
|
-
- Dario Cravero
|
34
|
-
- David Kellum
|
7
|
+
- https://github.com/sinatra/sinatra/graphs/contributors
|
35
8
|
autorequire:
|
36
9
|
bindir: bin
|
37
10
|
cert_chain: []
|
38
|
-
date:
|
11
|
+
date: 2017-05-07 00:00:00.000000000 Z
|
39
12
|
dependencies:
|
40
13
|
- !ruby/object:Gem::Dependency
|
41
14
|
name: rack
|
@@ -71,48 +44,22 @@ dependencies:
|
|
71
44
|
requirements:
|
72
45
|
- - "~>"
|
73
46
|
- !ruby/object:Gem::Version
|
74
|
-
version:
|
47
|
+
version: 3.0.0
|
75
48
|
type: :development
|
76
49
|
prerelease: false
|
77
50
|
version_requirements: !ruby/object:Gem::Requirement
|
78
51
|
requirements:
|
79
52
|
- - "~>"
|
80
53
|
- !ruby/object:Gem::Version
|
81
|
-
version:
|
82
|
-
description:
|
83
|
-
|
84
|
-
|
85
|
-
- p0deje@gmail.com
|
86
|
-
- jstaten07@gmail.com
|
87
|
-
- patrick@soundcloud.com
|
88
|
-
- jeff.welling@gmail.com
|
89
|
-
- bugant@gmail.com
|
90
|
-
- daydream.trippers@gmail.com
|
91
|
-
- florian.gilcher@asquera.de
|
92
|
-
- developer@fojasaur.us
|
93
|
-
- ujifgc@gmail.com
|
94
|
-
- mael@clerambault.fr
|
95
|
-
- martin.mauch@gmail.com
|
96
|
-
- rennex@iki.fi
|
97
|
-
- kaz.july.7@gmail.com
|
98
|
-
- s.savulchik@gmail.com
|
99
|
-
- steve.agalloco@gmail.com
|
100
|
-
- toby.net.info.mail+git@gmail.com
|
101
|
-
- dev+narwen+rkh@rkh.im
|
102
|
-
- vipulnsward@gmail.com
|
103
|
-
- akzhan.abdulin@gmail.com
|
104
|
-
- brooke@digitalocean.com
|
105
|
-
- bjoerge@bengler.no
|
106
|
-
- cheald@gmail.com
|
107
|
-
- self@hecticjeff.net
|
108
|
-
- coreyward@me.com
|
109
|
-
- dario@uxtemple.com
|
110
|
-
- dek-oss@gravitext.com
|
111
|
-
- homakov@gmail.com
|
54
|
+
version: 3.0.0
|
55
|
+
description: Protect against typical web attacks, works with all Rack apps, including
|
56
|
+
Rails.
|
57
|
+
email: sinatrarb@googlegroups.com
|
112
58
|
executables: []
|
113
59
|
extensions: []
|
114
60
|
extra_rdoc_files: []
|
115
61
|
files:
|
62
|
+
- Gemfile
|
116
63
|
- License
|
117
64
|
- README.md
|
118
65
|
- Rakefile
|
@@ -120,6 +67,8 @@ files:
|
|
120
67
|
- lib/rack/protection.rb
|
121
68
|
- lib/rack/protection/authenticity_token.rb
|
122
69
|
- lib/rack/protection/base.rb
|
70
|
+
- lib/rack/protection/content_security_policy.rb
|
71
|
+
- lib/rack/protection/cookie_tossing.rb
|
123
72
|
- lib/rack/protection/escaped_params.rb
|
124
73
|
- lib/rack/protection/form_token.rb
|
125
74
|
- lib/rack/protection/frame_options.rb
|
@@ -130,25 +79,11 @@ files:
|
|
130
79
|
- lib/rack/protection/remote_referrer.rb
|
131
80
|
- lib/rack/protection/remote_token.rb
|
132
81
|
- lib/rack/protection/session_hijacking.rb
|
82
|
+
- lib/rack/protection/strict_transport.rb
|
133
83
|
- lib/rack/protection/version.rb
|
134
84
|
- lib/rack/protection/xss_header.rb
|
135
85
|
- rack-protection.gemspec
|
136
|
-
|
137
|
-
- spec/base_spec.rb
|
138
|
-
- spec/escaped_params_spec.rb
|
139
|
-
- spec/form_token_spec.rb
|
140
|
-
- spec/frame_options_spec.rb
|
141
|
-
- spec/http_origin_spec.rb
|
142
|
-
- spec/ip_spoofing_spec.rb
|
143
|
-
- spec/json_csrf_spec.rb
|
144
|
-
- spec/path_traversal_spec.rb
|
145
|
-
- spec/protection_spec.rb
|
146
|
-
- spec/remote_referrer_spec.rb
|
147
|
-
- spec/remote_token_spec.rb
|
148
|
-
- spec/session_hijacking_spec.rb
|
149
|
-
- spec/spec_helper.rb
|
150
|
-
- spec/xss_header_spec.rb
|
151
|
-
homepage: http://github.com/rkh/rack-protection
|
86
|
+
homepage: http://github.com/sinatra/sinatra/tree/master/rack-protection
|
152
87
|
licenses:
|
153
88
|
- MIT
|
154
89
|
metadata: {}
|
@@ -168,9 +103,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
168
103
|
version: '0'
|
169
104
|
requirements: []
|
170
105
|
rubyforge_project:
|
171
|
-
rubygems_version: 2.
|
106
|
+
rubygems_version: 2.6.11
|
172
107
|
signing_key:
|
173
108
|
specification_version: 4
|
174
|
-
summary:
|
109
|
+
summary: Protect against typical web attacks, works with all Rack apps, including
|
110
|
+
Rails.
|
175
111
|
test_files: []
|
176
|
-
has_rdoc:
|