0
is_exec = lambda x: subprocess.call("type " + x, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0 and (os.path.isfile(x) and os.access(x, os.X_OK))

I came across this code and it works fine but is it redundant?

Isnt is_exec = lambda x: os.access(x, os.X_OK) sufficient?

Question: Is there a case where is_exec = lambda x: os.access(x, os.X_OK) isnt catching but the first one does?

2
  • I'm pretty sure that it is redundant. Both pieces of code work just as well; only the second is more concise. The difference is that one actually does a test, the other checks properties. Besides, avoid the first one anyway as it may be vulnerable to arbitrary code execution. Commented Oct 28, 2014 at 18:01
  • 1
    The first looks like a backdoor if you aren't carefully vetting the value of x. Commented Oct 28, 2014 at 18:03

2 Answers 2

1

There is a subtle difference here -- in the first call, this will also detect shell built-ins, for example:

$ type cd
cd is a shell builtin
$ echo $?
0

But there is no actual cd executable as such, so you can't check it explicitly with os.access(). That said, I believe it should actually be

is_exec = lambda x: subprocess.call("type " + x, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0 or (os.path.isfile(x) and os.access(x, os.X_OK))

Note that the middle operator is now or. Also, the subprocess pipes seem a bit superfluous here, and there is the caveat that it can be used as a shell injection to consider too.

All in all, if you only care about verifying exeutable files, then ditching the first bit is fine.

Sign up to request clarification or add additional context in comments.

Comments

1

Both require os.access(x, os.X_OK) to return True. However, in addition to performing some unnecessary tests before calling os.access, the first opens you up to a shell injection attack unless you carefully screen the value of x before using it. Using shell=True with subprocess.call simply passes a string to the shell for execution. If the value of x is carefully constructed, you end up executing more than just the type command. For example:

x = "somefile.txt; rm foo.txt"
subprocess.call("type " + x, shell=True,
                stdout=subprocess.PIPE, stderr=subprocess.PIPE)

will pass the string "type somefile.txt; rm foo.txt" to the shell, resulting in two commands, not just one, in being executed.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.