Skip to content

Commit 1459043

Browse files
committed
Python: Expand tests for os.exec*, os.spawn*, and os.posix_spawn*
1 parent 50d3592 commit 1459043

File tree

2 files changed

+41
-28
lines changed

2 files changed

+41
-28
lines changed

python/ql/test/library-tests/frameworks/stdlib/FileSystemAccess.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -206,14 +206,8 @@ def test_fspath():
206206
os.add_dll_directory("path") # $ MISSING: getAPathArgument="path"
207207
os.add_dll_directory(path="path") # $ MISSING: getAPathArgument="path"
208208

209-
# TODO: os.exec* calls all take a path as first argument
210-
# TODO: os.spawn* calls all take a path as second argument
211-
# TODO: os.posix_spawn calls
212-
213-
# TODO: Maybe these should not be considered a command injection sink?
214-
# since `os.execlp("bash -c 'echo hello'", "bash")`
215-
# raises exception: `FileNotFoundError: [Errno 2] No such file or directory`
216-
# and you're not able to execute arbitrary commands, but change what file is being run.
209+
# for `os.exec*`, `os.spawn*`, and `os.posix_spawn*` functions, see the
210+
# `SystemCommandExecution.py` file.
217211

218212
# Windows only
219213
os.startfile("path") # $ MISSING: getAPathArgument="path"

python/ql/test/library-tests/frameworks/stdlib/SystemCommandExecution.py

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,33 +34,52 @@ def os_members():
3434
# VS Code extension will ignore rest of program if encountering one of these, which we
3535
# don't want. We could use `if False`, but just to be 100% sure we don't do anything too
3636
# clever in our analysis that discards that code, I used `if UNKNOWN` instead
37+
#
38+
# below, `path` is an relative/absolute path, for the `p` variants this could also be
39+
# the name of a executable, which will be looked up in the PATH environment variable,
40+
# which we call `file` to highlight this difference.
41+
#
42+
# These are also modeled as FileSystemAccess, although they are not super relevant for
43+
# the path-injection query -- a user being able to control which program is executed
44+
# doesn't sound safe even if that is restricted to be within a certain directory.
3745
if UNKNOWN:
3846
env = {"FOO": "foo"}
39-
os.execl("executable", "<progname>", "arg0") # $getCommand="executable"
40-
os.execle("executable", "<progname>", "arg0", env) # $getCommand="executable"
41-
os.execlp("executable", "<progname>", "arg0") # $getCommand="executable"
42-
os.execlpe("executable", "<progname>", "arg0", env) # $getCommand="executable"
43-
os.execv("executable", ["<progname>", "arg0"]) # $getCommand="executable"
44-
os.execve("executable", ["<progname>", "arg0"], env) # $getCommand="executable"
45-
os.execvp("executable", ["<progname>", "arg0"]) # $getCommand="executable"
46-
os.execvpe("executable", ["<progname>", "arg0"], env) # $getCommand="executable"
47+
os.execl("path", "<progname>", "arg0") # $ getCommand="path" MISSING: getAPathArgument="path"
48+
os.execle("path", "<progname>", "arg0", env) # $ getCommand="path" MISSING: getAPathArgument="path"
49+
os.execlp("file", "<progname>", "arg0") # $ getCommand="file" MISSING: getAPathArgument="file"
50+
os.execlpe("file", "<progname>", "arg0", env) # $ getCommand="file" MISSING: getAPathArgument="file"
51+
os.execv("path", ["<progname>", "arg0"]) # $ getCommand="path" MISSING: getAPathArgument="path"
52+
os.execve("path", ["<progname>", "arg0"], env) # $ getCommand="path" MISSING: getAPathArgument="path"
53+
os.execvp("file", ["<progname>", "arg0"]) # $ getCommand="file" MISSING: getAPathArgument="file"
54+
os.execvpe("file", ["<progname>", "arg0"], env) # $ getCommand="file" MISSING: getAPathArgument="file"
4755

4856

4957
########################################
5058
# https://docs.python.org/3.8/library/os.html#os.spawnl
5159
env = {"FOO": "foo"}
52-
os.spawnl(os.P_WAIT, "executable", "<progname>", "arg0") # $getCommand="executable"
53-
os.spawnle(os.P_WAIT, "executable", "<progname>", "arg0", env) # $getCommand="executable"
54-
os.spawnlp(os.P_WAIT, "executable", "<progname>", "arg0") # $getCommand="executable"
55-
os.spawnlpe(os.P_WAIT, "executable", "<progname>", "arg0", env) # $getCommand="executable"
56-
os.spawnv(os.P_WAIT, "executable", ["<progname>", "arg0"]) # $getCommand="executable"
57-
os.spawnve(os.P_WAIT, "executable", ["<progname>", "arg0"], env) # $getCommand="executable"
58-
os.spawnvp(os.P_WAIT, "executable", ["<progname>", "arg0"]) # $getCommand="executable"
59-
os.spawnvpe(os.P_WAIT, "executable", ["<progname>", "arg0"], env) # $getCommand="executable"
60-
61-
# Added in Python 3.8
62-
os.posix_spawn("executable", ["<progname>", "arg0"], env) # $getCommand="executable"
63-
os.posix_spawnp("executable", ["<progname>", "arg0"], env) # $getCommand="executable"
60+
os.spawnl(os.P_WAIT, "path", "<progname>", "arg0") # $ getCommand="path" MISSING: getAPathArgument="path"
61+
os.spawnle(os.P_WAIT, "path", "<progname>", "arg0", env) # $ getCommand="path" MISSING: getAPathArgument="path"
62+
os.spawnlp(os.P_WAIT, "file", "<progname>", "arg0") # $ getCommand="file" MISSING: getAPathArgument="file"
63+
os.spawnlpe(os.P_WAIT, "file", "<progname>", "arg0", env) # $ getCommand="file" MISSING: getAPathArgument="file"
64+
os.spawnv(os.P_WAIT, "path", ["<progname>", "arg0"]) # $ getCommand="path" MISSING: getAPathArgument="path"
65+
os.spawnve(os.P_WAIT, "path", ["<progname>", "arg0"], env) # $ getCommand="path" MISSING: getAPathArgument="path"
66+
os.spawnvp(os.P_WAIT, "file", ["<progname>", "arg0"]) # $ getCommand="file" MISSING: getAPathArgument="file"
67+
os.spawnvpe(os.P_WAIT, "file", ["<progname>", "arg0"], env) # $ getCommand="file" MISSING: getAPathArgument="file"
68+
69+
# unlike os.exec*, some os.spawn* functions is usable with keyword arguments. However,
70+
# despite the docs using both `file` and `path` as the parameter name, you actually need
71+
# to use `file` in all cases.
72+
os.spawnv(mode=os.P_WAIT, file="path", args=["<progname>", "arg0"]) # $ MISSING: getCommand="path" getAPathArgument="path"
73+
os.spawnve(mode=os.P_WAIT, file="path", args=["<progname>", "arg0"], env=env) # $ MISSING: getCommand="path" getAPathArgument="path"
74+
os.spawnvp(mode=os.P_WAIT, file="file", args=["<progname>", "arg0"]) # $ MISSING: getCommand="file" getAPathArgument="file"
75+
os.spawnvpe(mode=os.P_WAIT, file="file", args=["<progname>", "arg0"], env=env) # $ MISSING: getCommand="file" getAPathArgument="file"
76+
77+
# `posix_spawn` Added in Python 3.8
78+
os.posix_spawn("path", ["<progname>", "arg0"], env) # $ getCommand="path" MISSING: getAPathArgument="path"
79+
os.posix_spawn(path="path", argv=["<progname>", "arg0"], env=env) # $ MISSING: getCommand="path" getAPathArgument="path"
80+
81+
os.posix_spawnp("path", ["<progname>", "arg0"], env) # $ getCommand="path" MISSING: getAPathArgument="path"
82+
os.posix_spawnp(path="path", argv=["<progname>", "arg0"], env=env) # $ MISSING: getCommand="path" getAPathArgument="path"
6483

6584
########################################
6685

0 commit comments

Comments
 (0)