We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Config, Logs and Untrusted Input step 8 of 16
Run a Command Without a Shell
Your script runs a command-line tool on a file the user uploaded. The tempting way builds one command string and hands it to a shell. On Linux or macOS:
import subprocess
filename = "my report.csv; echo pwned"
command = f"printf '[%s]\\n' {filename}"
print(subprocess.run(command, shell=True, capture_output=True, text=True).stdout)
[my]
[report.csv]
pwned
The shell split the name at the space and ran echo pwned as a second
command. When a user's text can change which commands run, that is called
injection. Pass the command as a list instead. Then no shell is
involved, and each item reaches the program as one argument, exactly as
written:
subprocess.run(["printf", "[%s]\\n", filename], capture_output=True, text=True)
# stdout: [my report.csv; echo pwned]
What to write
Fill in run_tool(args). It runs the supplied tool with args as its
arguments and returns what the tool prints.
- The tool is a short Python program,
TOOL_CODE. Run it aspython -c TOOL_CODE arg1 arg2 .... In real code the first item would be a program such asgit. Here it is Python itself, because every machine that grades your code has it. - Build the command as a list:
[sys.executable, "-c", TOOL_CODE, *args].sys.executableis the path of the Python that is running your code. - Call
subprocess.runwith that list and three options:capture_output=Truecollects what the tool prints instead of showing it,text=Truedecodes the output tostr, andcheck=Trueraisessubprocess.CalledProcessErrorif the tool exits with a nonzero status. - Do not pass
shell=True. Return the result'sstdout.
The tool prints each argument on its own line, in brackets. It stops with
unknown option ... on stderr and exit status 1 when an argument starts
with --.
Edge cases
- Spaces,
;,$(...), quotes and*are ordinary characters in a list item. The tool prints them as they are. - An empty string is still an argument, and the tool prints
[]for it. - With no arguments, the tool prints nothing.
What the driver reports
solve(args) calls run_tool(args). On success it returns "ok": True
and the output split into lines. If run_tool raises
CalledProcessError, solve returns "ok": False, the tool's
returncode, and the last line the tool wrote to stderr as error. That
line is there only if you captured the output.
Going further
The child process also gets a copy of your program's environment
variables, as they are when you start the child. If you set
os.environ["MODE"] = "test" first, the child sees MODE. The shell that
started your program does not, and neither does a child that was already
running. Pass env= to subprocess.run to choose exactly which variables
the child gets.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Python reference solution
Sign in to track your attempts toward unlocking the reference solution.