shellsafe
Safe shell commands via Python 3.14 template strings. Injection-proof by construction.
from shellsafe import run
message = get_user_input() # "fix; rm -rf ~"
run(t"git commit -m {message}")
# argv: ["git", "commit", "-m", "fix; rm -rf ~"]
# one command; the scary text is just an argument
Why
The dominant pattern in scripts and automation is still this:
subprocess.run(f"git commit -m {message}", shell=True) # injection waiting to happen
Python 3.14 template strings (t"...") separate static text from interpolated
values. shellsafe turns that structure into argv lists where interpolated values
are always data, never commands. When you genuinely need pipes, shell mode quotes
every value with POSIX rules first.
Install
pip install shellsafe
Requires Python 3.14+ (template strings).
Usage
Run a command. Interpolated values are always single arguments:
from shellsafe import run
run(t"mkdir {path}")
run(t"docker build -t {tag} .", check=True, timeout=300)
Capture output as text:
from shellsafe import capture
res = capture(t"grep {pattern} {file}")
print(res.stdout, res.returncode)
Pipes and redirections on POSIX (values are quoted with shlex.quote first):
from shellsafe import shx
shx(t"cat {file} | wc -l")
Inspect exactly what will execute:
from shellsafe import plan # lower-level: render without running
print(plan(t"git commit -m {message}"))
# argv: ["git","commit","-m","fix; rm -rf ~"]
What it refuses
| Case | Behavior |
|---|---|
| Interpolation as the executable | error: the command comes from static text only |
| Shell route on Windows | error: cmd.exe quoting cannot be made injection-safe; use argv mode |
| RAW misuse | error: one argument, verbatim, nesting refused |
RAW("...") / RAW(["a", "b"]) is the single explicit trust boundary for
pre-quoted content. Every use site is greppable.
Limits
- Windows: interpolated shell routes are refused rather than approximated; argv-mode commands work fully.
- Bytes interpolations are rejected: decode explicitly first.
- Runtime behavior after import is your test suite's job, same trust model as calling subprocess yourself.
Contributing
Issues and PRs welcome. Security reports go privately to the maintainer, never through public issues.
Requirements
- CPython >= 3.14 (uses template strings from PEP 750)
- Linux, macOS, Windows (Windows supports argv mode; POSIX-only shell mode)
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file shellsafe-0.1.0.tar.gz.
File metadata
- Download URL: shellsafe-0.1.0.tar.gz
- Upload date:
- Size: 14.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ca3629ad9b5f604ce1c6703616c2036e09395e2779ea05cfedc9f9a85b00fe07
|
|
| MD5 |
d17c48ec7496e90bbf66f6f55a05e55d
|
|
| BLAKE2b-256 |
7d816a077e2f471109b1149add46607b216ee2ebf43e7307d3b6a07693e8bf83
|
Provenance
The following attestation bundles were made for shellsafe-0.1.0.tar.gz:
Publisher:
release.yml on rahulXs/shellsafe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
shellsafe-0.1.0.tar.gz -
Subject digest:
ca3629ad9b5f604ce1c6703616c2036e09395e2779ea05cfedc9f9a85b00fe07 - Sigstore transparency entry: 2581439687
- Sigstore integration time:
-
Permalink:
rahulXs/shellsafe@dc9cba5af40e92c1bbdc015c5f9e3c5772c46aa2 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/rahulXs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@dc9cba5af40e92c1bbdc015c5f9e3c5772c46aa2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file shellsafe-0.1.0-py3-none-any.whl.
File metadata
- Download URL: shellsafe-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9679d1193444a554840cf3920eb9efc6e9fb4492d443dbb95fa6aff54b1492b7
|
|
| MD5 |
fa5f73307e9de10cbee4ca7c3cbdb8c5
|
|
| BLAKE2b-256 |
4410cc52ccad9b49a8f63c9e3234dd179193453a69ea4933785a4b01f9e46f4f
|
Provenance
The following attestation bundles were made for shellsafe-0.1.0-py3-none-any.whl:
Publisher:
release.yml on rahulXs/shellsafe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
shellsafe-0.1.0-py3-none-any.whl -
Subject digest:
9679d1193444a554840cf3920eb9efc6e9fb4492d443dbb95fa6aff54b1492b7 - Sigstore transparency entry: 2581439693
- Sigstore integration time:
-
Permalink:
rahulXs/shellsafe@dc9cba5af40e92c1bbdc015c5f9e3c5772c46aa2 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/rahulXs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@dc9cba5af40e92c1bbdc015c5f9e3c5772c46aa2 -
Trigger Event:
push
-
Statement type: