Skip to content

Conversation

damianhxy
Copy link
Member

@damianhxy damianhxy commented May 19, 2022

Improve correctness of extensions + submissions autocomplete, and extract common logic.
Also fix an XSS issue in gradebook, and various visual display issues.

Description

  • CGI.escapeHTML on backend
  • escape_javascript on frontend
  • remove sanitize from flash
  • replace encode64 with strict_encode64 to avoid line feeds
  • extracted common backend logic to ApplicationController
  • extracted common frontend logic to components/_autocomplete.html.erb
  • use to_csv for download_roster

Motivation and Context

Currently, there are several issues with autocomplete, including

  • XSS in name / email
  • Disappearing letters when backslashes exist in name
  • base64 breaking on long names
  • download roster breaking on names with commas

This PR serves to improve the robustness of extensions + submissions autocomplete

How Has This Been Tested?

First created the following users:
(invalid emails can be saved by using inspect element and deleting the type from the input field)

First Name Last Name Email Comments
\back \slash [email protected] Test js escaping
<img src=a onerror=alert(document.cookie)> alert [email protected] Test XSS, also tests long base64 encoding
<strong>first</strong> <strong>last</strong> [email protected] Test HTML escaping
qn? mark [email protected] Tests base64 encodings with /
Space (with a leading space) Name [email protected] Tests that btoa is serving its purpose of handling names with leading / trailing spaces
italics email <em>name</em>@italicsemail.com Tests HTML escaping
bad email <strong>name</strong>@bademail.com Tests HTML escaping
script email <script>alert("hello")</script>@scriptemail.com Tests HTML escaping
  • Ensured that flash displays correctly when creating users (i.e. any tags are escaped)
  • Successfully give all users an infinite extension, with no autocomplete display issues
  • Successfully create submissions for all users (via Manage Submissions > Create New Submission), with no autocomplete display issues
  • Ensured that gradebook displays correctly
  • Ensured that download roster produces a csv file that displays correctly

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • I have run rubocop for style check. If you haven't, run overcommit --install && overcommit --sign to use pre-commit hook for linting
  • My change requires a change to the documentation, which is located at Autolab Docs
  • I have updated the documentation accordingly, included in this PR

@damianhxy damianhxy linked an issue May 19, 2022 that may be closed by this pull request
@damianhxy damianhxy requested a review from victorhuangwq May 19, 2022 00:58
@damianhxy damianhxy changed the title Improve extensions autocomplete Improve extensions + submissions autocomplete May 19, 2022
@cg2v
Copy link
Member

cg2v commented May 20, 2022

Have you tested with names containing characters that would be entity encoded (I used ' (&apos;) to make sure they're not double encoded?

Did you verify that the flash xss occurs without these changes? the flash rendering seems safe already:

              <%= content_tag :div, sanitize(msg), id: "flash_#{name}",
                  class: "#{name}" %>

@damianhxy
Copy link
Member Author

Re: flash, I recall that the success flash did not escape <em>name</em>@italicsemail.com before the changes. However, I'll do more testing later and with the error flashes too.

I will also test for double encoding later.

@damianhxy
Copy link
Member Author

damianhxy commented May 21, 2022

Examples of flashes with email: <em>italics</em>@email.com

Screen Shot 2022-05-20 at 9 35 48 PM

Screen Shot 2022-05-20 at 9 36 13 PM

@damianhxy
Copy link
Member Author

damianhxy commented May 21, 2022

It appears sanitize isn't sufficient for our purposes as it strips tags, rather than escapes them.

Screen Shot 2022-05-20 at 9 42 52 PM

Will remedy this by replacing sanitize(msg) with sanitize(h msg)

@damianhxy
Copy link
Member Author

OK - Admittedly sanitize does prevent XSS by stripping unsafe tags and attributes. But nonetheless, I believe we should still HTML escape in order to display the text as-is.

@damianhxy
Copy link
Member Author

damianhxy commented May 21, 2022

Re: double encoding, there do not seem to be any issues. Text displays correctly, and creating extensions, creating submissions, sudo, etc. work.
Screen Shot 2022-05-20 at 10 06 47 PM
Screen Shot 2022-05-20 at 10 07 20 PM
Screen Shot 2022-05-20 at 10 07 32 PM

FWIW: it seems that escape_html does not cause double escapes, but CGI.escapeHTML has the potential to do so, thus care must be taken

@victorhuangwq
Copy link
Contributor

Is this still WIP or is it ready for review?

Copy link
Member

@cg2v cg2v left a comment

Choose a reason for hiding this comment

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

it seems pointless to call sanitize() after escape_html(). sanitize won't find anything to filter because escape_html has already encoded all the tag metacharacters.

It appears that if you don't call sanitize, you don't need escape_html either, because rails will automatically escape the inner text of the content_tag.(https://makandracards.com/makandra/2579-how-to-use-html_safe-correctly)

Copy link
Member

@cg2v cg2v left a comment

Choose a reason for hiding this comment

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

Instead of this, it might make sense to have the controller use strict_encode64 instead of urlsafe_encode64 (my bad there)

Copy link
Member

@cg2v cg2v left a comment

Choose a reason for hiding this comment

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

I suppose I'll have to test this at some point, but I don't see how this can avoid breaking the autocomplete in some cases. the keys in usersEncoded have to match the keys in users. They differ only in whether or not they're encoded. If the keys don't match, the user can't be autocompleted.

@damianhxy
Copy link
Member Author

damianhxy commented May 22, 2022

Re: sanitize / escape_html, if Ruby indeed escapes everything correctly in the absence of sanitize, removing it seems to be the best solution. The only downside I can think of is that we wouldn't be able to style the flashes -- but I don't think we do that anyway (other than roster_error, which is handled specially in application.html.erb).

Re: strict_encode64, I agree that that's a better solution given that we don't use the base64 in URLs, in which case I'll also remove the additional .replaces

Re: autocomplete, I'm not sure which line of code you're specifically referring to. However, I don't see any immediate issues with the current approach. Could you clarify this?

@cg2v
Copy link
Member

cg2v commented May 22, 2022

Say my name is

First Name Last Name Email
Chask'l Grundman [email protected]

Then @users[CGI.escapeHTML cud.full_name_with_email] = cud.id will use the value
Chask&apos;l Grundman ([email protected])
and @usersEncoded[Base64.urlsafe_encode64(cud.full_name_with_email.strip).strip] = cud.id will use
Q2hhc2snbCBHcnVuZG1hbiAoY2cydkBhdXRvbGFiLmlvKQ==

When the javascript calls btoa on Chask&apos;l Grundman ([email protected]), it gets Q2hhc2smYXBvcztsIEdydW5kbWFuIChjZzJ2QGF1dG9sYWIuaW8p which does not match.

However, the existing code is broken if there are tags in the name (materialize strips the tags - there's no xss). So perhaps the correct thing to do is to escapeHTML before encoding. Things are getting very hacky at this point and it leads me to wonder how other software deals with this problem.

@damianhxy
Copy link
Member Author

damianhxy commented May 22, 2022

Hmm, this doesn't seem to be an issue (at least on my browser, FF100 Mac) since $studentAutocompleteField.val() has the correct unescaped string before the call to btoa.
Screen Shot 2022-05-22 at 3 31 46 PM

Perhaps there is no need to change the code then?

@damianhxy
Copy link
Member Author

damianhxy commented May 22, 2022

Also note that I am doing j k instead of j k.html_safe as we appear to need an additional level of escaping to avoid XSS attacks in the autocomplete dropdown.

Essentially, it seems that the autocomplete code unescapes once when displaying text (such that .val() is twice unescaped). Hence, we need exactly two levels of escaping, in order to avoid an XSS after the first level of unescaping, and to obtain the correct string from $studentAutocompleteField.val().

An Example
Original string: <strong>name</strong>@bademail.com -- not escaped
String passed to autocomplete: &amp;lt;strong&amp;gt;name&amp;lt;/strong&amp;gt;@bademail.com -- twice escaped
HTML code of dropdown option: &lt;strong&gt;name&lt;/strong&gt;@bademail.com -- once escaped
What user sees of dropdown option: <strong>name</strong>@bademail.com -- not escaped
.val(): <strong>name</strong>@bademail.com -- not escaped

@damianhxy
Copy link
Member Author

Alternatively, CGI.escapeHTML (CGI.escapeHTML cud.full_name_with_email) together with j k.html_safe also seems to work, but I'm not sure which is preferable.

@cg2v
Copy link
Member

cg2v commented May 23, 2022

If that's what autocomplete is doing, I will withdraw my objections

@damianhxy
Copy link
Member Author

@victorhuangwq This PR is ready for review!

Copy link
Contributor

@victorhuangwq victorhuangwq left a comment

Choose a reason for hiding this comment

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

Tested the functionality as given by the test case, works as expected.
Good that you abstracted repeated functionality!

I approved, but I had a nit. Thanks for addressing this issue.

@damianhxy damianhxy merged commit 64e7e5c into master Jun 6, 2022
@damianhxy damianhxy deleted the extensions-improvements branch June 6, 2022 01:57
damianhxy added a commit that referenced this pull request Jun 6, 2022
@damianhxy damianhxy mentioned this pull request Jun 8, 2022
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Unable to create an extension
3 participants