zopefoundation / zope.i18nmessageid

Other
1 stars 11 forks source link

Mapping of zope.messageid.Message can be modified #1

Closed tseaver closed 1 month ago

tseaver commented 9 years ago

In https://bugs.launchpad.net/zope.i18nmessageid/+bug/235469, Christian Zagrodnick reported:

Currently one can do the following:

    msgid = _(u'foo', mapping = {})
    msgid.mapping['bar'] = something

So in a way a non modifiable object will be modified. Shouldn't we prevent this?

tseaver commented 1 month ago

Wondered if this had been fixed, but no:

$ .tox/py313/bin/python
Python 3.13.0b1 (main, May 18 2024, 09:26:33) [GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from zope.i18nmessageid import ZopeMessageFactory as _
>>> msgid = _('foo', mapping={})
>>> msgid.mapping['bar'] = 'baz'
tseaver commented 1 month ago

Fixing this in Python should just be:

--- a/src/zope/i18nmessageid/message.py
+++ b/src/zope/i18nmessageid/message.py
@@ -13,6 +13,8 @@
 ##############################################################################
 """I18n Messages and factories.
 """
+import types
+

 __docformat__ = "reStructuredText"
 _marker = object()
@@ -55,7 +57,7 @@ class Message(str):
         if default is not _marker:
             self.default = default
         if mapping is not _marker:
-            self.mapping = mapping
+            self.mapping = types.MappingProxyType(mapping)
         if msgid_plural is not _marker:
             self.msgid_plural = msgid_plural
         if default_plural is not _marker:

with the equivalent change in C:

--- a/src/zope/i18nmessageid/_zope_i18nmessageid_message.c
+++ b/src/zope/i18nmessageid/_zope_i18nmessageid_message.c
@@ -101,7 +101,7 @@ Message_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     self->default_ = default_;

   if (mapping != NULL)
-    self->mapping = mapping;
+    self->mapping = PyDictProxy_New(mapping);

   if (value_plural != NULL)
     self->value_plural = value_plural;

However, the current py3x-pure environments will load the C extension unconditionally (#28), so only the pypy3 tests would actually exercise the pure-Python side.

tseaver commented 1 month ago

The fix will be more complicated: