Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sanitize texts instead of using html_safe #3747

Merged
merged 24 commits into from
Oct 8, 2019
Merged

Sanitize texts instead of using html_safe #3747

merged 24 commits into from
Oct 8, 2019

Conversation

javierm
Copy link
Member

@javierm javierm commented Oct 5, 2019

Objectives

  • Make it easier to understand when a text is considered safe
  • Add rubocop and erb-lint rules so we don't use html_safe in the future

@javierm javierm added the security Pull requests that address a security vulnerability label Oct 5, 2019
@javierm javierm self-assigned this Oct 5, 2019
@javierm javierm added this to Reviewing in Roadmap via automation Oct 5, 2019
Copy link

@houndci-bot houndci-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some files could not be reviewed due to errors:

warning: parser/current is loading parser/ruby26, which recognizes
warning: parser/current is loading parser/ruby26, which recognizes
warning: 2.6.0-dev-compliant syntax, but you are running 2.6.1.
warning: please see https://github.com/whitequark/parser#compatibility-with-ruby-mri.
1 error(s) were found in ERB files
Linting 1 files with 13 linters...

erb statement not allowed here; did you mean '<%=' ?
In file: app/views/layouts/application.html.erb:25

@@ -12,7 +12,7 @@ namespace :proposals do
model.find_each do |resource|
if resource.external_url.present?
Globalize.with_locale(I18n.default_locale) do
new_description = "#{resource.description} <p>#{text_with_links(resource.external_url)}</p>"
new_description = "#{resource.description} <p>#{sanitize_and_auto_link(resource.external_url)}</p>"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metrics/LineLength: Line is too long. [111/110] (https://github.com/bbatsov/ruby-style-guide#80-character-limits)

app/views/layouts/_footer.html.erb Outdated Show resolved Hide resolved
@javierm javierm force-pushed the html_safe branch 2 times, most recently from 38fb7e4 to f9236f0 Compare October 5, 2019 16:55
@javierm javierm moved this from Reviewing to Doing in Roadmap Oct 5, 2019
@javierm javierm force-pushed the html_safe branch 3 times, most recently from 0eec7bd to de8734b Compare October 5, 2019 21:55
@javierm javierm force-pushed the html_safe branch 3 times, most recently from 00b21d9 to 6d7d457 Compare October 6, 2019 14:23
@javierm javierm force-pushed the active_record_labels branch 6 times, most recently from 03bcc26 to bbbd082 Compare October 6, 2019 23:56
@javierm javierm changed the base branch from active_record_labels to master October 7, 2019 00:42
@javierm javierm force-pushed the html_safe branch 2 times, most recently from 99ffafd to 87d3bec Compare October 8, 2019 00:19
@javierm javierm changed the title [WIP] Sanitize texts instead of using html_safe Sanitize texts instead of using html_safe Oct 8, 2019
@@ -12,7 +12,7 @@ namespace :proposals do
model.find_each do |resource|
if resource.external_url.present?
Globalize.with_locale(I18n.default_locale) do
new_description = "#{resource.description} <p>#{text_with_links(resource.external_url)}</p>"
new_description = "#{resource.description} <p>#{sanitize_and_auto_link(resource.external_url)}</p>"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metrics/LineLength: Line is too long. [111/110] (https://rubystyle.guide#80-character-limits)

The name `html_safe` is very confusing, and many developers (including
me a few years ago) think what that method does is convert the HTML
contents to safe content. It's actually quite the opposite: it marks the
string as safe, so the HTML inside it isn't stripped out by Rails.

In some cases we were marking strings as safe because we wanted to add
some HTML. However, it meant the whole string was considered safe, and
not just the contents which were under our control.

In particular, some translations added by admins to the database or
through crowding were marked as safe, when it wasn't necessarily the
case.

Although AFAIK crowdin checks for potential cross-site scripting
attacks, it's a good practice to sanitize parts of a string potentially
out of our control before marking the string as HTML safe.
This was causing erb-lint to issue a warning.
Using `html_safe` on the whole text meant the translations were also
considered HTML safe, but they are not supposed to have HTML.
We need to update other gems as well if we update this one. Dependabot
updated it automatically when updating `foundation_rails_helper`, but it
doesn't seem to be necessary.
It's possible to create a newsletter or a proposed action with
<script> tags by filling in the body using a textarea instead of a
CKEditor. While we trust our administrators not to do so, it's better to
completely eliminate that possibility.
There's a case where we would face a Cross-Site Scripting attack. An
attacker could use the browser's developer tools to add (on their
browser) a `<code>` tag with a `<script>` tag inside in the text of the
draft version. After doing so, commenting on that text would result in
the attacker's JavaScript being executed.
If we don't sanitize them, valuators might attempt Cross-Site Scripting
attacks.
The name `safe_html_with_links` was confusing and could make you think
it takes care of making the HTML safe. So I've renamed it in a way that
makes it a bit more intuitive that it expects its input to be already
sanitized.

I've changed `text_with_links` as well so now the two method names
complement each other.
We were confused about what `.html_safe` did, and were automatically
marking as safe content which was not.
We were using `Rinku.auto_link` the same way twice. And it makes sense
that the method `sanitize_and_auto_link` first sanitizes the text and
then calls `auto_link_already_sanitized_text`.
This way we can remove all those `html_safe` calls and we avoid
potential XSS attacks in label texts.
There's a slight chance an attribute like an author's name might contain
an attempt to perform XSS attacks. So, instead of marking the whole text
as HTML safe, we can sanitize it.

Also note I'm removing the `_html` suffix in the i18n key, since it's
got the same effect as using `html_safe`.
Sometimes we're interpolating a link inside a translation, and marking
the whole translations as HTML safe.

However, some translations added by admins to the database or through
crowdin are not entirely under our control.

Although AFAIK crowdin checks for potential cross-site scripting
attacks, it's a good practice to sanitize parts of a string potentially
out of our control before marking the string as HTML safe.
The difference is `html_safe` allows every HTML tag, including the
`<script>` tag, while `sanitize` only allows tags which are considered
safe. In this case, we want to allow a `<span>` tag in a translation,
and links inside flash messages.
We were using the markdown renderer with the `filter_html` option set to
false, so we weren't removing hypothetical `<script>` tags.
They do the exact same thing; however `html_safe` might confuse
developers into thinking it will make the HTML safe. Using `raw` makes
it clear that we're inserting the text without escaping it.
This way we make sure we won't add `html_safe` or `raw` calls in the
future.

I'm excluding `text_with_links_helpers` for this check, because in this
situation the use of `html_safe` is justified: we check the original
input is safe, and we're only adding link tags to raw URLs.
Using `<%==` is the same as using `raw`. I'm not sure if we meant
`sanitize` in this case, or it's just a typo. I'm assuming the latter
since we don't use anything similar in any other places.
Using `<%==` is the same as using `raw`, and here we only want to mark
as safe a `<br>` tag.
We were using `<%==`, which is the same as using `raw`.

Note ERB Lint doesn't warn us of this usage. Brakeman does warn us,
though.
app/views/layouts/application.html.erb Show resolved Hide resolved
@@ -11,35 +11,35 @@
type: "image/png" %>
<%= content_for :social_media_meta_tags %>

<%= setting["html.per_page_code_head"].try(:html_safe) %>
<%= raw setting["html.per_page_code_head"] %>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

erb interpolation with '<%= raw(...

</head>
<body class="proposal-dashboard">
<%= setting["per_page_code_body"].try(:html_safe) %>
<%= raw setting["per_page_code_body"] %>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

erb interpolation with '<%= raw(...

@@ -18,10 +18,10 @@
type: "image/png" %>
<%= content_for :social_media_meta_tags %>

<%= setting["per_page_code_head"].try(:html_safe) %>
<%= raw setting["per_page_code_head"] %>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

erb interpolation with '<%= raw(...

</head>

<body class="auth-page">
<%= setting["html.per_page_code_body"].try(:html_safe) %>
<%= raw setting["html.per_page_code_body"] %>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

erb interpolation with '<%= raw(...

@@ -3,11 +3,11 @@
<head>
<%= render "layouts/common_head", default_title: "Gobierno abierto" %>
<%= render "layouts/meta_tags" %>
<%= setting["html.per_page_code_head"].try(:html_safe) %>
<%= raw setting["html.per_page_code_head"] %>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

erb interpolation with '<%= raw(...

</head>
<body class="proposal-dashboard">
<%= setting["per_page_code_body"].try(:html_safe) %>
<%= raw setting["per_page_code_body"] %>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

erb interpolation with '<%= raw(...

@@ -18,10 +18,10 @@
type: "image/png" %>
<%= content_for :social_media_meta_tags %>

<%= setting["per_page_code_head"].try(:html_safe) %>
<%= raw setting["per_page_code_head"] %>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

erb interpolation with '<%= raw(...

@@ -43,7 +43,7 @@
<% @headings_content_blocks.each do |content_block| %>
<tr id="<%= dom_id(content_block) %>">
<td><%= link_to "#{content_block.heading.name} (#{content_block.locale})", admin_site_customization_edit_heading_content_block_path(content_block) %></td>
<td><%= content_block.body.html_safe %></td>
<td><%= raw content_block.body %></td>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

erb interpolation with '<%= raw(...

@@ -32,7 +32,7 @@
<% @content_blocks.each do |content_block| %>
<tr id="<%= dom_id(content_block) %>">
<td><%= link_to "#{content_block.name} (#{content_block.locale})", edit_admin_site_customization_content_block_path(content_block) %></td>
<td><%= content_block.body.html_safe %></td>
<td><%= raw content_block.body %></td>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

erb interpolation with '<%= raw(...

@javierm
Copy link
Member Author

javierm commented Oct 8, 2019

Travis failure is not related to this pull request.

@javierm javierm merged commit 8b2acc1 into master Oct 8, 2019
Roadmap automation moved this from Reviewing to Release 1.1.0 Oct 8, 2019
@javierm javierm deleted the html_safe branch October 8, 2019 17:57
smarques pushed a commit to venetochevogliamo/consul that referenced this pull request Apr 29, 2020
Sanitize texts instead of using html_safe
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
security Pull requests that address a security vulnerability
Projects
No open projects
Roadmap
  
Release 1.1.0
Development

Successfully merging this pull request may close these issues.

None yet

2 participants