[kupfer] Add a safer, pure-python ConservativeUnpickler in conspickle.py
- From: Ulrik Sverdrup <usverdrup src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [kupfer] Add a safer, pure-python ConservativeUnpickler in conspickle.py
- Date: Thu, 11 Feb 2010 16:57:49 +0000 (UTC)
commit 719078e130d6ceaf4ebbeb55237100379408856e
Author: Ulrik Sverdrup <ulrik sverdrup gmail com>
Date: Tue Feb 9 14:54:21 2010 +0100
Add a safer, pure-python ConservativeUnpickler in conspickle.py
puid's use of pickle is not performance-critical, so using just
python's pickle right now is fine, and it's more future-proof.
We use safer defaults by explicitly denying almost all builtin
function and classes. However, we have to allow all kupfer classes to
be created.
This change requires Python 2.6
kupfer/conspickle.py | 34 +++++++++++++++++++++++++++++
kupfer/puid.py | 58 +++++++++++--------------------------------------
2 files changed, 47 insertions(+), 45 deletions(-)
---
diff --git a/kupfer/conspickle.py b/kupfer/conspickle.py
new file mode 100644
index 0000000..3d2a6f6
--- /dev/null
+++ b/kupfer/conspickle.py
@@ -0,0 +1,34 @@
+import fnmatch
+import io
+import pickle
+import sys
+
+class universalset (object):
+ def __contains__(self, item):
+ return True
+
+class ConservativeUnpickler (pickle.Unpickler):
+ "An Unpickler that refuses to import new modules"
+ safe_modules = {
+ "__builtin__" : set(["set", "sum", "object"]),
+ "copy_reg" : set(["_reconstructor"]),
+ "kupfer.*" : universalset(),
+ }
+ @classmethod
+ def is_safe_symbol(cls, module, name):
+ for pattern in cls.safe_modules:
+ if fnmatch.fnmatchcase(module, pattern):
+ return name in cls.safe_modules[pattern]
+ return False
+
+ def find_class(self, module, name):
+ if module not in sys.modules:
+ raise pickle.UnpicklingError("Refusing to load module %s" % module)
+ if not self.is_safe_symbol(module, name):
+ raise pickle.UnpicklingError("Refusing unsafe %s.%s" % (module, name))
+ return pickle.Unpickler.find_class(self, module, name)
+
+ @classmethod
+ def loads(cls, pickledata):
+ unpickler = cls(io.BytesIO(pickledata))
+ return unpickler.load()
diff --git a/kupfer/puid.py b/kupfer/puid.py
index 28ce8ab..a206e53 100644
--- a/kupfer/puid.py
+++ b/kupfer/puid.py
@@ -1,22 +1,25 @@
"""
Persistent Globally Unique Indentifiers for KupferObjects.
-"""
-from __future__ import with_statement
+Some objects are assigned identifiers by reference, some are assigned
+identifiers containing the whole object data (SerializedObject).
-import contextlib
-import cPickle as pickle
-import sys
+SerializedObject is a saved representation of a KupferObject, i.e. a
+data model user-level object.
-try:
- from cStringIO import StringIO
-except ImportError:
- from StringIO import StringIO
+We unpickle SerializedObjects in an especially conservative way: new
+module loading is always refused; this way, we avoid loading parts of
+the program that we didn't wish to activate.
+"""
+
+import contextlib
+import pickle
from kupfer import pretty
from kupfer.core import actioncompat
from kupfer.core import qfurl
from kupfer.core.sources import GetSourceController
+from kupfer.conspickle import ConservativeUnpickler
__all__ = [
"SerializedObject", "SERIALIZABLE_ATTRIBUTE",
@@ -26,41 +29,6 @@ __all__ = [
SERIALIZABLE_ATTRIBUTE = "serializable"
-"""
-SerializedObject is a saved representation of a KupferObject, i.e. a
-data model user-level object.
-
-We unpickle SerializedObjects in an especially conservative way: new
-module loading is always refused; this way, we avoid loading parts of
-the program that we didn't wish to activate.
-
-The implementation with Pure-Python pickle would look like::
-
- class ConservativeUnpickler (pickle.Unpickler):
- "An Unpickler that refuses to import new modules"
- def find_class(self, module, name):
- if module not in sys.modules:
- raise ValueError("Plugin %s is not loaded" % module)
- return pickle.Unpickler.find_class(self, module, name)
-
- @classmethod
- def loads(cls, pickledata):
- unpickler = cls(StringIO(pickledata))
- return unpickler.load()
-
-"""
-
-def _conservative_find_global(module, name):
- if module not in sys.modules:
- raise pickle.UnpicklingError("Refusing to load module %s" % module)
- return getattr(sys.modules[module], name)
-
-def conservative_loads(pickledata):
- "Unpickle, but refuse to import new modules"
- unpickler = pickle.Unpickler(StringIO(pickledata))
- unpickler.find_global = _conservative_find_global
- return unpickler.load()
-
class SerializedObject (object):
# treat the serializable attribute as a version number, defined on the class
@@ -71,7 +39,7 @@ class SerializedObject (object):
return (isinstance(other, type(self)) and self.data == other.data and
self.version == other.version)
def reconstruct(self):
- obj = conservative_loads(self.data)
+ obj = ConservativeUnpickler.loads(self.data)
if self.version != getattr(obj, SERIALIZABLE_ATTRIBUTE):
raise ValueError("Version mismatch for reconstructed %s" % obj)
return obj
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]