2

I've read on stackoverflow explicit warnings against using eval() to create objects. The reason that seems to be given is that malicious code could be injected and blindly created objects could compromise a system. Isn't this malicious-intent argument invalidated by the consenting-adult argument, though?

Some sample code:

class Test(object):
    def __init__(self, vala, valb, valc):
        self._vala = vala
        self._valb = valb
        self._valc = valc

    def __repr__(self):
        return "<Test(vala={0}, valb={1}, valc={2})>".format(self._vala, self._valb, self._valc)


testlist = [dict(cls="Test", vala="this is vala", valb="this is valb", valc="this is valc"), dict(cls="Test", vala="this is vala II", valb="this is valb II", valc="this is valc II")]

mytestobjects = []

for test in testlist:
    newobj = eval(test['cls'])(**{key: value for key, value in test.items() if key != "cls"})
    mytestobjects.append(newobj)

print(mytestobjects)
  • 7
    There's also the "invoking the full parser of your host language is overkill for parsing a domain-specific mini-language for object creation (and also not an option in languages without eval)" argument. – Doval Jul 27 '14 at 12:47
  • 3
    There is zero need for eval in your example. Store the actual classes in testlist (i.e. dict(cls=Test, ...)) and have simpler code that runs faster too. Same goes for most other uses of eval and friends, only a small fraction of the cases where people are tempted to use eval actually call for eval. – user7043 Jul 27 '14 at 15:17
  • @Doval: you lost me – Dowwie Jul 27 '14 at 17:21
  • @delnan: yes, thank you for pointing that out. I think you are referring to the "dispatcher command pattern"? This is the solution I'll go with.. – Dowwie Jul 27 '14 at 17:22
  • 2
    What I was getting at is that calling the Python compiler at runtime to create an object is overkill. The amount of plumbing involved in compiling arbitrary Python code is far more than what you need to parse a string and create an object. See Eric Lippert's Eval is Evil blog posts. – Doval Jul 27 '14 at 17:38
7

You should have some idea about who will use your software and how.

If you are sure that your code will always be used by "good people" - e.g. if your code is some internal in-house development which will never interact with outside people (or sources), then the consenting adults argument is valid. But then you should document -at the very least in some comments- your design choice (i.e. state that bad input would trigger the dreaded undefined behavior, and that input is supposed to be good and valid).

If on the contrary you might believe that your code will eventually work with some data gotten from the internet (or from a server connected on the Internet), you should be aware that because of many various (social and cultural and human) factors, bad people willing to spend time and effort to break in exist.

BTW look at how some current internet technologies exist, eg. email. In the beginning of SMTP, Internet was mostly an academic (and also military) thing - with a strong culture of "good network behavior". Today we are getting billions of SPAM because it is no longer the case

See bobby-tables! Beware of code injection...

  • 1
    Alas, the Consenting Adults argument was insufficient for the folks at Stack Overflow who (years ago) insisted that I parameterize my SQL Statements for an internal app that would never, ever be publicly accessible, otherwise I wasn't a Real Professional.™ – Robert Harvey Jan 20 '16 at 22:06
  • 1
    @RobertHarvey - That's merely because they've never forgiven you for the "Little Bobby Tales" incident. But I thought that was obvious and didn't need to be brought back up. – GlenH7 Jan 20 '16 at 22:12
5

The consenting-adults argument is that programmers who use your code are consenting adults. Users may not be. (They may, for instance, not understand how their data will be used in the program.)

Moreover, the person who supplies the malicious data may not be the person who "consented" to using your program. If the program grabs data from the internet, for instance, even a "consenting adult" user may inadvertently download malicious data and damage his system. On the flip side, if the program runs on a server, the "user" may be the server admin, who doesn't want site visitors to be able to hose his server by entering malicious data into a web form or the like. Either way, the point is that the person doing the consenting is not necessarily the person suffering the effect.

1

This is done by Python's stdlib, in collections.namedtuple(), though they do use exec() rather than eval(). When I reported a possible code-injection vulnerability (initially to python-security), it was fixed, but not treated as a security issue. In particular, they kept the exec() despite the existence of a patch which avoided it.

As I understand it, the Python developers are allowing practicality to beat purity in this case. The patch is rather complex and involves metaclasses and other cleverness, while the exec() is easier to understand. Moreover, it's uncommon to call collections.namedtuple() with untrusted user input, and even more uncommon to pass the kind of strange object I had to construct to get around the safety checks already in place.

I probably wouldn't use this technique myself unless I had no other viable options, but I'm not quite sure I can reasonably call this implementation wrong.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.