Mercurial > p > roundup > code
changeset 5004:494d255043c9
Display errors containing HTML with RejectRaw (issue2550847)
In general outputting un-escaped HTML in a message to the user is an
unsafe operation, which is why error message are escaped by default. In
some cases though it is desirable for a detector to include HTML within
an error message. For these cases where HTML is required the RejectRaw
exception can be used within the detector.
| author | John Kristensen <john@jerrykan.com> |
|---|---|
| date | Sat, 10 Oct 2015 23:35:51 +1100 |
| parents | a9e0ef739241 |
| children | a86b0c02940d |
| files | CHANGES.txt doc/customizing.txt roundup/cgi/actions.py roundup/cgi/client.py roundup/exceptions.py |
| diffstat | 5 files changed, 41 insertions(+), 21 deletions(-) [+] |
line wrap: on
line diff
--- a/CHANGES.txt Sat Oct 10 23:07:17 2015 +1100 +++ b/CHANGES.txt Sat Oct 10 23:35:51 2015 +1100 @@ -57,6 +57,9 @@ (similar to RFC 2822), e.g. +0200 for CEST or -0500 for EST. This also works in the XMLRPC interface. For examples see roundup.date.Date. (Ralf Schlatterbeck) +- Add RejectRaw exception to allow unescaped HTML error messages to be + displayed to the user (thanks Ezio Melotti for the initial patch) + (John Kristensen) Fixed:
--- a/doc/customizing.txt Sat Oct 10 23:07:17 2015 +1100 +++ b/doc/customizing.txt Sat Oct 10 23:35:51 2015 +1100 @@ -928,7 +928,13 @@ And then when your rejection criteria have been detected, simply:: - raise Reject + raise Reject('Description of error') + +Error messages raised with ``Reject`` automatically have any HTML content +escaped before being displayed to the user. To display an error message to the +user without performing any HTML escaping the ``RejectRaw`` should be used. All +security implications should be carefully considering before using +``RejectRaw``. Generating email from Roundup
--- a/roundup/cgi/actions.py Sat Oct 10 23:07:17 2015 +1100 +++ b/roundup/cgi/actions.py Sat Oct 10 23:35:51 2015 +1100 @@ -3,9 +3,9 @@ from roundup import hyperdb, token, date, password from roundup.actions import Action as BaseAction from roundup.i18n import _ -import roundup.exceptions from roundup.cgi import exceptions, templating from roundup.mailgw import uidFromAddress +from roundup.exceptions import Reject, RejectRaw from roundup.anypy import io_, urllib_ __all__ = ['Action', 'ShowAction', 'RetireAction', 'SearchAction', @@ -106,7 +106,7 @@ """Retire the context item.""" # ensure modification comes via POST if self.client.env['REQUEST_METHOD'] != 'POST': - raise roundup.exceptions.Reject(self._('Invalid request')) + raise Reject(self._('Invalid request')) # if we want to view the index template now, then unset the itemid # context info (a special-case for retire actions on the index page) @@ -285,7 +285,7 @@ """ # ensure modification comes via POST if self.client.env['REQUEST_METHOD'] != 'POST': - raise roundup.exceptions.Reject(self._('Invalid request')) + raise Reject(self._('Invalid request')) # figure the properties list for the class cl = self.db.classes[self.classname] @@ -606,7 +606,7 @@ """ # ensure modification comes via POST if self.client.env['REQUEST_METHOD'] != 'POST': - raise roundup.exceptions.Reject(self._('Invalid request')) + raise Reject(self._('Invalid request')) user_activity = self.lastUserActivity() if user_activity: @@ -620,10 +620,10 @@ # handle the props try: message = self._editnodes(props, links) - except (ValueError, KeyError, IndexError, - roundup.exceptions.Reject), message: + except (ValueError, KeyError, IndexError, Reject) as message: + escape = not isinstance(message, RejectRaw) self.client.add_error_message( - self._('Edit Error: %s') % str(message)) + self._('Edit Error: %s') % str(message), escape=escape) return # commit now that all the tricky stuff is done @@ -652,7 +652,7 @@ ''' # ensure modification comes via POST if self.client.env['REQUEST_METHOD'] != 'POST': - raise roundup.exceptions.Reject(self._('Invalid request')) + raise Reject(self._('Invalid request')) # parse the props from the form try: @@ -666,10 +666,11 @@ try: # when it hits the None element, it'll set self.nodeid messages = self._editnodes(props, links) - except (ValueError, KeyError, IndexError, - roundup.exceptions.Reject), message: + except (ValueError, KeyError, IndexError, Reject) as message: + escape = not isinstance(message, RejectRaw) # these errors might just be indicative of user dumbness - self.client.add_error_message(_('Error: %s') % str(message)) + self.client.add_error_message(_('Error: %s') % str(message), + escape=escape) return # commit now that all the tricky stuff is done @@ -833,7 +834,7 @@ """ # ensure modification comes via POST if self.client.env['REQUEST_METHOD'] != 'POST': - raise roundup.exceptions.Reject(self._('Invalid request')) + raise Reject(self._('Invalid request')) # parse the props from the form try: @@ -849,10 +850,11 @@ try: # when it hits the None element, it'll set self.nodeid messages = self._editnodes(props, links) - except (ValueError, KeyError, IndexError, - roundup.exceptions.Reject), message: + except (ValueError, KeyError, IndexError, Reject) as message: + escape = not isinstance(message, RejectRaw) # these errors might just be indicative of user dumbness - self.client.add_error_message(_('Error: %s') % str(message)) + self.client.add_error_message(_('Error: %s') % str(message), + escape=escape) return # fix up the initial roles @@ -957,7 +959,7 @@ """ # ensure modification comes via POST if self.client.env['REQUEST_METHOD'] != 'POST': - raise roundup.exceptions.Reject(self._('Invalid request')) + raise Reject(self._('Invalid request')) # we need the username at a minimum if '__login_name' not in self.form:
--- a/roundup/cgi/client.py Sat Oct 10 23:07:17 2015 +1100 +++ b/roundup/cgi/client.py Sat Oct 10 23:35:51 2015 +1100 @@ -16,7 +16,7 @@ from roundup import roundupdb, date, hyperdb, password from roundup.cgi import templating, cgitb, TranslationService from roundup.cgi.actions import * -from roundup.exceptions import * +from roundup.exceptions import LoginError, Reject, RejectRaw, Unauthorised from roundup.cgi.exceptions import * from roundup.cgi.form_parser import FormParser from roundup.mailer import Mailer, MessageSendError, encode_quopri @@ -1274,9 +1274,9 @@ return getattr(self, action_klass)() else: return action_klass(self).execute() - - except (ValueError, Reject), err: - self.add_error_message(str(err)) + except (ValueError, Reject) as err: + escape = not isinstance(err, RejectRaw) + self.add_error_message(str(err), escape=escape) def get_action_class(self, action_name): if (hasattr(self.instance, 'cgi_actions') and
--- a/roundup/exceptions.py Sat Oct 10 23:07:17 2015 +1100 +++ b/roundup/exceptions.py Sat Oct 10 23:35:51 2015 +1100 @@ -21,6 +21,15 @@ """ pass + +class RejectRaw(Reject): + """ + Performs the same function as Reject, except HTML in the message is not + escaped when displayed to the user. + """ + pass + + class UsageError(ValueError): pass
