Quick jump: Installation | Examples | Contributing | Citation | Contact
SaSh is a static analysis tool for the Unix shell, using symbolic execution to find bugs in shell programs. It currently supports the set of features and syntax defined by the POSIX standard.
News: SaSh received a best paper award at SOSP'26!
SaSh can be installed natively on Linux and MacOS, or used through Docker.
All dependencies of SaSh are listed in the Dockerfile and pyproject.toml. The following installation instructions make use of these configurations as appropriate.
Make sure you have the following installed:
gitmakeautomakeautoconflibtoolg++-13orclang-17(or newer)uv(recommended) orpipx
You already have g++-13 or clang-17 if you are on Debian 13, Ubuntu 23, or newer.
On MacOS, clang-17 is part of the xcode command line tools.
Then, run:
CFLAGS="-std=gnu17" uv tool install git+https://github.com/atlas-brown/sash.git
uv tool update-shell # If PATH needs to be updatedOr:
CFLAGS="-std=gnu17" pipx install git+https://github.com/atlas-brown/sash.git
pipx ensurepath # If PATH needs to be updatedbrew tap atlas-brown/tap
brew trust atlas-brown/tap
brew install asashRequires Docker.
nix profile add github:atlas-brown/sashIf you want to avoid installing these dependencies, you can use SaSh through Docker.
To install:
git clone https://github.com/atlas-brown/sash.git
cd ./sash
docker build -t sash .
docker run --rm sash --help # Should output a help message
# Install the wrapper script (see below) onto your PATH, then clean up:
mkdir -p ~/.local/bin
install -m 0755 ./scripts/sash-docker.sh ~/.local/bin/sash
cd ..
rm -rf ./sashImportant
The sash image reads files from the host, so the file to be analyzed
must be mounted into the container. The sash-docker.sh wrapper installed above
handles this for you: it mounts each file argument (read-only) into the
container at its own absolute path and passes everything else through to SaSh,
so you can just run sash file.sh from anywhere. It runs under either Docker or
Podman, auto-detecting whichever is installed (override with SASH_RUNTIME).
# To pass extra `docker run` flags (e.g. '--privileged' for pausing/resuming
# execution via CRIU), set SASH_DOCKER_ARGS:
SASH_DOCKER_ARGS=--privileged sash file.sh
# To run a differently-tagged image, set SASH_IMAGE (default: sash).
# Without the wrapper, you can mount manually, but then SaSh can only see files
# under the mounted directory:
docker run --rm -v "$(pwd)":/ws -w /ws sash file.shConsider a script that captures the output of a command and later uses that value to clean up a directory:
#!/bin/sh
ROOT="$(cd ${0%/*} && echo $PWD)"
rm -rf "$ROOT/"*If the cd fails, $ROOT becomes empty.
Then, "$ROOT/"* expand to /*, making rm -rf delete every user-writable file on the system.
SaSh detects this ahead of time:
$ sash install.sh
> Line 3 (error): Word splitting or empty variable could lead to deletion of system file /*
A similar bug was responsible for the 2015 Steam updater incident1.
This script moves two files to the same destination:
#!/bin/sh
mv a target
mv b targetIf target is a directory, both files end up inside it and the operation is safe. If target is a regular file, the first mv renames a to target, and the second mv renames b to target, silently overwriting a.
SaSh warns about the risk:
$ sash organize.sh
> Line 3 (error): Command 'mv' deletes the following paths, one of which has not been read, potentially causing loss of data: target
but only if unknown paths are assumed to be files
The project provides a configuration file for containerized development.
Additionally, the Dockerfile provides an additional target for development (dev), which does not copy the project files into the container, to allow for mounting.
docker build --target dev -t sash-dev .
docker run --rm -it -v "$(pwd)":/app -v /app/.venv sash-dev
# Again, remember to add '--privileged' if you need to use CRIUThis project uses pytest.
To run all tests, use uv run pytest.
To ensure correct test discovery when writing new tests:
- Test files should be named with the prefix
test_(e.g.,test_example.py). - Test functions should also start with
test_(e.g.,def test_example(): ...).
If you use SaSh in your research, please cite the paper:
@inproceedings{sash:sosp:2026,
title = {Ahead-of-time Analysis of Shell Program Effects},
author = {Lazarek, Lukas and Lamprou, Evangelos and Kapetanakis, George and Zhao, Eric and Zheng, Zhiwen and Greenberg, Michael and Kallas, Konstantinos and Vasilakis, Nikos},
year = {2026},
month = {sep},
booktitle = {Proceedings of the 32nd ACM Symposium on Operating Systems Principles},
location = {Prague, Czechia},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
series = {SOSP '26},
url = {https://sigops.org/s/conferences/sosp/2026/},
keywords = {Unix, Linux, shell, static analysis, effects},
artifact = {https://github.com/atlas-brown/sash},
}For questions please contact [email protected], or open an issue on GitHub.
