On Wed, Jul 15, 2020, at 07:40, Steven D'Aprano wrote:
> On Tue, Jul 14, 2020 at 09:47:15PM -0400, Random832 wrote:
> 
> > I was asking for the current Unpickler class, which currently has a 
> > whitelist hook for loading globals, to be modified to also have a 
> > whitelist hook so that an application can provide a function that 
> > looks at a callable and its arguments that the pickle proposes to 
> > call, and can choose to either evaluate it, raise an error, or return 
> > a substitute value.
> 
> Could you provide a proof of concept subclass?

I was thinking of something like this... this is largely a trivial modification 
of the pure-python unpickler, but there's no methods that can be overridden for 
this effect in the C one.


class MyUnpickler(pickle._Unpickler):
    # this method is intended to be overriden by subclasses
    def do_call(self, func, *a, **k):
        #print(f"blocked call {func}(*{a}, **{k})")
        #return None
        raise NotImplementedError("This unpickler can't handle this pickle")

    # these methods are defined the same as in _Unpickler except for the use of 
do_call
    def _instantiate(self, klass, args):
        if (args or not isinstance(klass, type) or
            hasattr(klass, "__getinitargs__")):
            try:
                value = do_call(klass, *args)
            except TypeError as err:
                raise TypeError("in constructor for %s: %s" %
                                (klass.__name__, str(err)), sys.exc_info()[2])
        else:
            value = do_call(klass.__new__, klass)
        self.append(value)

    def load_newobj(self):
        args = self.stack.pop()
        cls = self.stack.pop()
        obj = self.do_call(cls.__new__, cls, *args)
        self.append(obj)

    def load_newobj_ex(self):
        kwargs = self.stack.pop()
        args = self.stack.pop()
        cls = self.stack.pop()
        obj = self.do_call(cls.__new__, cls, *args, **kwargs)
        self.append(obj)

    def load_reduce(self):
        stack = self.stack
        args = stack.pop()
        func = stack[-1]
        stack[-1] = self.do_call(func, *args)

    dispatch = pickle._Unpickler.dispatch.copy()
    # load_inst and load_obj use _instantiate and don't need to be overridden 
directly
    dispatch[pickle.NEWOBJ[0]] = load_newobj
    dispatch[pickle.NEWOBJ_EX[0]] = load_newobj_ex
    dispatch[pickle.REDUCE[0]] = load_reduce


def loads(s, /, *, fix_imports=True, encoding="ASCII", errors="strict", 
buffers=None, unpickler=pickle.Unpickler):
    if isinstance(s, str):
        raise TypeError("Can't load pickle from unicode string")
    file = io.BytesIO(s)
    return unpickler(file, fix_imports=fix_imports, buffers=buffers,
                      encoding=encoding, errors=errors).load()
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/PGYB6ARHJMUAC4TY4X2HXU4ANZ33KIUN/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to