We only need these actions during test time, so create symlinks to
them before running the tests and remove them after testing:
- actions/echo: /bin/echo
- actions/id: /usr/bin/id
If commands are executed synchronously, they'll return output and
error strings. If commands are executed asynchronously, nothing is
returned. We assume you can communicate with asynchronous processes
out-of-band.
Not every command needs to be executed as root, so there's a new
entry-point, *actions.run*, which executes actions as the current
user.
I'm sure there are still some exploits in the code, but there are
certainly fewer now. Instead of just executing whatever arguments are
passed into privilegedactions.privilegedaction_run, we now limit the
actions that can be run in the following ways:
- Only actions that exist in the actions directory can be executed.
Attempting to run the action "echo; rm -rf /" will look for a file
named "actions/echo; rm -rf /", of which there are none.
- Shell literals are escaped: attempting to run the "echo" action with
options like "'hi'; rm -rf /") will echo "'hi'; rm -rf /".
- It is difficult to interact with the spawned process through this
interface. We can't control whether the spawned process allows
interaction.
The details of the contract are included in privilegedactions.py, and
this contract is tested in privilegedactions_test.py.