1 Upstream patch fixed in Havana 2013.2.4 and Icehouse 2014.1.2 |
|
2 |
|
3 From c844bd692894353c60b320005b804970605e910f Mon Sep 17 00:00:00 2001 |
|
4 From: Julie Pichon <[email protected]> |
|
5 Date: Thu, 22 May 2014 16:45:03 +0100 |
|
6 Subject: [PATCH] Fix multiple Cross-Site Scripting (XSS) vulnerabilities |
|
7 |
|
8 * Ensure user emails are properly escaped |
|
9 |
|
10 User emails in the Users and Groups panel are being passed through the |
|
11 urlize filter to transform them into clickable links. However, urlize |
|
12 expects input to be already escaped and safe. We should make sure to |
|
13 escape the strings first as email addresses are not validated and can |
|
14 contain any type of string. |
|
15 |
|
16 Closes-Bug: #1320235 |
|
17 |
|
18 * Ensure network names are properly escaped in the Launch Instance menu |
|
19 |
|
20 Closes-Bug: #1322197 |
|
21 |
|
22 * Escape the URLs generated for the Horizon tables |
|
23 |
|
24 When generating the Horizon tables, there was an assumption that only |
|
25 the anchor text needed to be escaped. However some URLs are generated |
|
26 based on user-provided data and should be escaped as well. Also escape |
|
27 the link attributes for good measure. |
|
28 |
|
29 * Use 'reverse' to generate the Resource URLs in the stacks tables |
|
30 |
|
31 Closes-Bug: #1308727 |
|
32 |
|
33 Conflicts: |
|
34 horizon/tables/base.py |
|
35 openstack_dashboard/dashboards/admin/users/tables.py |
|
36 |
|
37 Change-Id: Ic8a92e69f66c2d265a802f350e30f091181aa42e |
|
38 --- |
|
39 horizon/static/horizon/js/horizon.instances.js | 9 ++++++++- |
|
40 horizon/tables/base.py | 4 +++- |
|
41 .../dashboards/admin/groups/tables.py | 3 ++- |
|
42 .../dashboards/admin/users/tables.py | 3 ++- |
|
43 .../dashboards/project/stacks/tables.py | 10 ++++++++-- |
|
44 .../dashboards/project/stacks/tabs.py | 6 ++++++ |
|
45 6 files changed, 29 insertions(+), 6 deletions(-) |
|
46 |
|
47 diff --git a/horizon/static/horizon/js/horizon.instances.js b/horizon/static/horizon/js/horizon.instances.js |
|
48 index c901180..c6ff323 100644 |
|
49 --- a/horizon/static/horizon/js/horizon.instances.js |
|
50 +++ b/horizon/static/horizon/js/horizon.instances.js |
|
51 @@ -51,8 +51,15 @@ horizon.instances = { |
|
52 $(this.get_network_element("")).each(function(){ |
|
53 var $this = $(this); |
|
54 var $input = $this.children("input"); |
|
55 + var name = $this.text().replace(/^\s+/,"") |
|
56 + .replace(/&/g, '&') |
|
57 + .replace(/</g, '<') |
|
58 + .replace(/>/g, '>') |
|
59 + .replace(/"/g, '"') |
|
60 + .replace(/'/g, ''') |
|
61 + .replace(/\//g, '/'); |
|
62 var network_property = { |
|
63 - name:$this.text().replace(/^\s+/,""), |
|
64 + name:name, |
|
65 id:$input.attr("id"), |
|
66 value:$input.attr("value") |
|
67 }; |
|
68 diff --git a/horizon/tables/base.py b/horizon/tables/base.py |
|
69 index adc284c..9011b77 100644 |
|
70 --- a/horizon/tables/base.py |
|
71 +++ b/horizon/tables/base.py |
|
72 @@ -585,7 +585,9 @@ class Cell(html.HTMLElement): |
|
73 link_classes = ' '.join(self.column.link_classes) |
|
74 # Escape the data inside while allowing our HTML to render |
|
75 data = mark_safe('<a href="%s" class="%s">%s</a>' % |
|
76 - (self.url, link_classes, escape(data))) |
|
77 + (escape(self.url), |
|
78 + escape(link_classes), |
|
79 + escape(data))) |
|
80 return data |
|
81 |
|
82 @property |
|
83 diff --git a/openstack_dashboard/dashboards/admin/groups/tables.py b/openstack_dashboard/dashboards/admin/groups/tables.py |
|
84 index bce8f50..ff8103b 100644 |
|
85 --- a/openstack_dashboard/dashboards/admin/groups/tables.py |
|
86 +++ b/openstack_dashboard/dashboards/admin/groups/tables.py |
|
87 @@ -161,7 +161,8 @@ class AddMembersLink(tables.LinkAction): |
|
88 class UsersTable(tables.DataTable): |
|
89 name = tables.Column('name', verbose_name=_('User Name')) |
|
90 email = tables.Column('email', verbose_name=_('Email'), |
|
91 - filters=[defaultfilters.urlize]) |
|
92 + filters=[defaultfilters.escape, |
|
93 + defaultfilters.urlize]) |
|
94 id = tables.Column('id', verbose_name=_('User ID')) |
|
95 enabled = tables.Column('enabled', verbose_name=_('Enabled'), |
|
96 status=True, |
|
97 diff --git a/openstack_dashboard/dashboards/admin/users/tables.py b/openstack_dashboard/dashboards/admin/users/tables.py |
|
98 index d47d68d..c0b0ea5 100644 |
|
99 --- a/openstack_dashboard/dashboards/admin/users/tables.py |
|
100 +++ b/openstack_dashboard/dashboards/admin/users/tables.py |
|
101 @@ -117,7 +117,8 @@ class UsersTable(tables.DataTable): |
|
102 ) |
|
103 name = tables.Column('name', verbose_name=_('User Name')) |
|
104 email = tables.Column('email', verbose_name=_('Email'), |
|
105 - filters=[defaultfilters.urlize]) |
|
106 + filters=[defaultfilters.escape, |
|
107 + defaultfilters.urlize]) |
|
108 # Default tenant is not returned from Keystone currently. |
|
109 #default_tenant = tables.Column('default_tenant', |
|
110 # verbose_name=_('Default Project')) |
|
111 diff --git a/openstack_dashboard/dashboards/project/stacks/tables.py b/openstack_dashboard/dashboards/project/stacks/tables.py |
|
112 index f0bc731..822726b 100644 |
|
113 --- a/openstack_dashboard/dashboards/project/stacks/tables.py |
|
114 +++ b/openstack_dashboard/dashboards/project/stacks/tables.py |
|
115 @@ -12,6 +12,7 @@ |
|
116 # License for the specific language governing permissions and limitations |
|
117 # under the License. |
|
118 |
|
119 +from django.core import urlresolvers |
|
120 from django.http import Http404 # noqa |
|
121 from django.template.defaultfilters import timesince # noqa |
|
122 from django.template.defaultfilters import title # noqa |
|
123 @@ -94,11 +95,16 @@ class StacksTable(tables.DataTable): |
|
124 row_actions = (DeleteStack, ) |
|
125 |
|
126 |
|
127 +def get_resource_url(obj): |
|
128 + return urlresolvers.reverse('horizon:project:stacks:resource', |
|
129 + args=(obj.stack_id, obj.resource_name)) |
|
130 + |
|
131 + |
|
132 class EventsTable(tables.DataTable): |
|
133 |
|
134 logical_resource = tables.Column('resource_name', |
|
135 verbose_name=_("Stack Resource"), |
|
136 - link=lambda d: d.resource_name,) |
|
137 + link=get_resource_url) |
|
138 physical_resource = tables.Column('physical_resource_id', |
|
139 verbose_name=_("Resource"), |
|
140 link=mappings.resource_to_url) |
|
141 @@ -142,7 +148,7 @@ class ResourcesTable(tables.DataTable): |
|
142 |
|
143 logical_resource = tables.Column('resource_name', |
|
144 verbose_name=_("Stack Resource"), |
|
145 - link=lambda d: d.resource_name) |
|
146 + link=get_resource_url) |
|
147 physical_resource = tables.Column('physical_resource_id', |
|
148 verbose_name=_("Resource"), |
|
149 link=mappings.resource_to_url) |
|
150 diff --git a/openstack_dashboard/dashboards/project/stacks/tabs.py b/openstack_dashboard/dashboards/project/stacks/tabs.py |
|
151 index 15ef833..b5886f3 100644 |
|
152 --- a/openstack_dashboard/dashboards/project/stacks/tabs.py |
|
153 +++ b/openstack_dashboard/dashboards/project/stacks/tabs.py |
|
154 @@ -75,6 +75,9 @@ class StackEventsTab(tabs.Tab): |
|
155 stack_identifier = '%s/%s' % (stack.stack_name, stack.id) |
|
156 events = api.heat.events_list(self.request, stack_identifier) |
|
157 LOG.debug('got events %s' % events) |
|
158 + # The stack id is needed to generate the resource URL. |
|
159 + for event in events: |
|
160 + event.stack_id = stack.id |
|
161 except Exception: |
|
162 events = [] |
|
163 messages.error(request, _( |
|
164 @@ -95,6 +98,9 @@ class StackResourcesTab(tabs.Tab): |
|
165 stack_identifier = '%s/%s' % (stack.stack_name, stack.id) |
|
166 resources = api.heat.resources_list(self.request, stack_identifier) |
|
167 LOG.debug('got resources %s' % resources) |
|
168 + # The stack id is needed to generate the resource URL. |
|
169 + for r in resources: |
|
170 + r.stack_id = stack.id |
|
171 except Exception: |
|
172 resources = [] |
|
173 messages.error(request, _( |
|
174 -- |
|
175 1.7.9.5 |
|