[ui messages, xss] Start migration towards use of _msgid instead of __message (prone to XSS injection) closes #1698245

authorArthur Lutz <arthur.lutz@logilab.fr>
changesetcab99ccdb774
branchdefault
phasepublic
hiddenno
parent revision#205d3647194f backport stable
child revision#9aadb5a04b53 merge default heads
files modified by this revision
web/controller.py
web/form.py
web/request.py
web/views/basecontrollers.py
web/views/basetemplates.py
# HG changeset patch
# User Arthur Lutz <arthur.lutz@logilab.fr>
# Date 1306316476 -7200
# Wed May 25 11:41:16 2011 +0200
# Node ID cab99ccdb774d446134800305c32ea3ef75b8c29
# Parent 205d3647194f7cc3fafd79e77fa47807dd688d4a
[ui messages, xss] Start migration towards use of _msgid instead of __message (prone to XSS injection) closes #1698245

diff --git a/web/controller.py b/web/controller.py
@@ -1,6 +1,6 @@
1 -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
2 +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
3  # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
4  #
5  # This file is part of CubicWeb.
6  #
7  # CubicWeb is free software: you can redistribute it and/or modify it under the
@@ -112,21 +112,23 @@
8          msg = format_mail({'email' : senderemail,
9                             'name' : self._cw.user.dc_title(),},
10                            [recipient], body, subject)
11          if not self._cw.vreg.config.sendmails([(msg, [recipient])]):
12              msg = self._cw._('could not connect to the SMTP server')
13 -            url = self._cw.build_url(__message=msg)
14 +            url = self._cw.build_url(__message=msgid)
15              raise Redirect(url)
16 
17      def reset(self):
18          """reset form parameters and redirect to a view determinated by given
19          parameters
20          """
21          newparams = {}
22          # sets message if needed
23 -        if self._cw.message:
24 -            newparams['_cwmsgid'] = self._cw.set_redirect_message(self._cw.message)
25 +        # XXX - don't call .message twice since it pops the id
26 +        msg = self._cw.message
27 +        if msg:
28 +            newparams['_cwmsgid'] = self._cw.set_redirect_message(msg)
29          if self._cw.form.has_key('__action_apply'):
30              self._return_to_edition_view(newparams)
31          if self._cw.form.has_key('__action_cancel'):
32              self._return_to_lastpage(newparams)
33          else:
diff --git a/web/form.py b/web/form.py
@@ -1,6 +1,6 @@
34 -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
35 +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
36  # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
37  #
38  # This file is part of CubicWeb.
39  #
40  # CubicWeb is free software: you can redistribute it and/or modify it under the
@@ -110,11 +110,16 @@
41                  if not param in kwargs:
42                      value = req.form.get(param)
43                      if value:
44                          self.add_hidden(param, value)
45          if submitmsg is not None:
46 -            self.add_hidden(u'__message', submitmsg)
47 +            self.set_message(submitmsg)
48 +
49 +    def set_message(self, submitmsg):
50 +        """sets a submitmsg if exists, using _cwmsgid mechanism """
51 +        cwmsgid = self._cw.set_redirect_message(submitmsg)
52 +        self.add_hidden(u'_cwmsgid', cwmsgid)
53 
54      @property
55      def root_form(self):
56          """return the root form"""
57          if self.parent_form is None:
diff --git a/web/request.py b/web/request.py
@@ -212,10 +212,16 @@
58              if param in self.no_script_form_params and val:
59                  val = self.no_script_form_param(param, val)
60              if param == '_cwmsgid':
61                  self.set_message_id(val)
62              elif param == '__message':
63 +                warn('[3.13] __message in request parameter is deprecated (may '
64 +                     'only be given to .build_url). Seeing this message usualy '
65 +                     'means your application hold some <form> where you should '
66 +                     'replace use of __message hidden input by form.set_message, '
67 +                     'so new _cwmsgid mechanism is properly used',
68 +                     DeprecationWarning)
69                  self.set_message(val)
70              else:
71                  self.form[param] = val
72 
73      def no_script_form_param(self, param, value):
@@ -262,11 +268,11 @@
74      # web state helpers #######################################################
75 
76      @property
77      def message(self):
78          try:
79 -            return self.session.data.pop(self._msgid, '')
80 +            return self.session.data.pop(self._msgid, u'')
81          except AttributeError:
82              try:
83                  return self._msg
84              except AttributeError:
85                  return None
@@ -281,20 +287,21 @@
86      @cached
87      def redirect_message_id(self):
88          return make_uid()
89 
90      def set_redirect_message(self, msg):
91 +        # TODO - this should probably be merged with append_to_redirect_message
92          assert isinstance(msg, unicode)
93          msgid = self.redirect_message_id()
94          self.session.data[msgid] = msg
95          return msgid
96 
97      def append_to_redirect_message(self, msg):
98          msgid = self.redirect_message_id()
99          currentmsg = self.session.data.get(msgid)
100          if currentmsg is not None:
101 -            currentmsg = '%s %s' % (currentmsg, msg)
102 +            currentmsg = u'%s %s' % (currentmsg, msg)
103          else:
104              currentmsg = msg
105          self.session.data[msgid] = currentmsg
106          return msgid
107 
@@ -622,10 +629,20 @@
108          self.html_headers.add_post_inline_script(jscode)
109          return "javascript: %s()" % cbname
110 
111      # urls/path management ####################################################
112 
113 +    def build_url(self, *args, **kwargs):
114 +        """return an absolute URL using params dictionary key/values as URL
115 +        parameters. Values are automatically URL quoted, and the
116 +        publishing method to use may be specified or will be guessed.
117 +        """
118 +        if '__message' in kwargs:
119 +            msg = kwargs.pop('__message')
120 +            kwargs['_cwmsgid'] = self.set_redirect_message(msg)
121 +        return super(CubicWebRequestBase, self).build_url(*args, **kwargs)
122 +
123      def url(self, includeparams=True):
124          """return currently accessed url"""
125          return self.base_url() + self.relative_path(includeparams)
126 
127      def selected(self, url):
diff --git a/web/views/basecontrollers.py b/web/views/basecontrollers.py
@@ -100,11 +100,11 @@
128          #   anonymous connection is allowed and the page will be displayed or
129          #   we'll be redirected to the login form
130          msg = self._cw._('you have been logged out')
131          # force base_url so on dual http/https configuration, we generate an url
132          # on the http version of the site
133 -        return self._cw.build_url('view', vid='index', __message=msg,
134 +        return self._cw.build_url('view', vid='loggedout',
135                                    base_url=self._cw.vreg.config['base-url'])
136 
137 
138  class ViewController(Controller):
139      """standard entry point :
diff --git a/web/views/basetemplates.py b/web/views/basetemplates.py
@@ -23,11 +23,11 @@
140  from logilab.mtconverter import xml_escape
141  from logilab.common.deprecation import class_renamed
142 
143  from cubicweb.appobject import objectify_selector
144  from cubicweb.selectors import match_kwargs, no_cnx, anonymous_user
145 -from cubicweb.view import View, MainTemplate, NOINDEX, NOFOLLOW
146 +from cubicweb.view import View, MainTemplate, NOINDEX, NOFOLLOW, StartupView
147  from cubicweb.utils import UStringIO
148  from cubicweb.schema import display_name
149  from cubicweb.web import component, formfields as ff, formwidgets as fw
150  from cubicweb.web.views import forms
151 
@@ -64,23 +64,23 @@
152 
153      def content(self, w):
154          self.wview('logform', rset=self.cw_rset, id='loginBox', klass='')
155 
156 
157 -class LoggedOutTemplate(LogInOutTemplate):
158 +class LoggedOutTemplate(StartupView):
159      __regid__ = 'loggedout'
160 +    __select__ = anonymous_user()
161      title = 'logged out'
162 
163 -    def content(self, w):
164 -        # FIXME Deprecated code ?
165 +    def call(self):
166          msg = self._cw._('you have been logged out')
167 -        w(u'<h2>%s</h2>\n' % msg)
168 -        if self._cw.vreg.config.anonymous_user()[0]:
169 -            indexurl = self._cw.build_url('view', vid='index', __message=msg)
170 -            w(u'<p><a href="%s">%s</a><p>' % (
171 -                xml_escape(indexurl),
172 -                self._cw._('go back to the index page')))
173 +        if self._cw.cnx:
174 +            comp = self._cw.vreg['components'].select('applmessages', self._cw)
175 +            comp.render(w=self.w, msg=msg)
176 +            self.wview('index')
177 +        else:
178 +            self.w(u'<h2>%s</h2>' % msg)
179 
180 
181  @objectify_selector
182  def templatable_view(cls, req, rset, *args, **kwargs):
183      view = kwargs.pop('view', None)