rails 4.1.16 → 4.2.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/guides/CHANGELOG.md +13 -102
- data/guides/Rakefile +2 -2
- data/guides/assets/javascripts/guides.js +6 -0
- data/guides/assets/stylesheets/main.css +4 -1
- data/guides/bug_report_templates/action_controller_gem.rb +2 -2
- data/guides/bug_report_templates/action_controller_master.rb +5 -2
- data/guides/bug_report_templates/active_record_master.rb +2 -0
- data/guides/rails_guides.rb +2 -2
- data/guides/rails_guides/helpers.rb +1 -1
- data/guides/rails_guides/levenshtein.rb +29 -21
- data/guides/rails_guides/markdown.rb +6 -7
- data/guides/rails_guides/markdown/renderer.rb +1 -1
- data/guides/source/2_3_release_notes.md +3 -3
- data/guides/source/3_0_release_notes.md +4 -4
- data/guides/source/3_1_release_notes.md +2 -2
- data/guides/source/3_2_release_notes.md +2 -2
- data/guides/source/4_1_release_notes.md +8 -9
- data/guides/source/4_2_release_notes.md +572 -0
- data/guides/source/_license.html.erb +1 -1
- data/guides/source/_welcome.html.erb +2 -8
- data/guides/source/action_controller_overview.md +79 -7
- data/guides/source/action_mailer_basics.md +36 -11
- data/guides/source/action_view_overview.md +138 -119
- data/guides/source/active_job_basics.md +253 -0
- data/guides/source/active_model_basics.md +23 -0
- data/guides/source/active_record_basics.md +16 -15
- data/guides/source/active_record_callbacks.md +12 -9
- data/guides/source/{migrations.md → active_record_migrations.md} +90 -217
- data/guides/source/active_record_postgresql.md +437 -0
- data/guides/source/active_record_querying.md +261 -261
- data/guides/source/active_record_validations.md +7 -7
- data/guides/source/active_support_core_extensions.md +105 -44
- data/guides/source/active_support_instrumentation.md +3 -2
- data/guides/source/api_documentation_guidelines.md +62 -16
- data/guides/source/asset_pipeline.md +58 -46
- data/guides/source/association_basics.md +47 -38
- data/guides/source/caching_with_rails.md +31 -6
- data/guides/source/command_line.md +56 -25
- data/guides/source/configuring.md +98 -19
- data/guides/source/contributing_to_ruby_on_rails.md +174 -111
- data/guides/source/credits.html.erb +1 -1
- data/guides/source/debugging_rails_applications.md +438 -284
- data/guides/source/development_dependencies_install.md +17 -4
- data/guides/source/documents.yaml +11 -7
- data/guides/source/engines.md +192 -203
- data/guides/source/form_helpers.md +54 -45
- data/guides/source/generators.md +20 -11
- data/guides/source/getting_started.md +330 -191
- data/guides/source/i18n.md +92 -62
- data/guides/source/index.html.erb +1 -0
- data/guides/source/initialization.md +108 -59
- data/guides/source/layout.html.erb +1 -4
- data/guides/source/layouts_and_rendering.md +24 -23
- data/guides/source/nested_model_forms.md +3 -3
- data/guides/source/plugins.md +26 -26
- data/guides/source/rails_application_templates.md +21 -3
- data/guides/source/rails_on_rack.md +1 -1
- data/guides/source/routing.md +97 -71
- data/guides/source/ruby_on_rails_guides_guidelines.md +10 -12
- data/guides/source/security.md +39 -33
- data/guides/source/testing.md +111 -108
- data/guides/source/upgrading_ruby_on_rails.md +131 -14
- data/guides/source/working_with_javascript_in_rails.md +18 -16
- data/guides/w3c_validator.rb +2 -0
- metadata +37 -94
- data/guides/bug_report_templates/generic_gem.rb +0 -15
- data/guides/bug_report_templates/generic_master.rb +0 -26
- data/guides/code/getting_started/Gemfile +0 -40
- data/guides/code/getting_started/Gemfile.lock +0 -125
- data/guides/code/getting_started/README.rdoc +0 -28
- data/guides/code/getting_started/Rakefile +0 -6
- data/guides/code/getting_started/app/assets/javascripts/application.js +0 -15
- data/guides/code/getting_started/app/assets/javascripts/comments.js.coffee +0 -3
- data/guides/code/getting_started/app/assets/javascripts/posts.js.coffee +0 -3
- data/guides/code/getting_started/app/assets/javascripts/welcome.js.coffee +0 -3
- data/guides/code/getting_started/app/assets/stylesheets/application.css +0 -13
- data/guides/code/getting_started/app/assets/stylesheets/comments.css.scss +0 -3
- data/guides/code/getting_started/app/assets/stylesheets/posts.css.scss +0 -3
- data/guides/code/getting_started/app/assets/stylesheets/welcome.css.scss +0 -3
- data/guides/code/getting_started/app/controllers/application_controller.rb +0 -5
- data/guides/code/getting_started/app/controllers/comments_controller.rb +0 -23
- data/guides/code/getting_started/app/controllers/posts_controller.rb +0 -53
- data/guides/code/getting_started/app/controllers/welcome_controller.rb +0 -4
- data/guides/code/getting_started/app/helpers/application_helper.rb +0 -2
- data/guides/code/getting_started/app/helpers/comments_helper.rb +0 -2
- data/guides/code/getting_started/app/helpers/posts_helper.rb +0 -2
- data/guides/code/getting_started/app/helpers/welcome_helper.rb +0 -2
- data/guides/code/getting_started/app/models/comment.rb +0 -3
- data/guides/code/getting_started/app/models/post.rb +0 -7
- data/guides/code/getting_started/app/views/comments/_comment.html.erb +0 -15
- data/guides/code/getting_started/app/views/comments/_form.html.erb +0 -13
- data/guides/code/getting_started/app/views/layouts/application.html.erb +0 -14
- data/guides/code/getting_started/app/views/posts/_form.html.erb +0 -27
- data/guides/code/getting_started/app/views/posts/edit.html.erb +0 -5
- data/guides/code/getting_started/app/views/posts/index.html.erb +0 -21
- data/guides/code/getting_started/app/views/posts/new.html.erb +0 -5
- data/guides/code/getting_started/app/views/posts/show.html.erb +0 -18
- data/guides/code/getting_started/app/views/welcome/index.html.erb +0 -4
- data/guides/code/getting_started/bin/bundle +0 -4
- data/guides/code/getting_started/bin/rails +0 -4
- data/guides/code/getting_started/bin/rake +0 -4
- data/guides/code/getting_started/config.ru +0 -4
- data/guides/code/getting_started/config/application.rb +0 -18
- data/guides/code/getting_started/config/boot.rb +0 -4
- data/guides/code/getting_started/config/database.yml +0 -25
- data/guides/code/getting_started/config/environment.rb +0 -5
- data/guides/code/getting_started/config/environments/development.rb +0 -30
- data/guides/code/getting_started/config/environments/production.rb +0 -80
- data/guides/code/getting_started/config/environments/test.rb +0 -36
- data/guides/code/getting_started/config/initializers/backtrace_silencers.rb +0 -7
- data/guides/code/getting_started/config/initializers/filter_parameter_logging.rb +0 -4
- data/guides/code/getting_started/config/initializers/inflections.rb +0 -16
- data/guides/code/getting_started/config/initializers/locale.rb +0 -9
- data/guides/code/getting_started/config/initializers/mime_types.rb +0 -5
- data/guides/code/getting_started/config/initializers/secret_token.rb +0 -12
- data/guides/code/getting_started/config/initializers/session_store.rb +0 -3
- data/guides/code/getting_started/config/initializers/wrap_parameters.rb +0 -14
- data/guides/code/getting_started/config/locales/en.yml +0 -23
- data/guides/code/getting_started/config/routes.rb +0 -7
- data/guides/code/getting_started/db/migrate/20130122042648_create_posts.rb +0 -10
- data/guides/code/getting_started/db/migrate/20130122045842_create_comments.rb +0 -11
- data/guides/code/getting_started/db/schema.rb +0 -33
- data/guides/code/getting_started/db/seeds.rb +0 -7
- data/guides/code/getting_started/public/404.html +0 -60
- data/guides/code/getting_started/public/422.html +0 -60
- data/guides/code/getting_started/public/500.html +0 -59
- data/guides/code/getting_started/public/favicon.ico +0 -0
- data/guides/code/getting_started/public/robots.txt +0 -5
- data/guides/code/getting_started/test/controllers/comments_controller_test.rb +0 -7
- data/guides/code/getting_started/test/controllers/posts_controller_test.rb +0 -7
- data/guides/code/getting_started/test/controllers/welcome_controller_test.rb +0 -9
- data/guides/code/getting_started/test/fixtures/comments.yml +0 -11
- data/guides/code/getting_started/test/fixtures/posts.yml +0 -9
- data/guides/code/getting_started/test/helpers/comments_helper_test.rb +0 -4
- data/guides/code/getting_started/test/helpers/posts_helper_test.rb +0 -4
- data/guides/code/getting_started/test/helpers/welcome_helper_test.rb +0 -4
- data/guides/code/getting_started/test/models/comment_test.rb +0 -7
- data/guides/code/getting_started/test/models/post_test.rb +0 -7
- data/guides/code/getting_started/test/test_helper.rb +0 -12
@@ -13,17 +13,17 @@ After reading this guide, you will know:
|
|
13
13
|
Markdown
|
14
14
|
-------
|
15
15
|
|
16
|
-
Guides are written in [GitHub Flavored Markdown](
|
16
|
+
Guides are written in [GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown). There is comprehensive [documentation for Markdown](http://daringfireball.net/projects/markdown/syntax), as well as a [cheatsheet](http://daringfireball.net/projects/markdown/basics).
|
17
17
|
|
18
18
|
Prologue
|
19
19
|
--------
|
20
20
|
|
21
|
-
Each guide should start with motivational text at the top (that's the little introduction in the blue area). The prologue should tell the reader what the guide is about, and what they will learn.
|
21
|
+
Each guide should start with motivational text at the top (that's the little introduction in the blue area). The prologue should tell the reader what the guide is about, and what they will learn. As an example, see the [Routing Guide](routing.html).
|
22
22
|
|
23
|
-
|
23
|
+
Headings
|
24
24
|
------
|
25
25
|
|
26
|
-
The title of every guide uses `h1
|
26
|
+
The title of every guide uses an `h1` heading; guide sections use `h2` headings; subsections use `h3` headings; etc. Note that the generated HTML output will use heading tags starting with `<h2>`.
|
27
27
|
|
28
28
|
```
|
29
29
|
Guide Title
|
@@ -35,14 +35,14 @@ Section
|
|
35
35
|
### Sub Section
|
36
36
|
```
|
37
37
|
|
38
|
-
|
38
|
+
When writing headings, capitalize all words except for prepositions, conjunctions, internal articles, and forms of the verb "to be":
|
39
39
|
|
40
40
|
```
|
41
41
|
#### Middleware Stack is an Array
|
42
42
|
#### When are Objects Saved?
|
43
43
|
```
|
44
44
|
|
45
|
-
Use the same
|
45
|
+
Use the same inline formatting as regular text:
|
46
46
|
|
47
47
|
```
|
48
48
|
##### The `:content_type` Option
|
@@ -51,25 +51,23 @@ Use the same typography as in regular text:
|
|
51
51
|
API Documentation Guidelines
|
52
52
|
----------------------------
|
53
53
|
|
54
|
-
The guides and the API should be coherent and consistent where appropriate.
|
54
|
+
The guides and the API should be coherent and consistent where appropriate. In particular, these sections of the [API Documentation Guidelines](api_documentation_guidelines.html) also apply to the guides:
|
55
55
|
|
56
56
|
* [Wording](api_documentation_guidelines.html#wording)
|
57
57
|
* [Example Code](api_documentation_guidelines.html#example-code)
|
58
|
-
* [Filenames](api_documentation_guidelines.html#
|
58
|
+
* [Filenames](api_documentation_guidelines.html#file-names)
|
59
59
|
* [Fonts](api_documentation_guidelines.html#fonts)
|
60
60
|
|
61
|
-
Those guidelines apply also to guides.
|
62
|
-
|
63
61
|
HTML Guides
|
64
62
|
-----------
|
65
63
|
|
66
64
|
Before generating the guides, make sure that you have the latest version of Bundler installed on your system. As of this writing, you must install Bundler 1.3.5 on your device.
|
67
65
|
|
68
|
-
To install the latest version of Bundler,
|
66
|
+
To install the latest version of Bundler, run `gem install bundler`.
|
69
67
|
|
70
68
|
### Generation
|
71
69
|
|
72
|
-
To generate all the guides, just `cd` into the `guides` directory, run `bundle install
|
70
|
+
To generate all the guides, just `cd` into the `guides` directory, run `bundle install`, and execute:
|
73
71
|
|
74
72
|
```
|
75
73
|
bundle exec rake guides:generate
|
data/guides/source/security.md
CHANGED
@@ -17,7 +17,7 @@ After reading this guide, you will know:
|
|
17
17
|
Introduction
|
18
18
|
------------
|
19
19
|
|
20
|
-
Web application frameworks are made to help developers build web applications. Some of them also help you with securing the web application. In fact one framework is not more secure than another: If you use it correctly, you will be able to build secure apps with many frameworks. Ruby on Rails has some clever helper methods, for example against SQL injection, so that this is hardly a problem.
|
20
|
+
Web application frameworks are made to help developers build web applications. Some of them also help you with securing the web application. In fact one framework is not more secure than another: If you use it correctly, you will be able to build secure apps with many frameworks. Ruby on Rails has some clever helper methods, for example against SQL injection, so that this is hardly a problem.
|
21
21
|
|
22
22
|
In general there is no such thing as plug-n-play security. Security depends on the people using the framework, and sometimes on the development method. And it depends on all layers of a web application environment: The back-end storage, the web server and the web application itself (and possibly other layers or applications).
|
23
23
|
|
@@ -25,7 +25,7 @@ The Gartner Group however estimates that 75% of attacks are at the web applicati
|
|
25
25
|
|
26
26
|
The threats against web applications include user account hijacking, bypass of access control, reading or modifying sensitive data, or presenting fraudulent content. Or an attacker might be able to install a Trojan horse program or unsolicited e-mail sending software, aim at financial enrichment or cause brand name damage by modifying company resources. In order to prevent attacks, minimize their impact and remove points of attack, first of all, you have to fully understand the attack methods in order to find the correct countermeasures. That is what this guide aims at.
|
27
27
|
|
28
|
-
In order to develop secure web applications you have to keep up to date on all layers and know your enemies. To keep up to date subscribe to security mailing lists, read security blogs and make updating and security checks a habit (check the
|
28
|
+
In order to develop secure web applications you have to keep up to date on all layers and know your enemies. To keep up to date subscribe to security mailing lists, read security blogs and make updating and security checks a habit (check the [Additional Resources](#additional-resources) chapter). It is done manually because that's how you find the nasty logical security problems.
|
29
29
|
|
30
30
|
Sessions
|
31
31
|
--------
|
@@ -60,7 +60,7 @@ Many web applications have an authentication system: a user provides a user name
|
|
60
60
|
|
61
61
|
Hence, the cookie serves as temporary authentication for the web application. Anyone who seizes a cookie from someone else, may use the web application as this user - with possibly severe consequences. Here are some ways to hijack a session, and their countermeasures:
|
62
62
|
|
63
|
-
* Sniff the cookie in an insecure network. A wireless LAN can be an example of such a network. In an unencrypted wireless LAN it is especially easy to listen to the traffic of all connected clients.
|
63
|
+
* Sniff the cookie in an insecure network. A wireless LAN can be an example of such a network. In an unencrypted wireless LAN it is especially easy to listen to the traffic of all connected clients. For the web application builder this means to _provide a secure connection over SSL_. In Rails 3.1 and later, this could be accomplished by always forcing SSL connection in your application config file:
|
64
64
|
|
65
65
|
```ruby
|
66
66
|
config.force_ssl = true
|
@@ -68,7 +68,7 @@ Hence, the cookie serves as temporary authentication for the web application. An
|
|
68
68
|
|
69
69
|
* Most people don't clear out the cookies after working at a public terminal. So if the last user didn't log out of a web application, you would be able to use it as this user. Provide the user with a _log-out button_ in the web application, and _make it prominent_.
|
70
70
|
|
71
|
-
* Many cross-site scripting (XSS) exploits aim at obtaining the user's cookie. You'll read
|
71
|
+
* Many cross-site scripting (XSS) exploits aim at obtaining the user's cookie. You'll read [more about XSS](#cross-site-scripting-xss) later.
|
72
72
|
|
73
73
|
* Instead of stealing a cookie unknown to the attacker, they fix a user's session identifier (in the cookie) known to them. Read more about this so-called session fixation later.
|
74
74
|
|
@@ -95,9 +95,16 @@ Rails 2 introduced a new default session storage, CookieStore. CookieStore saves
|
|
95
95
|
|
96
96
|
That means the security of this storage depends on this secret (and on the digest algorithm, which defaults to SHA1, for compatibility). So _don't use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters_.
|
97
97
|
|
98
|
-
`
|
98
|
+
`secrets.secret_key_base` is used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get `secrets.secret_key_base` initialized to a random key present in `config/secrets.yml`, e.g.:
|
99
99
|
|
100
|
-
|
100
|
+
development:
|
101
|
+
secret_key_base: a75d...
|
102
|
+
|
103
|
+
test:
|
104
|
+
secret_key_base: 492f...
|
105
|
+
|
106
|
+
production:
|
107
|
+
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
|
101
108
|
|
102
109
|
Older versions of Rails use CookieStore, which uses `secret_token` instead of `secret_key_base` that is used by EncryptedCookieStore. Read the upgrade documentation for more information.
|
103
110
|
|
@@ -111,9 +118,9 @@ It works like this:
|
|
111
118
|
|
112
119
|
* A user receives credits, the amount is stored in a session (which is a bad idea anyway, but we'll do this for demonstration purposes).
|
113
120
|
* The user buys something.
|
114
|
-
*
|
115
|
-
* The
|
116
|
-
* The user has their credit back.
|
121
|
+
* The new adjusted credit value is stored in the session.
|
122
|
+
* The user takes the cookie from the first step (which they previously copied) and replaces the current cookie in the browser.
|
123
|
+
* The user has their original credit back.
|
117
124
|
|
118
125
|
Including a nonce (a random value) in the session solves replay attacks. A nonce is valid only once, and the server has to keep track of all the valid nonces. It gets even more complicated if you have several application servers (mongrels). Storing nonces in a database table would defeat the entire purpose of CookieStore (avoiding accessing the database).
|
119
126
|
|
@@ -128,8 +135,8 @@ NOTE: _Apart from stealing a user's session id, the attacker may fix a session i
|
|
128
135
|
This attack focuses on fixing a user's session id known to the attacker, and forcing the user's browser into using this id. It is therefore not necessary for the attacker to steal the session id afterwards. Here is how this attack works:
|
129
136
|
|
130
137
|
* The attacker creates a valid session id: They load the login page of the web application where they want to fix the session, and take the session id in the cookie from the response (see number 1 and 2 in the image).
|
131
|
-
* They
|
132
|
-
*
|
138
|
+
* They maintain the session by accessing the web application periodically in order to keep an expiring session alive.
|
139
|
+
* The attacker forces the user's browser into using this session id (see number 3 in the image). As you may not change a cookie of another domain (because of the same origin policy), the attacker has to run a JavaScript from the domain of the target web application. Injecting the JavaScript code into the application by XSS accomplishes this attack. Here is an example: `<script>document.cookie="_session_id=16d5b78abb28e3d6206b60f22a03c8d9";</script>`. Read more about XSS and injection later on.
|
133
140
|
* The attacker lures the victim to the infected page with the JavaScript code. By viewing the page, the victim's browser will change the session id to the trap session id.
|
134
141
|
* As the new trap session is unused, the web application will require the user to authenticate.
|
135
142
|
* From now on, the victim and the attacker will co-use the web application with the same session: The session became valid and the victim didn't notice the attack.
|
@@ -144,7 +151,7 @@ The most effective countermeasure is to _issue a new session identifier_ and dec
|
|
144
151
|
reset_session
|
145
152
|
```
|
146
153
|
|
147
|
-
If you use the popular RestfulAuthentication plugin for user management, add
|
154
|
+
If you use the popular RestfulAuthentication plugin for user management, add reset_session to the SessionsController#create action. Note that this removes any value from the session, _you have to transfer them to the new session_.
|
148
155
|
|
149
156
|
Another countermeasure is to _save user-specific properties in the session_, verify them every time a request comes in, and deny access, if the information does not match. Such properties could be the remote IP address or the user agent (the web browser name), though the latter is less user-specific. When saving the IP address, you have to bear in mind that there are Internet service providers or large organizations that put their users behind proxies. _These might change over the course of a session_, so these users will not be able to use your application, or only in a limited way.
|
150
157
|
|
@@ -180,7 +187,7 @@ This attack method works by including malicious code or a link in a page that ac
|
|
180
187
|
|
181
188
|
![](images/csrf.png)
|
182
189
|
|
183
|
-
In the
|
190
|
+
In the [session chapter](#sessions) you have learned that most Rails applications use cookie-based sessions. Either they store the session id in the cookie and have a server-side session hash, or the entire session hash is on the client-side. In either case the browser will automatically send along the cookie on every request to a domain, if it can find a cookie for that domain. The controversial point is, that it will also send the cookie, if the request comes from a site of a different domain. Let's start with an example:
|
184
191
|
|
185
192
|
* Bob browses a message board and views a post from a hacker where there is a crafted HTML image element. The element references a command in Bob's project management application, rather than an image file.
|
186
193
|
* `<img src="http://www.webapp.com/project/1/destroy">`
|
@@ -191,7 +198,7 @@ In the <a href="#sessions">session chapter</a> you have learned that most Rails
|
|
191
198
|
|
192
199
|
It is important to notice that the actual crafted image or link doesn't necessarily have to be situated in the web application's domain, it can be anywhere - in a forum, blog post or email.
|
193
200
|
|
194
|
-
CSRF appears very rarely in CVE (Common Vulnerabilities and Exposures) - less than 0.1% in 2006 - but it really is a 'sleeping giant' [Grossman]. This is in stark contrast to the results in
|
201
|
+
CSRF appears very rarely in CVE (Common Vulnerabilities and Exposures) - less than 0.1% in 2006 - but it really is a 'sleeping giant' [Grossman]. This is in stark contrast to the results in many security contract works - _CSRF is an important security issue_.
|
195
202
|
|
196
203
|
### CSRF Countermeasures
|
197
204
|
|
@@ -232,26 +239,25 @@ Or the attacker places the code into the onmouseover event handler of an image:
|
|
232
239
|
|
233
240
|
There are many other possibilities, like using a `<script>` tag to make a cross-site request to a URL with a JSONP or JavaScript response. The response is executable code that the attacker can find a way to run, possibly extracting sensitive data. To protect against this data leakage, we disallow cross-site `<script>` tags. Only Ajax requests may have JavaScript responses since XmlHttpRequest is subject to the browser Same-Origin policy - meaning only your site can initiate the request.
|
234
241
|
|
235
|
-
To protect against all other forged requests, we introduce a _required security token_ that our site knows but other sites don't know. We include the security token in requests and verify it on the server. This is a one-liner in your application controller:
|
242
|
+
To protect against all other forged requests, we introduce a _required security token_ that our site knows but other sites don't know. We include the security token in requests and verify it on the server. This is a one-liner in your application controller, and is the default for newly created rails applications:
|
236
243
|
|
237
244
|
```ruby
|
238
|
-
protect_from_forgery
|
245
|
+
protect_from_forgery with: :exception
|
239
246
|
```
|
240
247
|
|
241
|
-
This will automatically include a security token in all forms and Ajax requests generated by Rails. If the security token doesn't match what was expected,
|
248
|
+
This will automatically include a security token in all forms and Ajax requests generated by Rails. If the security token doesn't match what was expected, an exception will be thrown.
|
242
249
|
|
243
250
|
It is common to use persistent cookies to store user information, with `cookies.permanent` for example. In this case, the cookies will not be cleared and the out of the box CSRF protection will not be effective. If you are using a different cookie store than the session for this information, you must handle what to do with it yourself:
|
244
251
|
|
245
252
|
```ruby
|
246
|
-
|
247
|
-
|
248
|
-
sign_out_user # Example method that will destroy the user cookies.
|
253
|
+
rescue_from ActionController::InvalidAuthenticityToken do |exception|
|
254
|
+
sign_out_user # Example method that will destroy the user cookies
|
249
255
|
end
|
250
256
|
```
|
251
257
|
|
252
|
-
The above method can be placed in the `ApplicationController` and will be called when a CSRF token is not present on a non-GET request.
|
258
|
+
The above method can be placed in the `ApplicationController` and will be called when a CSRF token is not present or is incorrect on a non-GET request.
|
253
259
|
|
254
|
-
Note that _cross-site scripting (XSS) vulnerabilities bypass all CSRF protections_. XSS gives the attacker access to all elements on a page, so they can read the CSRF security token from a form or directly submit the form. Read
|
260
|
+
Note that _cross-site scripting (XSS) vulnerabilities bypass all CSRF protections_. XSS gives the attacker access to all elements on a page, so they can read the CSRF security token from a form or directly submit the form. Read [more about XSS](#cross-site-scripting-xss) later.
|
255
261
|
|
256
262
|
Redirection and Files
|
257
263
|
---------------------
|
@@ -307,7 +313,7 @@ def sanitize_filename(filename)
|
|
307
313
|
end
|
308
314
|
```
|
309
315
|
|
310
|
-
A significant disadvantage of synchronous processing of file uploads (as the
|
316
|
+
A significant disadvantage of synchronous processing of file uploads (as the attachment_fu plugin may do with images), is its _vulnerability to denial-of-service attacks_. An attacker can synchronously start image file uploads from many computers which increases the server load and may eventually crash or stall the server.
|
311
317
|
|
312
318
|
The solution to this is best to _process media files asynchronously_: Save the media file and schedule a processing request in the database. A second process will handle the processing of the file in the background.
|
313
319
|
|
@@ -356,7 +362,7 @@ Refer to the Injection section for countermeasures against XSS. It is _recommend
|
|
356
362
|
|
357
363
|
**CSRF** Cross-Site Request Forgery (CSRF), also known as Cross-Site Reference Forgery (XSRF), is a gigantic attack method, it allows the attacker to do everything the administrator or Intranet user may do. As you have already seen above how CSRF works, here are a few examples of what attackers can do in the Intranet or admin interface.
|
358
364
|
|
359
|
-
A real-world example is a [router reconfiguration by CSRF](http://www.h-online.com/security/
|
365
|
+
A real-world example is a [router reconfiguration by CSRF](http://www.h-online.com/security/Symantec-reports-first-active-attack-on-a-DSL-router--/news/102352). The attackers sent a malicious e-mail, with CSRF in it, to Mexican users. The e-mail claimed there was an e-card waiting for them, but it also contained an image tag that resulted in a HTTP-GET request to reconfigure the user's router (which is a popular model in Mexico). The request changed the DNS-settings so that requests to a Mexico-based banking site would be mapped to the attacker's site. Everyone who accessed the banking site through that router saw the attacker's fake web site and had their credentials stolen.
|
360
366
|
|
361
367
|
Another example changed Google Adsense's e-mail address and password by. If the victim was logged into Google Adsense, the administration interface for Google advertisements campaigns, an attacker could change their credentials.
|
362
368
|
|
@@ -368,7 +374,7 @@ For _countermeasures against CSRF in administration interfaces and Intranet appl
|
|
368
374
|
|
369
375
|
The common admin interface works like this: it's located at www.example.com/admin, may be accessed only if the admin flag is set in the User model, re-displays user input and allows the admin to delete/add/edit whatever data desired. Here are some thoughts about this:
|
370
376
|
|
371
|
-
* It is very important to _think about the worst case_: What if someone really got hold of
|
377
|
+
* It is very important to _think about the worst case_: What if someone really got hold of your cookies or user credentials. You could _introduce roles_ for the admin interface to limit the possibilities of the attacker. Or how about _special login credentials_ for the admin interface, other than the ones used for the public part of the application. Or a _special password for very serious actions_?
|
372
378
|
|
373
379
|
* Does the admin really have to access the interface from everywhere in the world? Think about _limiting the login to a bunch of source IP addresses_. Examine request.remote_ip to find out about the user's IP address. This is not bullet-proof, but a great barrier. Remember that there might be a proxy in use, though.
|
374
380
|
|
@@ -400,7 +406,7 @@ If the parameter was nil, the resulting SQL query will be
|
|
400
406
|
SELECT * FROM users WHERE (users.activation_code IS NULL) LIMIT 1
|
401
407
|
```
|
402
408
|
|
403
|
-
And thus it found the first user in the database, returned it and logged them in. You can find out more about it in [
|
409
|
+
And thus it found the first user in the database, returned it and logged them in. You can find out more about it in [this blog post](http://www.rorsecurity.info/2007/10/28/restful_authentication-login-security/). _It is advisable to update your plug-ins from time to time_. Moreover, you can review your application to find more flaws like this.
|
404
410
|
|
405
411
|
### Brute-Forcing Accounts
|
406
412
|
|
@@ -471,7 +477,7 @@ config.filter_parameters << :password
|
|
471
477
|
|
472
478
|
INFO: _Do you find it hard to remember all your passwords? Don't write them down, but use the initial letters of each word in an easy to remember sentence._
|
473
479
|
|
474
|
-
Bruce Schneier, a security technologist, [has analyzed](http://www.schneier.com/blog/archives/2006/12/realworld_passw.html) 34,000 real-world user names and passwords from the MySpace phishing attack mentioned
|
480
|
+
Bruce Schneier, a security technologist, [has analyzed](http://www.schneier.com/blog/archives/2006/12/realworld_passw.html) 34,000 real-world user names and passwords from the MySpace phishing attack mentioned [below](#examples-from-the-underground). It turns out that most of the passwords are quite easy to crack. The 20 most common passwords are:
|
475
481
|
|
476
482
|
password1, abc123, myspace1, password, blink182, qwerty1, ****you, 123abc, baseball1, football1, 123456, soccer, monkey1, liverpool1, princess1, jordan23, slipknot1, superman1, iloveyou1, and monkey.
|
477
483
|
|
@@ -624,7 +630,7 @@ Also, the second query renames some columns with the AS statement so that the we
|
|
624
630
|
|
625
631
|
#### Countermeasures
|
626
632
|
|
627
|
-
Ruby on Rails has a built-in filter for special SQL characters, which will escape ' , " , NULL character and line breaks.
|
633
|
+
Ruby on Rails has a built-in filter for special SQL characters, which will escape ' , " , NULL character and line breaks. *Using `Model.find(id)` or `Model.find_by_some thing(something)` automatically applies this countermeasure*. But in SQL fragments, especially *in conditions fragments (`where("...")`), the `connection.execute()` or `Model.find_by_sql()` methods, it has to be applied manually*.
|
628
634
|
|
629
635
|
Instead of passing a string to the conditions option, you can pass an array to sanitize tainted strings like this:
|
630
636
|
|
@@ -726,7 +732,7 @@ Imagine a blacklist deletes "script" from the user input. Now the attacker injec
|
|
726
732
|
strip_tags("some<<b>script>alert('hello')<</b>/script>")
|
727
733
|
```
|
728
734
|
|
729
|
-
This returned "some<script>alert('hello')</script>", which makes an attack work. That's why
|
735
|
+
This returned "some<script>alert('hello')</script>", which makes an attack work. That's why a whitelist approach is better, using the updated Rails 2 method sanitize():
|
730
736
|
|
731
737
|
```ruby
|
732
738
|
tags = %w(a acronym b strong i em li ul ol h1 h2 h3 h4 h5 h6 blockquote br cite sub sup ins p)
|
@@ -735,7 +741,7 @@ s = sanitize(user_input, tags: tags, attributes: %w(href title))
|
|
735
741
|
|
736
742
|
This allows only the given tags and does a good job, even against all kinds of tricks and malformed tags.
|
737
743
|
|
738
|
-
As a second step, _it is good practice to escape all output of the application_, especially when re-displaying user input, which hasn't been input-filtered (as in the search form example earlier on). _Use `escapeHTML()` (or its alias `h()`) method_ to replace the HTML input characters &, ", <, > by their uninterpreted representations in HTML (`&`, `"`, `<`;, and `>`). However, it can easily happen that the programmer forgets to use it, so _it is recommended to use the
|
744
|
+
As a second step, _it is good practice to escape all output of the application_, especially when re-displaying user input, which hasn't been input-filtered (as in the search form example earlier on). _Use `escapeHTML()` (or its alias `h()`) method_ to replace the HTML input characters &, ", <, > by their uninterpreted representations in HTML (`&`, `"`, `<`;, and `>`). However, it can easily happen that the programmer forgets to use it, so _it is recommended to use the SafeErb gem. SafeErb reminds you to escape strings from external sources.
|
739
745
|
|
740
746
|
##### Obfuscation and Encoding Injection
|
741
747
|
|
@@ -806,7 +812,7 @@ The [moz-binding](http://www.securiteam.com/securitynews/5LP051FHPE.html) CSS pr
|
|
806
812
|
|
807
813
|
#### Countermeasures
|
808
814
|
|
809
|
-
This example, again, showed that a blacklist filter is never complete. However, as custom CSS in web applications is a quite rare feature,
|
815
|
+
This example, again, showed that a blacklist filter is never complete. However, as custom CSS in web applications is a quite rare feature, it may be hard to find a good whitelist CSS filter. _If you want to allow custom colors or images, you can allow the user to choose them and build the CSS in the web application_. Use Rails' `sanitize()` method as a model for a whitelist CSS filter, if you really need one.
|
810
816
|
|
811
817
|
### Textile Injection
|
812
818
|
|
@@ -996,7 +1002,7 @@ _'1; mode=block' in Rails by default_ - use XSS Auditor and block page if XSS at
|
|
996
1002
|
* X-Content-Type-Options
|
997
1003
|
_'nosniff' in Rails by default_ - stops the browser from guessing the MIME type of a file.
|
998
1004
|
* X-Content-Security-Policy
|
999
|
-
[A powerful mechanism for controlling which sites certain content types can be loaded from](http://
|
1005
|
+
[A powerful mechanism for controlling which sites certain content types can be loaded from](http://w3c.github.io/webappsec/specs/content-security-policy/csp-specification.dev.html)
|
1000
1006
|
* Access-Control-Allow-Origin
|
1001
1007
|
Used to control which sites are allowed to bypass same origin policies and send cross-origin requests.
|
1002
1008
|
* Strict-Transport-Security
|
@@ -1005,7 +1011,7 @@ Used to control which sites are allowed to bypass same origin policies and send
|
|
1005
1011
|
Environmental Security
|
1006
1012
|
----------------------
|
1007
1013
|
|
1008
|
-
It is beyond the scope of this guide to inform you on how to secure your application code and environments. However, please secure your database configuration, e.g. `config/database.yml`, and your server-side secret, e.g. stored in `config/
|
1014
|
+
It is beyond the scope of this guide to inform you on how to secure your application code and environments. However, please secure your database configuration, e.g. `config/database.yml`, and your server-side secret, e.g. stored in `config/secrets.yml`. You may want to further restrict access, using environment-specific versions of these files and any others that may contain sensitive information.
|
1009
1015
|
|
1010
1016
|
Additional Resources
|
1011
1017
|
--------------------
|
data/guides/source/testing.md
CHANGED
@@ -49,7 +49,9 @@ The `test_helper.rb` file holds the default configuration for your tests.
|
|
49
49
|
|
50
50
|
### The Low-Down on Fixtures
|
51
51
|
|
52
|
-
For good tests, you'll need to give some thought to setting up test data.
|
52
|
+
For good tests, you'll need to give some thought to setting up test data.
|
53
|
+
In Rails, you can handle this by defining and customizing fixtures.
|
54
|
+
You can find comprehensive documentation in the [fixture api documentation](http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html).
|
53
55
|
|
54
56
|
#### What Are Fixtures?
|
55
57
|
|
@@ -94,6 +96,12 @@ one:
|
|
94
96
|
category: about
|
95
97
|
```
|
96
98
|
|
99
|
+
Note: For associations to reference one another by name, you cannot specify the `id:`
|
100
|
+
attribute on the fixtures. Rails will auto assign a primary key to be consistent between
|
101
|
+
runs. If you manually specify an `id:` attribute, this behavior will not work. For more
|
102
|
+
information on this assocation behavior please read the
|
103
|
+
[fixture api documentation](http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html).
|
104
|
+
|
97
105
|
#### ERB'in It Up
|
98
106
|
|
99
107
|
ERB allows you to embed Ruby code within templates. The YAML fixture format is pre-processed with ERB when Rails loads fixtures. This allows you to use Ruby to help you generate some sample data. For example, the following code generates a thousand users:
|
@@ -134,27 +142,27 @@ Unit Testing your Models
|
|
134
142
|
|
135
143
|
In Rails, models tests are what you write to test your models.
|
136
144
|
|
137
|
-
For this guide we will be using Rails _scaffolding_. It will create the model, a migration, controller and views for the new resource in a single operation. It will also create a full test suite following Rails best practices.
|
145
|
+
For this guide we will be using Rails _scaffolding_. It will create the model, a migration, controller and views for the new resource in a single operation. It will also create a full test suite following Rails best practices. We will be using examples from this generated code and will be supplementing it with additional examples where necessary.
|
138
146
|
|
139
|
-
NOTE: For more information on Rails
|
147
|
+
NOTE: For more information on Rails _scaffolding_, refer to [Getting Started with Rails](getting_started.html)
|
140
148
|
|
141
149
|
When you use `rails generate scaffold`, for a resource among other things it creates a test stub in the `test/models` folder:
|
142
150
|
|
143
151
|
```bash
|
144
|
-
$ bin/rails generate scaffold
|
152
|
+
$ bin/rails generate scaffold article title:string body:text
|
145
153
|
...
|
146
|
-
create app/models/
|
147
|
-
create test/models/
|
148
|
-
create test/fixtures/
|
154
|
+
create app/models/article.rb
|
155
|
+
create test/models/article_test.rb
|
156
|
+
create test/fixtures/articles.yml
|
149
157
|
...
|
150
158
|
```
|
151
159
|
|
152
|
-
The default test stub in `test/models/
|
160
|
+
The default test stub in `test/models/article_test.rb` looks like this:
|
153
161
|
|
154
162
|
```ruby
|
155
163
|
require 'test_helper'
|
156
164
|
|
157
|
-
class
|
165
|
+
class ArticleTest < ActiveSupport::TestCase
|
158
166
|
# test "the truth" do
|
159
167
|
# assert true
|
160
168
|
# end
|
@@ -170,15 +178,15 @@ require 'test_helper'
|
|
170
178
|
As you know by now, `test_helper.rb` specifies the default configuration to run our tests. This is included with all the tests, so any methods added to this file are available to all your tests.
|
171
179
|
|
172
180
|
```ruby
|
173
|
-
class
|
181
|
+
class ArticleTest < ActiveSupport::TestCase
|
174
182
|
```
|
175
183
|
|
176
|
-
The `
|
184
|
+
The `ArticleTest` class defines a _test case_ because it inherits from `ActiveSupport::TestCase`. `ArticleTest` thus has all the methods available from `ActiveSupport::TestCase`. You'll see those methods a little later in this guide.
|
177
185
|
|
178
|
-
Any method defined within a class inherited from `
|
179
|
-
(which is the superclass of `ActiveSupport::TestCase`) that begins with `
|
186
|
+
Any method defined within a class inherited from `Minitest::Test`
|
187
|
+
(which is the superclass of `ActiveSupport::TestCase`) that begins with `test_` (case sensitive) is simply called a test. So, `test_password` and `test_valid_password` are legal test names and are run automatically when the test case is run.
|
180
188
|
|
181
|
-
Rails adds a `test` method that takes a test name and a block. It generates a normal `
|
189
|
+
Rails adds a `test` method that takes a test name and a block. It generates a normal `Minitest::Unit` test with method names prefixed with `test_`. So,
|
182
190
|
|
183
191
|
```ruby
|
184
192
|
test "the truth" do
|
@@ -220,7 +228,7 @@ In order to run your tests, your test database will need to have the current str
|
|
220
228
|
Running a test is as simple as invoking the file containing the test cases through `rake test` command.
|
221
229
|
|
222
230
|
```bash
|
223
|
-
$ bin/rake test test/models/
|
231
|
+
$ bin/rake test test/models/article_test.rb
|
224
232
|
.
|
225
233
|
|
226
234
|
Finished tests in 0.009262s, 107.9680 tests/s, 107.9680 assertions/s.
|
@@ -231,7 +239,7 @@ Finished tests in 0.009262s, 107.9680 tests/s, 107.9680 assertions/s.
|
|
231
239
|
You can also run a particular test method from the test case by running the test and providing the `test method name`.
|
232
240
|
|
233
241
|
```bash
|
234
|
-
$ bin/rake test test/models/
|
242
|
+
$ bin/rake test test/models/article_test.rb test_the_truth
|
235
243
|
.
|
236
244
|
|
237
245
|
Finished tests in 0.009064s, 110.3266 tests/s, 110.3266 assertions/s.
|
@@ -243,25 +251,25 @@ This will run all test methods from the test case. Note that `test_helper.rb` is
|
|
243
251
|
|
244
252
|
The `.` (dot) above indicates a passing test. When a test fails you see an `F`; when a test throws an error you see an `E` in its place. The last line of the output is the summary.
|
245
253
|
|
246
|
-
To see how a test failure is reported, you can add a failing test to the `
|
254
|
+
To see how a test failure is reported, you can add a failing test to the `article_test.rb` test case.
|
247
255
|
|
248
256
|
```ruby
|
249
|
-
test "should not save
|
250
|
-
|
251
|
-
assert_not
|
257
|
+
test "should not save article without title" do
|
258
|
+
article = Article.new
|
259
|
+
assert_not article.save
|
252
260
|
end
|
253
261
|
```
|
254
262
|
|
255
263
|
Let us run this newly added test.
|
256
264
|
|
257
265
|
```bash
|
258
|
-
$ bin/rake test test/models/
|
266
|
+
$ bin/rake test test/models/article_test.rb test_should_not_save_article_without_title
|
259
267
|
F
|
260
268
|
|
261
269
|
Finished tests in 0.044632s, 22.4054 tests/s, 22.4054 assertions/s.
|
262
270
|
|
263
271
|
1) Failure:
|
264
|
-
|
272
|
+
test_should_not_save_article_without_title(ArticleTest) [test/models/article_test.rb:6]:
|
265
273
|
Failed assertion, no message given.
|
266
274
|
|
267
275
|
1 tests, 1 assertions, 1 failures, 0 errors, 0 skips
|
@@ -270,9 +278,9 @@ Failed assertion, no message given.
|
|
270
278
|
In the output, `F` denotes a failure. You can see the corresponding trace shown under `1)` along with the name of the failing test. The next few lines contain the stack trace followed by a message which mentions the actual value and the expected value by the assertion. The default assertion messages provide just enough information to help pinpoint the error. To make the assertion failure message more readable, every assertion provides an optional message parameter, as shown here:
|
271
279
|
|
272
280
|
```ruby
|
273
|
-
test "should not save
|
274
|
-
|
275
|
-
assert_not
|
281
|
+
test "should not save article without title" do
|
282
|
+
article = Article.new
|
283
|
+
assert_not article.save, "Saved the article without a title"
|
276
284
|
end
|
277
285
|
```
|
278
286
|
|
@@ -280,14 +288,14 @@ Running this test shows the friendlier assertion message:
|
|
280
288
|
|
281
289
|
```bash
|
282
290
|
1) Failure:
|
283
|
-
|
284
|
-
Saved the
|
291
|
+
test_should_not_save_article_without_title(ArticleTest) [test/models/article_test.rb:6]:
|
292
|
+
Saved the article without a title
|
285
293
|
```
|
286
294
|
|
287
295
|
Now to get this test to pass we can add a model level validation for the _title_ field.
|
288
296
|
|
289
297
|
```ruby
|
290
|
-
class
|
298
|
+
class Article < ActiveRecord::Base
|
291
299
|
validates :title, presence: true
|
292
300
|
end
|
293
301
|
```
|
@@ -295,7 +303,7 @@ end
|
|
295
303
|
Now the test should pass. Let us verify by running the test again:
|
296
304
|
|
297
305
|
```bash
|
298
|
-
$ bin/rake test test/models/
|
306
|
+
$ bin/rake test test/models/article_test.rb test_should_not_save_article_without_title
|
299
307
|
.
|
300
308
|
|
301
309
|
Finished tests in 0.047721s, 20.9551 tests/s, 20.9551 assertions/s.
|
@@ -320,15 +328,15 @@ end
|
|
320
328
|
Now you can see even more output in the console from running the tests:
|
321
329
|
|
322
330
|
```bash
|
323
|
-
$ bin/rake test test/models/
|
331
|
+
$ bin/rake test test/models/article_test.rb test_should_report_error
|
324
332
|
E
|
325
333
|
|
326
334
|
Finished tests in 0.030974s, 32.2851 tests/s, 0.0000 assertions/s.
|
327
335
|
|
328
336
|
1) Error:
|
329
|
-
test_should_report_error(
|
330
|
-
NameError: undefined local variable or method `some_undefined_variable' for #<
|
331
|
-
test/models/
|
337
|
+
test_should_report_error(ArticleTest):
|
338
|
+
NameError: undefined local variable or method `some_undefined_variable' for #<ArticleTest:0x007fe32e24afe0>
|
339
|
+
test/models/article_test.rb:10:in `block in <class:ArticleTest>'
|
332
340
|
|
333
341
|
1 tests, 0 assertions, 0 failures, 1 errors, 0 skips
|
334
342
|
```
|
@@ -345,7 +353,7 @@ backtrace. simply set the `BACKTRACE` environment variable to enable this
|
|
345
353
|
behavior:
|
346
354
|
|
347
355
|
```bash
|
348
|
-
$ BACKTRACE=1 bin/rake test test/models/
|
356
|
+
$ BACKTRACE=1 bin/rake test test/models/article_test.rb
|
349
357
|
```
|
350
358
|
|
351
359
|
### What to Include in Your Unit Tests
|
@@ -357,7 +365,7 @@ Ideally, you would like to include a test for everything which could possibly br
|
|
357
365
|
By now you've caught a glimpse of some of the assertions that are available. Assertions are the worker bees of testing. They are the ones that actually perform the checks to ensure that things are going as planned.
|
358
366
|
|
359
367
|
There are a bunch of different types of assertions you can use.
|
360
|
-
Here's an extract of the assertions you can use with `minitest
|
368
|
+
Here's an extract of the assertions you can use with [`Minitest`](https://github.com/seattlerb/minitest), the default testing library used by Rails. The `[msg]` parameter is an optional string message you can specify to make your test failure messages clearer. It's not required.
|
361
369
|
|
362
370
|
| Assertion | Purpose |
|
363
371
|
| ---------------------------------------------------------------- | ------- |
|
@@ -369,8 +377,12 @@ Here's an extract of the assertions you can use with `minitest`, the default tes
|
|
369
377
|
| `assert_not_same( expected, actual, [msg] )` | Ensures that `expected.equal?(actual)` is false.|
|
370
378
|
| `assert_nil( obj, [msg] )` | Ensures that `obj.nil?` is true.|
|
371
379
|
| `assert_not_nil( obj, [msg] )` | Ensures that `obj.nil?` is false.|
|
380
|
+
| `assert_empty( obj, [msg] )` | Ensures that `obj` is `empty?`.|
|
381
|
+
| `assert_not_empty( obj, [msg] )` | Ensures that `obj` is not `empty?`.|
|
372
382
|
| `assert_match( regexp, string, [msg] )` | Ensures that a string matches the regular expression.|
|
373
383
|
| `assert_no_match( regexp, string, [msg] )` | Ensures that a string doesn't match the regular expression.|
|
384
|
+
| `assert_includes( collection, obj, [msg] )` | Ensures that `obj` is in `collection`.|
|
385
|
+
| `assert_not_includes( collection, obj, [msg] )` | Ensures that `obj` is not in `collection`.|
|
374
386
|
| `assert_in_delta( expecting, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are within `delta` of each other.|
|
375
387
|
| `assert_not_in_delta( expecting, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are not within `delta` of each other.|
|
376
388
|
| `assert_throws( symbol, [msg] ) { block }` | Ensures that the given block throws the symbol.|
|
@@ -384,16 +396,20 @@ Here's an extract of the assertions you can use with `minitest`, the default tes
|
|
384
396
|
| `assert_not_respond_to( obj, symbol, [msg] )` | Ensures that `obj` does not respond to `symbol`.|
|
385
397
|
| `assert_operator( obj1, operator, [obj2], [msg] )` | Ensures that `obj1.operator(obj2)` is true.|
|
386
398
|
| `assert_not_operator( obj1, operator, [obj2], [msg] )` | Ensures that `obj1.operator(obj2)` is false.|
|
399
|
+
| `assert_predicate ( obj, predicate, [msg] )` | Ensures that `obj.predicate` is true, e.g. `assert_predicate str, :empty?`|
|
400
|
+
| `assert_not_predicate ( obj, predicate, [msg] )` | Ensures that `obj.predicate` is false, e.g. `assert_not_predicate str, :empty?`|
|
387
401
|
| `assert_send( array, [msg] )` | Ensures that executing the method listed in `array[1]` on the object in `array[0]` with the parameters of `array[2 and up]` is true. This one is weird eh?|
|
388
402
|
| `flunk( [msg] )` | Ensures failure. This is useful to explicitly mark a test that isn't finished yet.|
|
389
403
|
|
404
|
+
The above are subset of assertions that minitest supports. For an exhaustive & more up-to-date list, please check [Minitest API documentation](http://docs.seattlerb.org/minitest/), specifically [`Minitest::Assertions`](http://docs.seattlerb.org/minitest/Minitest/Assertions.html)
|
405
|
+
|
390
406
|
Because of the modular nature of the testing framework, it is possible to create your own assertions. In fact, that's exactly what Rails does. It includes some specialized assertions to make your life easier.
|
391
407
|
|
392
408
|
NOTE: Creating your own assertions is an advanced topic that we won't cover in this tutorial.
|
393
409
|
|
394
410
|
### Rails Specific Assertions
|
395
411
|
|
396
|
-
Rails adds some custom assertions of its own to the `
|
412
|
+
Rails adds some custom assertions of its own to the `minitest` framework:
|
397
413
|
|
398
414
|
| Assertion | Purpose |
|
399
415
|
| --------------------------------------------------------------------------------- | ------- |
|
@@ -422,26 +438,26 @@ You should test for things such as:
|
|
422
438
|
* was the correct object stored in the response template?
|
423
439
|
* was the appropriate message displayed to the user in the view?
|
424
440
|
|
425
|
-
Now that we have used Rails scaffold generator for our `
|
441
|
+
Now that we have used Rails scaffold generator for our `Article` resource, it has already created the controller code and tests. You can take look at the file `articles_controller_test.rb` in the `test/controllers` directory.
|
426
442
|
|
427
|
-
Let me take you through one such test, `test_should_get_index` from the file `
|
443
|
+
Let me take you through one such test, `test_should_get_index` from the file `articles_controller_test.rb`.
|
428
444
|
|
429
445
|
```ruby
|
430
|
-
class
|
446
|
+
class ArticlesControllerTest < ActionController::TestCase
|
431
447
|
test "should get index" do
|
432
448
|
get :index
|
433
449
|
assert_response :success
|
434
|
-
assert_not_nil assigns(:
|
450
|
+
assert_not_nil assigns(:articles)
|
435
451
|
end
|
436
452
|
end
|
437
453
|
```
|
438
454
|
|
439
|
-
In the `test_should_get_index` test, Rails simulates a request on the action called `index`, making sure the request was successful and also ensuring that it assigns a valid `
|
455
|
+
In the `test_should_get_index` test, Rails simulates a request on the action called `index`, making sure the request was successful and also ensuring that it assigns a valid `articles` instance variable.
|
440
456
|
|
441
457
|
The `get` method kicks off the web request and populates the results into the response. It accepts 4 arguments:
|
442
458
|
|
443
459
|
* The action of the controller you are requesting. This can be in the form of a string or a symbol.
|
444
|
-
* An optional hash of request parameters to pass into the action (eg. query string parameters or
|
460
|
+
* An optional hash of request parameters to pass into the action (eg. query string parameters or article variables).
|
445
461
|
* An optional hash of session variables to pass along with the request.
|
446
462
|
* An optional hash of flash values.
|
447
463
|
|
@@ -457,17 +473,17 @@ Another example: Calling the `:view` action, passing an `id` of 12 as the `param
|
|
457
473
|
get(:view, {'id' => '12'}, nil, {'message' => 'booya!'})
|
458
474
|
```
|
459
475
|
|
460
|
-
NOTE: If you try running `
|
476
|
+
NOTE: If you try running `test_should_create_article` test from `articles_controller_test.rb` it will fail on account of the newly added model level validation and rightly so.
|
461
477
|
|
462
|
-
Let us modify `
|
478
|
+
Let us modify `test_should_create_article` test in `articles_controller_test.rb` so that all our test pass:
|
463
479
|
|
464
480
|
```ruby
|
465
|
-
test "should create
|
466
|
-
assert_difference('
|
467
|
-
post :create,
|
481
|
+
test "should create article" do
|
482
|
+
assert_difference('Article.count') do
|
483
|
+
post :create, article: {title: 'Some title'}
|
468
484
|
end
|
469
485
|
|
470
|
-
assert_redirected_to
|
486
|
+
assert_redirected_to article_path(assigns(:article))
|
471
487
|
end
|
472
488
|
```
|
473
489
|
|
@@ -576,12 +592,12 @@ is the correct way to assert for the layout when the view renders a partial with
|
|
576
592
|
Here's another example that uses `flash`, `assert_redirected_to`, and `assert_difference`:
|
577
593
|
|
578
594
|
```ruby
|
579
|
-
test "should create
|
580
|
-
assert_difference('
|
581
|
-
post :create,
|
595
|
+
test "should create article" do
|
596
|
+
assert_difference('Article.count') do
|
597
|
+
post :create, article: {title: 'Hi', body: 'This is my first article.'}
|
582
598
|
end
|
583
|
-
assert_redirected_to
|
584
|
-
assert_equal '
|
599
|
+
assert_redirected_to article_path(assigns(:article))
|
600
|
+
assert_equal 'Article was successfully created.', flash[:notice]
|
585
601
|
end
|
586
602
|
```
|
587
603
|
|
@@ -589,13 +605,13 @@ end
|
|
589
605
|
|
590
606
|
Testing the response to your request by asserting the presence of key HTML elements and their content is a useful way to test the views of your application. The `assert_select` assertion allows you to do this by using a simple yet powerful syntax.
|
591
607
|
|
592
|
-
NOTE: You may find references to `assert_tag` in other documentation
|
608
|
+
NOTE: You may find references to `assert_tag` in other documentation. This has been removed in 4.2. Use `assert_select` instead.
|
593
609
|
|
594
610
|
There are two forms of `assert_select`:
|
595
611
|
|
596
|
-
`assert_select(selector, [equality], [message])` ensures that the equality condition is met on the selected elements through the selector. The selector may be a CSS selector expression (String)
|
612
|
+
`assert_select(selector, [equality], [message])` ensures that the equality condition is met on the selected elements through the selector. The selector may be a CSS selector expression (String) or an expression with substitution values.
|
597
613
|
|
598
|
-
`assert_select(element, selector, [equality], [message])` ensures that the equality condition is met on all the selected elements through the selector starting from the _element_ (instance of `
|
614
|
+
`assert_select(element, selector, [equality], [message])` ensures that the equality condition is met on all the selected elements through the selector starting from the _element_ (instance of `Nokogiri::XML::Node` or `Nokogiri::XML::NodeSet`) and its descendants.
|
599
615
|
|
600
616
|
For example, you could verify the contents on the title element in your response with:
|
601
617
|
|
@@ -625,7 +641,7 @@ assert_select "ol" do
|
|
625
641
|
end
|
626
642
|
```
|
627
643
|
|
628
|
-
The `assert_select` assertion is quite powerful. For more advanced usage, refer to its [documentation](
|
644
|
+
The `assert_select` assertion is quite powerful. For more advanced usage, refer to its [documentation](https://github.com/rails/rails-dom-testing/blob/master/lib/rails/dom/testing/assertions/selector_assertions.rb).
|
629
645
|
|
630
646
|
#### Additional View-Based Assertions
|
631
647
|
|
@@ -710,9 +726,9 @@ class UserFlowsTest < ActionDispatch::IntegrationTest
|
|
710
726
|
assert_equal 'Welcome david!', flash[:notice]
|
711
727
|
|
712
728
|
https!(false)
|
713
|
-
get "/
|
729
|
+
get "/articles/all"
|
714
730
|
assert_response :success
|
715
|
-
assert assigns(:
|
731
|
+
assert assigns(:products)
|
716
732
|
end
|
717
733
|
end
|
718
734
|
```
|
@@ -788,57 +804,53 @@ when you initiate a Rails project.
|
|
788
804
|
| `rake test:all:db` | Runs all tests quickly by merging all types and resetting db |
|
789
805
|
|
790
806
|
|
791
|
-
Brief Note About `
|
807
|
+
Brief Note About `Minitest`
|
792
808
|
-----------------------------
|
793
809
|
|
794
|
-
Ruby ships with a
|
795
|
-
us to use all of the basic assertions in our tests.
|
796
|
-
|
797
|
-
Ruby 1.9 introduced `MiniTest`, an updated version of `Test::Unit` which provides a backwards compatible API for `Test::Unit`. You could also use `MiniTest` in Ruby 1.8 by installing the `minitest` gem.
|
810
|
+
Ruby ships with a vast Standard Library for all common use-cases including testing. Since version 1.9, Ruby provides `Minitest`, a framework for testing. All the basic assertions such as `assert_equal` discussed above are actually defined in `Minitest::Assertions`. The classes `ActiveSupport::TestCase`, `ActionController::TestCase`, `ActionMailer::TestCase`, `ActionView::TestCase` and `ActionDispatch::IntegrationTest` - which we have been inheriting in our test classes - include `Minitest::Assertions`, allowing us to use all of the basic assertions in our tests.
|
798
811
|
|
799
|
-
NOTE: For more information on `
|
800
|
-
For more information on `MiniTest`, refer to [Minitest](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/minitest/unit/rdoc/)
|
812
|
+
NOTE: For more information on `Minitest`, refer to [Minitest](http://ruby-doc.org/stdlib-2.1.0/libdoc/minitest/rdoc/MiniTest.html)
|
801
813
|
|
802
814
|
Setup and Teardown
|
803
815
|
------------------
|
804
816
|
|
805
|
-
If you would like to run a block of code before the start of each test and another block of code after the end of each test you have two special callbacks for your rescue. Let's take note of this by looking at an example for our functional test in `
|
817
|
+
If you would like to run a block of code before the start of each test and another block of code after the end of each test you have two special callbacks for your rescue. Let's take note of this by looking at an example for our functional test in `Articles` controller:
|
806
818
|
|
807
819
|
```ruby
|
808
820
|
require 'test_helper'
|
809
821
|
|
810
|
-
class
|
822
|
+
class ArticlesControllerTest < ActionController::TestCase
|
811
823
|
|
812
824
|
# called before every single test
|
813
825
|
def setup
|
814
|
-
@
|
826
|
+
@article = articles(:one)
|
815
827
|
end
|
816
828
|
|
817
829
|
# called after every single test
|
818
830
|
def teardown
|
819
|
-
# as we are re-initializing @
|
831
|
+
# as we are re-initializing @article before every test
|
820
832
|
# setting it to nil here is not essential but I hope
|
821
833
|
# you understand how you can use the teardown method
|
822
|
-
@
|
834
|
+
@article = nil
|
823
835
|
end
|
824
836
|
|
825
|
-
test "should show
|
826
|
-
get :show, id: @
|
837
|
+
test "should show article" do
|
838
|
+
get :show, id: @article.id
|
827
839
|
assert_response :success
|
828
840
|
end
|
829
841
|
|
830
|
-
test "should destroy
|
831
|
-
assert_difference('
|
832
|
-
delete :destroy, id: @
|
842
|
+
test "should destroy article" do
|
843
|
+
assert_difference('Article.count', -1) do
|
844
|
+
delete :destroy, id: @article.id
|
833
845
|
end
|
834
846
|
|
835
|
-
assert_redirected_to
|
847
|
+
assert_redirected_to articles_path
|
836
848
|
end
|
837
849
|
|
838
850
|
end
|
839
851
|
```
|
840
852
|
|
841
|
-
Above, the `setup` method is called before each test and so `@
|
853
|
+
Above, the `setup` method is called before each test and so `@article` is available for each of the tests. Rails implements `setup` and `teardown` as `ActiveSupport::Callbacks`. Which essentially means you need not only use `setup` and `teardown` as methods in your tests. You could specify them by using:
|
842
854
|
|
843
855
|
* a block
|
844
856
|
* a method (like in the earlier example)
|
@@ -850,38 +862,38 @@ Let's see the earlier example by specifying `setup` callback by specifying a met
|
|
850
862
|
```ruby
|
851
863
|
require 'test_helper'
|
852
864
|
|
853
|
-
class
|
865
|
+
class ArticlesControllerTest < ActionController::TestCase
|
854
866
|
|
855
867
|
# called before every single test
|
856
|
-
setup :
|
868
|
+
setup :initialize_article
|
857
869
|
|
858
870
|
# called after every single test
|
859
871
|
def teardown
|
860
|
-
@
|
872
|
+
@article = nil
|
861
873
|
end
|
862
874
|
|
863
|
-
test "should show
|
864
|
-
get :show, id: @
|
875
|
+
test "should show article" do
|
876
|
+
get :show, id: @article.id
|
865
877
|
assert_response :success
|
866
878
|
end
|
867
879
|
|
868
|
-
test "should update
|
869
|
-
patch :update, id: @
|
870
|
-
assert_redirected_to
|
880
|
+
test "should update article" do
|
881
|
+
patch :update, id: @article.id, article: {}
|
882
|
+
assert_redirected_to article_path(assigns(:article))
|
871
883
|
end
|
872
884
|
|
873
|
-
test "should destroy
|
874
|
-
assert_difference('
|
875
|
-
delete :destroy, id: @
|
885
|
+
test "should destroy article" do
|
886
|
+
assert_difference('Article.count', -1) do
|
887
|
+
delete :destroy, id: @article.id
|
876
888
|
end
|
877
889
|
|
878
|
-
assert_redirected_to
|
890
|
+
assert_redirected_to articles_path
|
879
891
|
end
|
880
892
|
|
881
893
|
private
|
882
894
|
|
883
|
-
def
|
884
|
-
@
|
895
|
+
def initialize_article
|
896
|
+
@article = articles(:one)
|
885
897
|
end
|
886
898
|
end
|
887
899
|
```
|
@@ -889,11 +901,11 @@ end
|
|
889
901
|
Testing Routes
|
890
902
|
--------------
|
891
903
|
|
892
|
-
Like everything else in your Rails application, it is recommended that you test your routes. An example test for a route in the default `show` action of `
|
904
|
+
Like everything else in your Rails application, it is recommended that you test your routes. An example test for a route in the default `show` action of `Articles` controller above should look like:
|
893
905
|
|
894
906
|
```ruby
|
895
|
-
test "should route to
|
896
|
-
assert_routing '/
|
907
|
+
test "should route to article" do
|
908
|
+
assert_routing '/articles/1', {controller: "articles", action: "show", id: "1"}
|
897
909
|
end
|
898
910
|
```
|
899
911
|
|
@@ -992,7 +1004,7 @@ class UserControllerTest < ActionController::TestCase
|
|
992
1004
|
|
993
1005
|
assert_equal "You have been invited by me@example.com", invite_email.subject
|
994
1006
|
assert_equal 'friend@example.com', invite_email.to[0]
|
995
|
-
assert_match(/Hi friend@example.com/, invite_email.body)
|
1007
|
+
assert_match(/Hi friend@example.com/, invite_email.body.to_s)
|
996
1008
|
end
|
997
1009
|
end
|
998
1010
|
```
|
@@ -1002,17 +1014,9 @@ Testing helpers
|
|
1002
1014
|
|
1003
1015
|
In order to test helpers, all you need to do is check that the output of the
|
1004
1016
|
helper method matches what you'd expect. Tests related to the helpers are
|
1005
|
-
located under the `test/helpers` directory.
|
1006
|
-
generates both the helper and the test file:
|
1007
|
-
|
1008
|
-
```bash
|
1009
|
-
$ bin/rails generate helper User
|
1010
|
-
create app/helpers/user_helper.rb
|
1011
|
-
invoke test_unit
|
1012
|
-
create test/helpers/user_helper_test.rb
|
1013
|
-
```
|
1017
|
+
located under the `test/helpers` directory.
|
1014
1018
|
|
1015
|
-
|
1019
|
+
A helper test looks like so:
|
1016
1020
|
|
1017
1021
|
```ruby
|
1018
1022
|
require 'test_helper'
|
@@ -1041,11 +1045,10 @@ access to Rails' helper methods such as `link_to` or `pluralize`.
|
|
1041
1045
|
Other Testing Approaches
|
1042
1046
|
------------------------
|
1043
1047
|
|
1044
|
-
The built-in `
|
1048
|
+
The built-in `minitest` based testing is not the only way to test Rails applications. Rails developers have come up with a wide variety of other approaches and aids for testing, including:
|
1045
1049
|
|
1046
1050
|
* [NullDB](http://avdi.org/projects/nulldb/), a way to speed up testing by avoiding database use.
|
1047
1051
|
* [Factory Girl](https://github.com/thoughtbot/factory_girl/tree/master), a replacement for fixtures.
|
1048
|
-
* [Machinist](https://github.com/notahat/machinist/tree/master), another replacement for fixtures.
|
1049
1052
|
* [Fixture Builder](https://github.com/rdy/fixture_builder), a tool that compiles Ruby factories into fixtures before a test run.
|
1050
1053
|
* [MiniTest::Spec Rails](https://github.com/metaskills/minitest-spec-rails), use the MiniTest::Spec DSL within your rails tests.
|
1051
1054
|
* [Shoulda](http://www.thoughtbot.com/projects/shoulda), an extension to `test/unit` with additional helpers, macros, and assertions.
|