Skip to content

Commit

Permalink
Escape plone.formwidget.autocompletewidget autocomplete response to p…
Browse files Browse the repository at this point in the history
…revent xss
  • Loading branch information
elioschmutz committed Feb 27, 2020
1 parent f36380a commit c374e2c
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/HISTORY.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Changelog
------------------------

- Set seen_tours for all users in test fixture. [njohner]
- XSS: Escape html for plone formwidget autocomplete widget. [elioschmutz]
- XSS: Escape html for proposal history entries. [elioschmutz]


Expand Down
8 changes: 8 additions & 0 deletions opengever/base/browser/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -270,4 +270,12 @@
permission="zope2.View"
/>

<browser:page
name="autocomplete-search"
for="plone.formwidget.autocomplete.interfaces.IAutocompleteWidget"
permission="zope.Public"
class=".widgets.GeverAutocompleteSearch"
layer="opengever.base.interfaces.IOpengeverBaseLayer"
/>

</configure>
9 changes: 9 additions & 0 deletions opengever/base/browser/widgets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from plone.formwidget.autocomplete.widget import AutocompleteSearch
from opengever.base.utils import escape_html


class GeverAutocompleteSearch(AutocompleteSearch):

def __call__(self):
result = super(GeverAutocompleteSearch, self).__call__()
return escape_html(result)
21 changes: 21 additions & 0 deletions opengever/base/tests/test_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,24 @@ def test_renders_empty_message(self, browser):
"No items available",
browser.css('#formfield-form-widgets-empty_radio_table_field .empty_message').first.text
)


class TestGeverAutocompleteSearch(IntegrationTestCase):
@browsing
def test_autocomplete_result_is_escaped(self, browser):
self.login(self.manager, browser=browser)
self.subdossier.title = 'Evil <script></script>'
self.subdossier.reindexObject()

browser.open(self.dossier, view="@@move_items/++widget++form.widgets.destination_folder/@@autocomplete-search?q=Evil")

# The @@autocomplete-search view returns a string with `path|title\npath|title...`
items = browser.contents.split('\n')

self.assertEqual(
1, len(items),
"Due to the search query, only one dossier should be returned")

path, title = items[0].split('|')
self.assertEqual('/'.join(self.subdossier.getPhysicalPath()), path)
self.assertEqual('Evil &lt;script&gt;&lt;/script&gt;', title, 'The item')

0 comments on commit c374e2c

Please sign in to comment.