VYPR
Medium severity5.4NVD Advisory· Published Apr 21, 2017· Updated May 13, 2026

CVE-2016-6519

CVE-2016-6519

Description

Cross-site scripting (XSS) vulnerability in the "Shares" overview in Openstack Manila before 2.5.1 allows remote authenticated users to inject arbitrary web script or HTML via the Metadata field in the "Create Share" form.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

OpenStack Manila before 2.5.1 has a persistent XSS in the Metadata field of the Create Share form, allowing privilege escalation via cookie theft.

Vulnerability

A persistent cross-site scripting (XSS) vulnerability exists in OpenStack Manila (manila-ui) before version 2.5.1. The flaw resides in the 'Shares' overview page, where the Metadata field in the 'Create Share' form unsafely renders user-supplied input due to a mark_safe() call in tabs.py [1][2]. This allows authenticated users to inject arbitrary HTML or JavaScript that is later reflected in the share listing.

Exploitation

An authenticated but unprivileged user can exploit the vulnerability by crafting malicious payloads in the Metadata field when creating a share [2][3]. The injected script executes in the context of any other authenticated user who views the 'Shares' overview, without requiring any additional privileges or direct interaction beyond viewing the page.

Impact

Successful exploitation enables an attacker to steal session cookies from other authenticated users, leading to privilege escalation within the OpenStack dashboard [2][3]. The attacker can then impersonate the victim and perform actions with the victim's permissions, potentially gaining unauthorized access to sensitive data or resources.

Mitigation

The vulnerability is fixed in OpenStack Manila version 2.5.1 [1]. Red Hat issued security updates RHSA-2016:2115 (for Red Hat Enterprise Linux OpenStack Platform 7.0) and RHSA-2016:2117 (for Red Hat OpenStack Platform 9.0) on 2016-10-26 [3][4]. Users should upgrade to the patched version or apply the relevant vendor updates. No known workarounds are documented.

AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
manila-uiPyPI
< 2.5.12.5.1

Affected products

3

Patches

3
009913d725be

Fix metadata_to_str function code injection vulnerability

https://github.com/openstack/manila-uiValeriy PonomaryovJun 30, 2016via ghsa
5 files changed · +46 20
  • manila_ui/dashboards/admin/shares/tabs.py+3 5 modified
    @@ -12,7 +12,6 @@
     #    License for the specific language governing permissions and limitations
     #    under the License.
     
    -from django.utils.safestring import mark_safe
     from django.utils.translation import ugettext_lazy as _
     
     from horizon import exceptions
    @@ -25,6 +24,7 @@
     from manila_ui.api import network
     from manila_ui.dashboards.admin.shares import tables
     from manila_ui.dashboards.admin.shares import utils
    +from manila_ui.dashboards import utils as common_utils
     
     
     class SnapshotsTab(tabs.TableTab):
    @@ -104,10 +104,8 @@ def get_share_types_data(self):
                                   _("Unable to retrieve share types"))
             # Convert dict with extra specs to friendly view
             for st in share_types:
    -            es_str = ""
    -            for k, v in st.extra_specs.iteritems():
    -                es_str += "%s=%s\r\n<br />" % (k, v)
    -            st.extra_specs = mark_safe(es_str)
    +            st.extra_specs = common_utils.metadata_to_str(
    +                st.extra_specs, 8, 45)
             return share_types
     
     
    
  • manila_ui/dashboards/project/shares/shares/tables.py+1 3 modified
    @@ -17,7 +17,6 @@
     from django.core.urlresolvers import NoReverseMatch  # noqa
     from django.core.urlresolvers import reverse
     from django.template.defaultfilters import title  # noqa
    -from django.utils.safestring import mark_safe
     from django.utils.translation import string_concat, ugettext_lazy  # noqa
     from django.utils.translation import pgettext_lazy
     from django.utils.translation import ugettext_lazy as _
    @@ -150,8 +149,7 @@ def get_data(self, request, share_id):
                 share.share_network = share_net.name or share_net.id
             else:
                 share.share_network = None
    -        meta_str = utils.metadata_to_str(share.metadata)
    -        share.metadata = mark_safe(meta_str)
    +        share.metadata = utils.metadata_to_str(share.metadata)
     
             return share
     
    
  • manila_ui/dashboards/project/shares/shares/tabs.py+4 6 modified
    @@ -10,7 +10,6 @@
     #    License for the specific language governing permissions and limitations
     #    under the License.
     
    -from django.utils.safestring import mark_safe
     from django.utils.translation import ugettext_lazy as _
     
     from horizon import exceptions
    @@ -42,11 +41,10 @@ def get_shares_data(self):
             try:
                 shares = manila.share_list(self.request)
                 for share in shares:
    -                share.share_network = \
    -                    share_nets_names.get(share.share_network_id) or \
    -                    share.share_network_id
    -                meta_str = utils.metadata_to_str(share.metadata)
    -                share.metadata = mark_safe(meta_str)
    +                share.share_network = (
    +                    share_nets_names.get(share.share_network_id) or
    +                    share.share_network_id)
    +                share.metadata = utils.metadata_to_str(share.metadata)
     
                 snapshots = manila.share_snapshot_list(self.request, detailed=True)
                 share_ids_with_snapshots = []
    
  • manila_ui/dashboards/utils.py+18 6 modified
    @@ -13,9 +13,23 @@
     #    under the License.
     
     from django.forms import ValidationError  # noqa
    +from django.utils.safestring import mark_safe
     from django.utils.translation import ugettext_lazy as _
     
     
    +html_escape_table = {
    +    "&": "&amp;",
    +    '"': "&quot;",
    +    "'": "&apos;",
    +    ">": "&gt;",
    +    "<": "&lt;",
    +}
    +
    +
    +def html_escape(text):
    +    return ''.join(html_escape_table.get(s, s) for s in text)
    +
    +
     def parse_str_meta(meta_s):
         """Parse multiline string with data from form.
     
    @@ -58,14 +72,12 @@ def parse_str_meta(meta_s):
         return set_dict, unset_list
     
     
    -def metadata_to_str(metadata):
    +def metadata_to_str(metadata, meta_visible_limit=4, text_length_limit=25):
     
         # Only convert dictionaries
         if not hasattr(metadata, 'keys'):
             return metadata
     
    -    meta_visible_limit = 4
    -    text_length_limit = 25
         meta = []
         meta_keys = metadata.keys()
         meta_keys.sort()
    @@ -77,8 +89,8 @@ def metadata_to_str(metadata):
             v = metadata[k]
             if len(v) > text_length_limit:
                 v = v[:text_length_limit] + '...'
    -        meta.append("%s = %s" % (k_shortenned, v))
    +        meta.append("%s = %s" % (html_escape(k_shortenned), html_escape(v)))
         meta_str = "<br/>".join(meta)
    -    if len(metadata.keys()) > meta_visible_limit:
    +    if len(metadata.keys()) > meta_visible_limit and meta_str[-3:] != "...":
             meta_str += '...'
    -    return meta_str
    +    return mark_safe(meta_str)
    
  • manila_ui/test/dashboards/test_utils.py+20 0 modified
    @@ -61,3 +61,23 @@ def test_parse_str_meta_success(
         )
         def test_parse_str_meta_validation_error(self, input_data):
             self.assertRaises(ValidationError, utils.parse_str_meta, input_data)
    +
    +    @ddt.data(
    +        (({"a": "<script>alert('A')/*", "b": "*/</script>"}, ),
    +         "a = &lt;script&gt;alert(&apos;A&apos;)/*<br/>b = */&lt;/script&gt;"),
    +        (({"fookey": "foovalue", "barkey": "barvalue"}, ),
    +         "barkey = barvalue<br/>fookey = foovalue"),
    +        (({"foo": "barquuz"}, 1, 2), "fo... = ba..."),
    +        (({"foo": "barquuz", "zfoo": "zbarquuz"}, 1, 3), "foo = bar..."),
    +        (({"foo": "barquuz", "zfoo": "zbarquuz"}, 2, 3),
    +         "foo = bar...<br/>zfo... = zba..."),
    +        (({"foo": "barquuz", "zfoo": "zbarquuz"}, 3, 3),
    +         "foo = bar...<br/>zfo... = zba..."),
    +        (({"foo": "barquuz", "zfoo": "zbarquuz"}, 3, 8),
    +         "foo = barquuz<br/>zfoo = zbarquuz"),
    +    )
    +    @ddt.unpack
    +    def test_metadata_to_str(self, input_args, expected_output):
    +        result = utils.metadata_to_str(*input_args)
    +
    +        self.assertEqual(expected_output, result)
    
fca19a1b0d42

Fix metadata_to_str function code injection vulnerability

https://github.com/openstack/manila-uiValeriy PonomaryovJun 30, 2016via ghsa
5 files changed · +46 20
  • manila_ui/dashboards/admin/shares/tabs.py+3 5 modified
    @@ -12,7 +12,6 @@
     #    License for the specific language governing permissions and limitations
     #    under the License.
     
    -from django.utils.safestring import mark_safe
     from django.utils.translation import ugettext_lazy as _
     
     from horizon import exceptions
    @@ -25,6 +24,7 @@
     from manila_ui.api import network
     from manila_ui.dashboards.admin.shares import tables
     from manila_ui.dashboards.admin.shares import utils
    +from manila_ui.dashboards import utils as common_utils
     
     
     class SnapshotsTab(tabs.TableTab):
    @@ -104,10 +104,8 @@ def get_share_types_data(self):
                                   _("Unable to retrieve share types"))
             # Convert dict with extra specs to friendly view
             for st in share_types:
    -            es_str = ""
    -            for k, v in st.extra_specs.iteritems():
    -                es_str += "%s=%s\r\n<br />" % (k, v)
    -            st.extra_specs = mark_safe(es_str)
    +            st.extra_specs = common_utils.metadata_to_str(
    +                st.extra_specs, 8, 45)
             return share_types
     
     
    
  • manila_ui/dashboards/project/shares/shares/tables.py+1 3 modified
    @@ -15,7 +15,6 @@
     from django.core.urlresolvers import NoReverseMatch  # noqa
     from django.core.urlresolvers import reverse
     from django.template.defaultfilters import title  # noqa
    -from django.utils.safestring import mark_safe
     from django.utils.translation import string_concat, ugettext_lazy  # noqa
     from django.utils.translation import pgettext_lazy
     from django.utils.translation import ugettext_lazy as _
    @@ -149,8 +148,7 @@ def get_data(self, request, share_id):
                 share.share_network = share_net.name or share_net.id
             else:
                 share.share_network = None
    -        meta_str = utils.metadata_to_str(share.metadata)
    -        share.metadata = mark_safe(meta_str)
    +        share.metadata = utils.metadata_to_str(share.metadata)
     
             return share
     
    
  • manila_ui/dashboards/project/shares/shares/tabs.py+4 6 modified
    @@ -10,7 +10,6 @@
     #    License for the specific language governing permissions and limitations
     #    under the License.
     
    -from django.utils.safestring import mark_safe
     from django.utils.translation import ugettext_lazy as _
     
     from horizon import exceptions
    @@ -42,11 +41,10 @@ def get_shares_data(self):
             try:
                 shares = manila.share_list(self.request)
                 for share in shares:
    -                share.share_network = \
    -                    share_nets_names.get(share.share_network_id) or \
    -                    share.share_network_id
    -                meta_str = utils.metadata_to_str(share.metadata)
    -                share.metadata = mark_safe(meta_str)
    +                share.share_network = (
    +                    share_nets_names.get(share.share_network_id) or
    +                    share.share_network_id)
    +                share.metadata = utils.metadata_to_str(share.metadata)
     
                 snapshots = manila.share_snapshot_list(self.request, detailed=True)
                 share_ids_with_snapshots = []
    
  • manila_ui/dashboards/utils.py+18 6 modified
    @@ -13,9 +13,23 @@
     #    under the License.
     
     from django.forms import ValidationError  # noqa
    +from django.utils.safestring import mark_safe
     from django.utils.translation import ugettext_lazy as _
     
     
    +html_escape_table = {
    +    "&": "&amp;",
    +    '"': "&quot;",
    +    "'": "&apos;",
    +    ">": "&gt;",
    +    "<": "&lt;",
    +}
    +
    +
    +def html_escape(text):
    +    return ''.join(html_escape_table.get(s, s) for s in text)
    +
    +
     def parse_str_meta(meta_s):
         """Parse multiline string with data from form.
     
    @@ -58,14 +72,12 @@ def parse_str_meta(meta_s):
         return set_dict, unset_list
     
     
    -def metadata_to_str(metadata):
    +def metadata_to_str(metadata, meta_visible_limit=4, text_length_limit=25):
     
         # Only convert dictionaries
         if not hasattr(metadata, 'keys'):
             return metadata
     
    -    meta_visible_limit = 4
    -    text_length_limit = 25
         meta = []
         meta_keys = metadata.keys()
         meta_keys.sort()
    @@ -77,11 +89,11 @@ def metadata_to_str(metadata):
             v = metadata[k]
             if len(v) > text_length_limit:
                 v = v[:text_length_limit] + '...'
    -        meta.append("%s = %s" % (k_shortenned, v))
    +        meta.append("%s = %s" % (html_escape(k_shortenned), html_escape(v)))
         meta_str = "<br/>".join(meta)
    -    if len(metadata.keys()) > meta_visible_limit:
    +    if len(metadata.keys()) > meta_visible_limit and meta_str[-3:] != "...":
             meta_str += '...'
    -    return meta_str
    +    return mark_safe(meta_str)
     
     
     def get_nice_security_service_type(security_service):
    
  • manila_ui/tests/dashboards/test_utils.py+20 0 modified
    @@ -62,6 +62,26 @@ def test_parse_str_meta_success(
         def test_parse_str_meta_validation_error(self, input_data):
             self.assertRaises(ValidationError, utils.parse_str_meta, input_data)
     
    +    @ddt.data(
    +        (({"a": "<script>alert('A')/*", "b": "*/</script>"}, ),
    +         "a = &lt;script&gt;alert(&apos;A&apos;)/*<br/>b = */&lt;/script&gt;"),
    +        (({"fookey": "foovalue", "barkey": "barvalue"}, ),
    +         "barkey = barvalue<br/>fookey = foovalue"),
    +        (({"foo": "barquuz"}, 1, 2), "fo... = ba..."),
    +        (({"foo": "barquuz", "zfoo": "zbarquuz"}, 1, 3), "foo = bar..."),
    +        (({"foo": "barquuz", "zfoo": "zbarquuz"}, 2, 3),
    +         "foo = bar...<br/>zfo... = zba..."),
    +        (({"foo": "barquuz", "zfoo": "zbarquuz"}, 3, 3),
    +         "foo = bar...<br/>zfo... = zba..."),
    +        (({"foo": "barquuz", "zfoo": "zbarquuz"}, 3, 8),
    +         "foo = barquuz<br/>zfoo = zbarquuz"),
    +    )
    +    @ddt.unpack
    +    def test_metadata_to_str(self, input_args, expected_output):
    +        result = utils.metadata_to_str(*input_args)
    +
    +        self.assertEqual(expected_output, result)
    +
         @ddt.data(
             ("ldap", "LDAP"),
             ("active_directory", "Active Directory"),
    
89593686ef18

Fix metadata_to_str function code injection vulnerability

https://github.com/openstack/manila-uiValeriy PonomaryovJun 30, 2016via ghsa
5 files changed · +46 20
  • manila_ui/dashboards/admin/shares/tabs.py+3 5 modified
    @@ -12,7 +12,6 @@
     #    License for the specific language governing permissions and limitations
     #    under the License.
     
    -from django.utils.safestring import mark_safe
     from django.utils.translation import ugettext_lazy as _
     
     from horizon import exceptions
    @@ -25,6 +24,7 @@
     from manila_ui.api import network
     from manila_ui.dashboards.admin.shares import tables
     from manila_ui.dashboards.admin.shares import utils
    +from manila_ui.dashboards import utils as common_utils
     
     
     class SnapshotsTab(tabs.TableTab):
    @@ -104,10 +104,8 @@ def get_share_types_data(self):
                                   _("Unable to retrieve share types"))
             # Convert dict with extra specs to friendly view
             for st in share_types:
    -            es_str = ""
    -            for k, v in st.extra_specs.iteritems():
    -                es_str += "%s=%s\r\n<br />" % (k, v)
    -            st.extra_specs = mark_safe(es_str)
    +            st.extra_specs = common_utils.metadata_to_str(
    +                st.extra_specs, 8, 45)
             return share_types
     
     
    
  • manila_ui/dashboards/project/shares/shares/tables.py+1 3 modified
    @@ -17,7 +17,6 @@
     from django.core.urlresolvers import NoReverseMatch  # noqa
     from django.core.urlresolvers import reverse
     from django.template.defaultfilters import title  # noqa
    -from django.utils.safestring import mark_safe
     from django.utils.translation import string_concat, ugettext_lazy  # noqa
     from django.utils.translation import pgettext_lazy
     from django.utils.translation import ugettext_lazy as _
    @@ -150,8 +149,7 @@ def get_data(self, request, share_id):
                 share.share_network = share_net.name or share_net.id
             else:
                 share.share_network = None
    -        meta_str = utils.metadata_to_str(share.metadata)
    -        share.metadata = mark_safe(meta_str)
    +        share.metadata = utils.metadata_to_str(share.metadata)
     
             return share
     
    
  • manila_ui/dashboards/project/shares/shares/tabs.py+4 6 modified
    @@ -10,7 +10,6 @@
     #    License for the specific language governing permissions and limitations
     #    under the License.
     
    -from django.utils.safestring import mark_safe
     from django.utils.translation import ugettext_lazy as _
     
     from horizon import exceptions
    @@ -42,11 +41,10 @@ def get_shares_data(self):
             try:
                 shares = manila.share_list(self.request)
                 for share in shares:
    -                share.share_network = \
    -                    share_nets_names.get(share.share_network_id) or \
    -                    share.share_network_id
    -                meta_str = utils.metadata_to_str(share.metadata)
    -                share.metadata = mark_safe(meta_str)
    +                share.share_network = (
    +                    share_nets_names.get(share.share_network_id) or
    +                    share.share_network_id)
    +                share.metadata = utils.metadata_to_str(share.metadata)
     
                 snapshots = manila.share_snapshot_list(self.request, detailed=True)
                 share_ids_with_snapshots = []
    
  • manila_ui/dashboards/utils.py+18 6 modified
    @@ -13,9 +13,23 @@
     #    under the License.
     
     from django.forms import ValidationError  # noqa
    +from django.utils.safestring import mark_safe
     from django.utils.translation import ugettext_lazy as _
     
     
    +html_escape_table = {
    +    "&": "&amp;",
    +    '"': "&quot;",
    +    "'": "&apos;",
    +    ">": "&gt;",
    +    "<": "&lt;",
    +}
    +
    +
    +def html_escape(text):
    +    return ''.join(html_escape_table.get(s, s) for s in text)
    +
    +
     def parse_str_meta(meta_s):
         """Parse multiline string with data from form.
     
    @@ -58,14 +72,12 @@ def parse_str_meta(meta_s):
         return set_dict, unset_list
     
     
    -def metadata_to_str(metadata):
    +def metadata_to_str(metadata, meta_visible_limit=4, text_length_limit=25):
     
         # Only convert dictionaries
         if not hasattr(metadata, 'keys'):
             return metadata
     
    -    meta_visible_limit = 4
    -    text_length_limit = 25
         meta = []
         meta_keys = metadata.keys()
         meta_keys.sort()
    @@ -77,8 +89,8 @@ def metadata_to_str(metadata):
             v = metadata[k]
             if len(v) > text_length_limit:
                 v = v[:text_length_limit] + '...'
    -        meta.append("%s = %s" % (k_shortenned, v))
    +        meta.append("%s = %s" % (html_escape(k_shortenned), html_escape(v)))
         meta_str = "<br/>".join(meta)
    -    if len(metadata.keys()) > meta_visible_limit:
    +    if len(metadata.keys()) > meta_visible_limit and meta_str[-3:] != "...":
             meta_str += '...'
    -    return meta_str
    +    return mark_safe(meta_str)
    
  • manila_ui/test/dashboards/test_utils.py+20 0 modified
    @@ -61,3 +61,23 @@ def test_parse_str_meta_success(
         )
         def test_parse_str_meta_validation_error(self, input_data):
             self.assertRaises(ValidationError, utils.parse_str_meta, input_data)
    +
    +    @ddt.data(
    +        (({"a": "<script>alert('A')/*", "b": "*/</script>"}, ),
    +         "a = &lt;script&gt;alert(&apos;A&apos;)/*<br/>b = */&lt;/script&gt;"),
    +        (({"fookey": "foovalue", "barkey": "barvalue"}, ),
    +         "barkey = barvalue<br/>fookey = foovalue"),
    +        (({"foo": "barquuz"}, 1, 2), "fo... = ba..."),
    +        (({"foo": "barquuz", "zfoo": "zbarquuz"}, 1, 3), "foo = bar..."),
    +        (({"foo": "barquuz", "zfoo": "zbarquuz"}, 2, 3),
    +         "foo = bar...<br/>zfo... = zba..."),
    +        (({"foo": "barquuz", "zfoo": "zbarquuz"}, 3, 3),
    +         "foo = bar...<br/>zfo... = zba..."),
    +        (({"foo": "barquuz", "zfoo": "zbarquuz"}, 3, 8),
    +         "foo = barquuz<br/>zfoo = zbarquuz"),
    +    )
    +    @ddt.unpack
    +    def test_metadata_to_str(self, input_args, expected_output):
    +        result = utils.metadata_to_str(*input_args)
    +
    +        self.assertEqual(expected_output, result)
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

12

News mentions

0

No linked articles in our index yet.