Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
shpyx is a simple, lightweight and typed library for running shell commands in Python.
Use shpyx.run
to run a shell command in a subprocess:
>>> import shpyx
>>> shpyx.run("echo 1")
ShellCmdResult(cmd='echo 1', stdout='1\n', stderr='', all_output='1\n', return_code=0)
>>> shpyx.run("echo 1").return_code
0
>>> shpyx.run("echo 1").stdout
'1\n'
>>> shpyx.run("echo 1").stderr
''
Install with pip
:
pip install shpyx
In string format:
>>> shpyx.run("echo 1")
ShellCmdResult(cmd='echo 1', stdout='1\n', stderr='', all_output='1\n', return_code=0)
In list format:
>>> shpyx.run(["echo", ["1"])
ShellCmdResult(cmd='echo 1', stdout='1\n', stderr='', all_output='1\n', return_code=0)
>>> shpyx.run("echo 1", log_output=True)
1
ShellCmdResult(cmd='echo 1', stdout='1\n', stderr='', all_output='1\n', return_code=0)
When the argument to run
is a string, an actual shell is created in the subprocess and shell logic can be used.
For example, the pipe operator can be used in bash/sh:
>>> shpyx.run("seq 1 5 | grep '2'")
ShellCmdResult(cmd="seq 1 5 | grep '2'", stdout='2\n', stderr='', all_output='2\n', return_code=0)
Use a custom runner to override the execution defaults, and not have to pass them to every call.
For example, we can change the default value of log_cmd
, so that all commands are logged:
>>> shpyx.run("echo 1")
ShellCmdResult(cmd='echo 1', stdout='1\n', stderr='', all_output='1\n', return_code=0)
>>> shpyx.run("echo 1", log_cmd=True)
Running: echo 1
ShellCmdResult(cmd='echo 1', stdout='1\n', stderr='', all_output='1\n', return_code=0)
>>> runner = shpyx.Runner(log_cmd=True)
>>> runner.run("echo 1")
Running: echo 1
ShellCmdResult(cmd='echo 1', stdout='1\n', stderr='', all_output='1\n', return_code=0)
Note: as of now this is only supported for Unix environments.
Some commands, like psql
, might output special characters used for terminal management like cursor movement and
colors. For example, the psql
command is used to start an interactive shell against a Postgres DB:
shpyx.run(f"psql -h {host} -p {port} -U {user} -d {database}", log_output=True)
However, the above call will not work as good as running psql
directly, due to terminal control sequences not being
properly propagated. To make it work well, we need to use the script
utility which will properly propagate all control sequences:
# Linux:
shpyx.run(f"script -q -c 'psql -h {host} -p {port} -U {user} -d {database}'", log_output=True)
# MacOS:
shpyx.run(f"script -q psql -h {host} -p {port} -U {user} -d {database}", log_output=True)
shpyx provides a keyword argument that does this wrapping automatically, unix_raw
:
shpyx.run(f"psql -h {host} -p {port} -U {user} -d {database}", log_output=True, unix_raw=True)
The flag is disabled by default, and should only be used for interactive commands like psql
.
The following arguments are supported by Runner
:
Name | Description | Default |
---|---|---|
log_cmd | Log the executed command. | False |
log_output | Log the live output of the command (while it is being executed). | False |
verify_return_code | Raise an exception if the shell return code of the command is not 0 . | True |
verify_stderr | Raise an exception if anything was written to stderr during the execution. | False |
use_signal_names | Log the name of the signal corresponding to a non-zero error code. | True |
The following arguments are supported by run
:
Name | Description | Default |
---|---|---|
log_cmd | Log the executed command. | Runner default |
log_output | Log the live output of the command (while it is being executed). | Runner default |
verify_return_code | Raise an exception if the shell return code of the command is not 0 . | Runner default |
verify_stderr | Raise an exception if anything was written to stderr during the execution. | Runner default |
use_signal_names | Log the name of the signal corresponding to a non-zero error code. | Runner default |
env | Environment variables to set during the execution of the command. | Same as parent process |
exec_dir | Custom path to execute the command in (defaults to current directory). | Same as parent process |
unix_raw | (UNIX ONLY) Whether to use the script Unix utility to run the command. | False |
shpyx
is a wrapper around the excellent subprocess module, aiming
to concentrate all the different API functions (Popen
/communicate
/poll
/wait
) into a single function - shpyx.run
.
While the core API logic is fully supported on both Unix and Windows systems, there is some OS specific code for minor quality-of-life improvements. For example, on non Windows systems, fcntl is used to configure the subprocess to always be incorruptible (which means one can CTRL-C out of any command).
The call to subprocess.Popen
uses shell=True
when the input to run
is a string (to support shell logic like bash piping).
This means that an actual system shell is being created, and the subprocess has the permissions of the main Python process.
It is therefore recommended not pass any untrusted input to shpyx.run
.
For more info, see security considerations.
Relevant Python libraries:
Other 3rd-party libraries for running shell commands in Python:
To contribute simply open a PR with your changes.
Tests, linters and type checks are run in CI through GitHub Actions.
To run checks locally, start by installing all the development dependencies:
poetry install
To run the linters use pre-commit
:
pre-commit run -a
To run the unit tests use pytest
:
pytest -c tests/pytest.ini tests
To run type checks use mypy
:
mypy --config-file shpyx/mypy.toml shpyx tests
To trigger a deployment of a new version upon merge, bump the version number in pyproject.toml
.
FAQs
Run shell commands in Python
We found that shpyx demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.