CVE-2022-4527: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: A vulnerability was found in collective.task up to 3.0.8. It has been classified as problematic. This affects the function renderCell/AssignedGroupColumn of the file src/collective/task/browser/table.py. The manipulation leads to cross site scripting. It is possible to initiate the attack remotely. Upgrading to version 3.0.9 is able to address this issue. The name of the patch is 1aac7f83fa2c2b41d59ba02748912953461f3fac. It is recommended to upgrade the affected component. The associated identifier of this vulnerability is VDB-215907.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 6.1
Published: 2022-12-15T21:15Z
Repository: https://github.com/collective/collective.task
Language: Python
Files changed: CHANGES.rst, setup.py, src/collective/task/browser/table.py
Commit date: 2022-06-10 10:56:09 +0200
Commit message: Escaped some column rendering to avoid xss

Diff: diff --git a/CHANGES.rst b/CHANGES.rst
index 14993a7..80f0174 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -2,12 +2,13 @@ Changelog
 =========
 
 3.0.9 (unreleased)
 ------------------
 
-- Nothing changed yet.
-
+- Required z3c.table 2.2 to include escaping, to avoid xss.
+  Escaped some columns.
+  [sgeulette]
 
 3.0.8 (2022-05-13)
 ------------------
 
 - Added `get_methods_adapter` on Task content class. So it's possible to get the adapter
diff --git a/setup.py b/setup.py
index 3d6eb6d..2fd7d2c 100644
--- a/setup.py
+++ b/setup.py
@@ -45,14 +45,15 @@ setup(
         'plone.api',
         'plone.app.lockingbehavior',
         'plone.directives.form',
         'plone.formwidget.masterselect',
         'plone.principalsource',
+        'future',
         'imio.helpers',
         'imio.migrator',
         'setuptools',
-        'z3c.table',
+        'z3c.table>=2.2',
     ],
     extras_require={
         'test': [
             'collective.eeafaceted.batchactions',
             'imio.prettylink',
diff --git a/src/collective/task/browser/table.py b/src/collective/task/browser/table.py
index 5dc3ccc..0c13e24 100644
--- a/src/collective/task/browser/table.py
+++ b/src/collective/task/browser/table.py
@@ -2,10 +2,12 @@
 """Define tables and columns."""
 
 from collective.task import _
 from collective.task import PMF
 from collective.task.adapters import EMPTY_STRING
+from html import escape
+from imio.helpers.adapters import NoEscapeLinkColumn
 from plone import api
 from Products.CMFPlone.utils import normalizeString
 from Products.CMFPlone.utils import safe_unicode
 from z3c.table.column import Column
 from z3c.table.column import LinkColumn
@@ -18,11 +20,10 @@ try:
 except ImportError:
     pass
 
 
 class TasksTable(Table):
-
     """Table that displays tasks info."""
 
     cssClassEven = u'even'
     cssClassOdd = u'odd'
     cssClasses = {'table': 'listing taskContainerListing icons-on'}
@@ -47,26 +48,24 @@ class TasksTable(Table):
     def values(self):
         return self.results
 
 
 class UserColumn(Column):
-
     """Base user column."""
 
     field = NotImplemented
 
     def renderCell(self, value):
         username = getattr(value, self.field, '')
         if username and username != EMPTY_STRING:
             member = api.user.get(username)
-            return member.getUser().getProperty('fullname').decode('utf-8')
+            return escape(member.getUser().getProperty('fullname').decode('utf-8'))
 
         return ""
 
 
 class TitleColumn(LinkColumn):
-
     """Column that displays title."""
 
     header = PMF("Title")
     weight = 10
 
@@ -77,11 +76,10 @@ class TitleColumn(LinkColumn):
     def getLinkContent(self, item):
         return safe_unicode(item.title)
 
 
 class PrettyLinkTitleColumn(TitleColumn):
-
     """Column that displays prettylink title."""
 
     header = PMF("Title")
     weight = 10
 
@@ -97,44 +95,39 @@ class PrettyLinkTitleColumn(TitleColumn):
         """ """
         return self.getPrettyLink(item)
 
 
 class EnquirerColumn(UserColumn):
-
     """Column that displays enquirer."""
 
     header = _("Enquirer")
     weight = 20
     field = 'enquirer'
 
 
 class AssignedGroupColumn(Column):
-
     """Column that displays assigned group."""
 
     header = _("Assigned group")
     weight = 30
 
     def renderCell(self, value):
         if value.assigned_group:
             group = api.group.get(value.assigned_group).getGroup()
-            return group.getProperty('title').decode('utf-8')
-
+            return escape(group.getProperty('title').decode('utf-8'))
         return ""
 
 
 class AssignedUserColumn(UserColumn):
-
     """Column that displays assigned user."""
 
     header = _("Assigned user")
     weight = 40
     field = 'assigned_user'
 
 
 class DueDateColumn(Column):
-
     """Column that displays due date."""
 
     header = _("Due date")
     weight = 50
     long_format = False
@@ -142,24 +135,21 @@ class DueDateColumn(Column):
 
     def renderCell(self, value):
         if value.due_date:
             return api.portal.get_localized_time(datetime=value.due_date, long_format=self.long_format,
                                                  time_only=self.time_only)
-
         return ""
 
 
 class ReviewStateColumn(Column):
-
     """Column that displays value's review state."""
 
     header = PMF("Review state")
     weight = 60
 
     def renderCell(self, value):
         state = api.content.get_state(value)
         if state:
             wtool = api.portal.get_tool('portal_workflow')
             state_title = wtool.getTitleForStateOnType(state, value.portal_type)
-            return translate(PMF(state_title), context=self.request)
-
+            return escape(translate(PMF(state_title), context=self.request))
         return ''

Vulnerable code: - Nothing changed yet.

        'z3c.table',


            return member.getUser().getProperty('fullname').decode('utf-8')




            return group.getProperty('title').decode('utf-8')





            return translate(PMF(state_title), context=self.request)

Fixed code: - Required z3c.table 2.2 to include escaping, to avoid xss.
  Escaped some columns.
  [sgeulette]
        'future',
        'z3c.table>=2.2',
from html import escape
from imio.helpers.adapters import NoEscapeLinkColumn
            return escape(member.getUser().getProperty('fullname').decode('utf-8'))
            return escape(group.getProperty('title').decode('utf-8'))
            return escape(translate(PMF(state_title), context=self.request))