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

[pro#549] apostrophe encoding #4807

Merged
merged 10 commits into from
Sep 18, 2018
Merged

[pro#549] apostrophe encoding #4807

merged 10 commits into from
Sep 18, 2018

Conversation

lizconlan
Copy link

@lizconlan lizconlan commented Aug 14, 2018

What does this do?

  1. Prevents appending text to the outgoing message body from triggering HTML escaping of the existing text (only applies to logged in users) screen shot 2018-08-07 at 18 41 33

  2. Alters the preview templates on the pro side to bring them in line with the standard ones (the HTML was not being escaped before display before)

screen shot 2018-08-17 at 16 04 08

  1. Prevent apostrophe's being displayed as ' (and the whole message being wrapped in an HTML template) when an admin is sending an email as part of marking a request as vexatious/not_foii screen shot 2018-08-12 at 20 28 43

  2. Fixes the display of user names with apostrophes in the admins' user list screen shot 2018-08-17 at 11 08 23

Why was this needed?

Fixes some of the inconsistencies of how apostrophes (and other special characters such as ampersands) are displayed across the site.

Notes to reviewer

  • Are the pro request preview changes correct or was the behaviour difference intentional?
  • Is removing the HTML wrapper from the admin email textarea correct? (4410611)

Relevant issue(s)

Fixes mysociety/alaveteli-professional#549
Fixes #4793

end

it 'appends the user name' do
using_session(user_session) do

Choose a reason for hiding this comment

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

Use 2 (not 3) spaces for indentation.

@lizconlan lizconlan force-pushed the pro549-apostrophe-encoding branch 4 times, most recently from ca74840 to f5a4253 Compare August 16, 2018 17:13
@lizconlan lizconlan changed the title [WIP] [pro#549] apostrophe encoding [pro#549] apostrophe encoding Aug 17, 2018
@@ -7,7 +7,7 @@
<span class="user-labels">
<%= user_labels(user) %>
</span>
<%= link_to("#{ h(user.name) }", admin_user_path(user)) %>
<%= link_to("#{ h(user.name.html_safe) }", admin_user_path(user)) %>
Copy link
Member

Choose a reason for hiding this comment

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

👎 Rails escapes content by default.

The solution here is to remove the h call, which is causing the double escape.

<%= link_to user.name, admin_user_path(user) %>

@@ -94,7 +94,7 @@ def get_default_message
def set_signature_name(name)
# TODO: We use raw_body here to get unstripped one
if raw_body == get_default_message
self.body = raw_body + name
self.body = "#{raw_body}#{name}".html_safe
Copy link
Member

Choose a reason for hiding this comment

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

This feels like the wrong place to be doing this.

The escaping happens much further down the chain:

t = OutgoingMessage::Template::InitialRequest.new
# => #<OutgoingMessage::Template::InitialRequest:0x0000000a34c8d0>
t.body(public_body_name: "Department for '& Humpadinking")
# => "Dear Department for &#39;&amp; Humpadinking,\n\n\n\nYours faithfully,\n\n"
_("hi {{name}}", name: '&')
# => "hi &amp;"

Translations are explicitly html_safe.

_("hi {{name}}", name: '&').html_safe?
# => true

It looks like this should all "just work":

diff --git a/app/controllers/admin_spam_addresses_controller.rb b/app/controllers/admin_spam_addresses_controller.rb
index 357d9e594..35370f90e 100644
--- a/app/controllers/admin_spam_addresses_controller.rb
+++ b/app/controllers/admin_spam_addresses_controller.rb
@@ -6,6 +6,7 @@ class AdminSpamAddressesController < AdminController
   def index
     @spam_addresses = SpamAddress.all
     @spam_address = SpamAddress.new
+    @thing = _("Hi {{name}}", name: "bo&'b")
   end

   def create
diff --git a/app/views/admin_spam_addresses/index.html.erb b/app/views/admin_spam_addresses/index.html.erb
index f445e35aa..2630115d2 100644
--- a/app/views/admin_spam_addresses/index.html.erb
+++ b/app/views/admin_spam_addresses/index.html.erb
@@ -2,6 +2,9 @@

 <h1><%= @title %></h1>

+<p><%= @thing %></p>
+<%= text_area_tag :thing, @thing %>
+
 <div class="row">
   <div class="span12">
     <p>

screen shot 2018-08-22 at 10 02 35

The actual problem we have is not that HTML is escaped, but that it is double escaped:

_("hi {{name}}", name: '&')
# => "hi &amp;"
CGI.escapeHTML(_("hi {{name}}", name: '&'))
# => "hi &amp;amp;"
CGI.escapeHTML(_("hi {{name}}", name: '&').html_safe)
# => "hi &amp;amp;"

Copy link
Member

Choose a reason for hiding this comment

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

We think a better solution is:

diff --git a/app/models/outgoing_message.rb b/app/models/outgoing_message.rb
index 560a23a59..1cf33c71d 100644
--- a/app/models/outgoing_message.rb
+++ b/app/models/outgoing_message.rb
@@ -94,7 +94,7 @@ class OutgoingMessage < ActiveRecord::Base
   def set_signature_name(name)
     # TODO: We use raw_body here to get unstripped one
     if raw_body == get_default_message
-      self.body = raw_body + name
+      self.body = get_default_message + name
     end
   end

@@ -1,4 +1,4 @@
<%= _("Dear {{name}},", :name => name_to) %>
<%= _("Dear {{name}},", :name => name_to.html_safe) %>
Copy link
Member

Choose a reason for hiding this comment

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

We're going to try making this a text template rather than html.

Copy link
Author

@lizconlan lizconlan Aug 22, 2018

Choose a reason for hiding this comment

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

The "right" way to deal with this is probably to pass in a SafeBuffer as this is supposed to be text/plain output and it puts the decision of how to treat the information in the calling controller rather than the reusable template. (Just passing name_to in still causes it to be escaped in a text.erb template - unless I've missed something, will double-check that because it's happening when we translate the phrase - which is irritating). oh, presumably the same problem will apply to {{site_name}} as well (I half remember having to fix this for email templates ages ago) sigh

Immediate downside of renaming the template to text.erb from html.erb is that without adding e.g. response.content_type = :html after calling render_to_string (stealth bug!) the admin page is sent as plain text as well /o\

However it doesn't look as though the version of the templated text carefully constructed in the controller is ever used as the textarea is unreachable with javascript disabled and the javascript version (from ServicesController#hidden_user_explanation) will immediately replace the text with javascript enabled so it's probably safe to remove it. Although that's going to make it hard to write a test for...

Oh also the textarea text is used as-is to construct a plain/text email to the user (sent by AdminRequestController#hide via ContactMailer#from_admin_message).

Copy link
Author

Choose a reason for hiding this comment

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

Waiting to see if the changes I've made so far break anything I'd not thought about. Then I think the best place to test the textarea/email to user to say their request has been hidden as vexatious content is the ServicesController spec

@request_hidden_user_explanation = render_to_string(:template => "admin_request/hidden_user_explanation",
:locals => vars_for_explanation)
@request_hidden_user_explanation =
render_to_string(template: "admin_request/hidden_user_explanation",
Copy link
Member

Choose a reason for hiding this comment

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

If we make the template a text template rather than html we probably won't need this change.

@@ -58,11 +58,10 @@ def hidden_user_explanation
render :template => "admin_request/hidden_user_explanation",
:content_type => "text/plain",
:layout => false,
:locals => {:name_to => info_request.user_name,
:name_from => AlaveteliConfiguration.contact_name,
:locals => {:name_to => info_request.user_name.html_safe,

Choose a reason for hiding this comment

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

Align the elements of a hash literal if they span more than one line.

@lizconlan lizconlan force-pushed the pro549-apostrophe-encoding branch from 8e1899b to 288b6cf Compare August 23, 2018 10:38
:locals => {
:name_to => info_request.user_name.html_safe,
:info_request => info_request, :reason => params[:reason],
:info_request_url => 'http://' + AlaveteliConfiguration.domain + request_path(info_request),

Choose a reason for hiding this comment

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

Line is too long. [106/80]

@lizconlan lizconlan force-pushed the pro549-apostrophe-encoding branch from 43a9e2f to da3a5f0 Compare August 23, 2018 13:12
locals: {
name_to: info_request.user_name.html_safe,
info_request: info_request, :reason => params[:reason],
info_request_url: 'http://' + AlaveteliConfiguration.domain + request_path(info_request),

Choose a reason for hiding this comment

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

Line is too long. [102/80]

Copy link
Member

Choose a reason for hiding this comment

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

Should be able to change this to request_url(info_request) to clean this up and fix the line-length issue. I'd make a new commit for that.

Copy link
Author

Choose a reason for hiding this comment

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

The test failed but I'm willing to bet that's a problem with my test rather than the Rails helper method.

@lizconlan lizconlan force-pushed the pro549-apostrophe-encoding branch from da3a5f0 to 336b828 Compare August 23, 2018 13:13
# https links in emails if forcing SSL
protocol = if AlaveteliConfiguration.force_ssl
"https://"
else

Choose a reason for hiding this comment

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

Align else with if.

def request_link(request_path)
# https links in emails if forcing SSL
protocol = if AlaveteliConfiguration.force_ssl
"https://"

Choose a reason for hiding this comment

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

Use 2 (not -9) spaces for indentation.

@lizconlan lizconlan force-pushed the pro549-apostrophe-encoding branch from a0f40b5 to e0aa4b5 Compare August 23, 2018 13:35
@lizconlan lizconlan assigned garethrees and unassigned lizconlan Aug 23, 2018
Copy link
Member

@garethrees garethrees left a comment

Choose a reason for hiding this comment

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

I think this is good in principle.

Inline comments to address, but the gist of the fix looks okay to me.

@@ -7,7 +7,7 @@
<span class="user-labels">
<%= user_labels(user) %>
</span>
<%= link_to("#{ h(user.name) }", admin_user_path(user)) %>
<%= link_to("#{ user.name }", admin_user_path(user)) %>
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't need to be interpolated in to a string.

render partial: 'admin_user/user_table.html.erb',
locals: { users: users,
banned_column: false }
expect(rendered).not_to match("O&amp;#39;Toole")
Copy link
Member

Choose a reason for hiding this comment

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

Would be clearer and more robust to expect(rendered).to match("O'Toole"). That way the spec would fail if the behaviour was incorrect, with a diff of expected vs actual (and the actual would be the incorrect double escaped string).

Copy link
Author

Choose a reason for hiding this comment

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

(added more to remind myself) the expected match is "O&#39;Toole" as it escapes the ' but avoids the double escape (which catches the ampersand)

spec/controllers/services_controller_spec.rb Show resolved Hide resolved
spec/controllers/services_controller_spec.rb Show resolved Hide resolved
app/controllers/services_controller.rb Show resolved Hide resolved
locals: {
name_to: info_request.user_name.html_safe,
info_request: info_request, :reason => params[:reason],
info_request_url: 'http://' + AlaveteliConfiguration.domain + request_path(info_request),
Copy link
Member

Choose a reason for hiding this comment

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

Should be able to change this to request_url(info_request) to clean this up and fix the line-length issue. I'd make a new commit for that.

locals: {
name_to: info_request.user_name,
name_from: AlaveteliConfiguration.contact_name,
info_request: info_request, :reason => params[:reason],
Copy link
Member

Choose a reason for hiding this comment

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

You missed :reason => params[:reason] – should drop that on to a new line and make it a 1.9-style hash

app/controllers/services_controller.rb Outdated Show resolved Hide resolved
@@ -115,4 +115,14 @@ def ask_the_eu_link
%q(<a href="http://asktheeu.org">Ask The EU</a>)
end

def request_link(request_path)
Copy link
Member

Choose a reason for hiding this comment

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

👎 – pretty sure the following would fix this:

diff --git a/app/controllers/services_controller.rb b/app/controllers/services_controller.rb
index 079f69d7f..76771a6c0 100644
--- a/app/controllers/services_controller.rb
+++ b/app/controllers/services_controller.rb
@@ -61,7 +61,7 @@ class ServicesController < ApplicationController
            locals: {
              name_to: info_request.user_name.html_safe,
              info_request: info_request, :reason => params[:reason],
-             info_request_url: request_link(request_path(info_request)),
+             info_request_url: request_url(info_request),
              site_name: site_name.html_safe }
   end

The specs do fail, but that's because config.force_ssl is set in the environment initialiser so can't be stubbed.

Its worth checking that I'm not talking rubbish here, but I'm pretty sure the helpers take protocol in to account…

@lizconlan lizconlan force-pushed the pro549-apostrophe-encoding branch 3 times, most recently from 36eedf2 to 7a7d30d Compare August 31, 2018 14:54
@lizconlan lizconlan assigned garethrees and unassigned lizconlan Aug 31, 2018
Copy link
Member

@garethrees garethrees left a comment

Choose a reason for hiding this comment

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

Need to address #4807 (comment), but good to merge after that

So that the new text is added to the html_safe version of the existing
text to avoid the original text (including the authority name) from
being HTML escaped twice.
Use the same `OutgoingMessage#get_body_for_html_display` used by the
standard preview template.
The form fields for dealing with hiding a request as either not_foi or
vexatious are hidden until the radio button is selected (requiring
javascript). And with javascript enabled, the pre-populated message is
immediately replaced with a holding message then a fresh copy of the
text as generated by ServicesController#hidden_user_explanation
* Simplify `require` of spec helper; can just reference the file by name as its already in the load path
* Make the top level `describe` block describe only the class we're testing
* Group specs under `describe '#method_name' do` block
* Remove the Ruby 1.9 encoding comment line
* Remove line breaks from spec descriptions
* Reduce line lengths
Prior to Rails 4.0, the template format was set from the MIME type (as
originally set here via the `:content_type` option). This has now
flipped so that content type is derived from the supplied format
(which defaults to `:html`). If we don't pass in `formats: [:text]` it
assumes HTML and fails to find the renamed template (raising an
`ActionView::MissingTemplate` error).

See: rails/rails@67b2404
This template is only going to be rendered as `text/plain` so malicious
user input (e.g. <script> tags) is not going to be rendered as HTML,
allowing us to treat the text as safe.

We only need to do this because we're going to use these arguments in
translation strings and we don't currently have a way to disable
GetText's HTML escaping of interpolated strings.
@lizconlan lizconlan force-pushed the pro549-apostrophe-encoding branch from 7a7d30d to 9562015 Compare September 18, 2018 09:10
@lizconlan lizconlan merged commit 9562015 into develop Sep 18, 2018
@lizconlan lizconlan deleted the pro549-apostrophe-encoding branch October 9, 2018 12:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants