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++-13 or clang-17 (or newer)uv (recommended) or pipxYou 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 updated
Or:
CFLAGS="-std=gnu17" pipx install git+https://github.com/atlas-brown/sash.git
pipx ensurepath # If PATH needs to be updated
brew tap atlas-brown/tap
brew trust atlas-brown/tap
brew install asash
Requires Docker.
nix profile add github:atlas-brown/sash
If 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/asash-docker.sh ~/.local/bin/asash
cd ..
rm -rf ./sash
[!IMPORTANT] The
sashimage reads files from the host, so the file to be analyzed must be mounted into the container. Theasash-docker.shwrapper 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 runasash file.shfrom anywhere. It runs under either Docker or Podman, auto-detecting whichever is installed (override withASASH_RUNTIME).# To pass extra `docker run` flags (e.g. '--privileged' for pausing/resuming # execution via CRIU), set ASASH_DOCKER_ARGS: ASASH_DOCKER_ARGS=--privileged asash file.sh # To run a differently-tagged image, set ASASH_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.sh
Consider 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:
$ asash 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 target
If 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:
$ asash 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 CRIU
This project uses pytest.
To run all tests, use uv run pytest.
To ensure correct test discovery when writing new tests:
test_ (e.g., test_example.py).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 atlas@brown.edu, or open an issue on GitHub.
Shell
46.9%
Python
46.7%
HTML
4.1%
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++-13 or clang-17 (or newer)uv (recommended) or pipxYou 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 updated
Or:
CFLAGS="-std=gnu17" pipx install git+https://github.com/atlas-brown/sash.git
pipx ensurepath # If PATH needs to be updated
brew tap atlas-brown/tap
brew trust atlas-brown/tap
brew install asash
Requires Docker.
nix profile add github:atlas-brown/sash
If 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/asash-docker.sh ~/.local/bin/asash
cd ..
rm -rf ./sash
[!IMPORTANT] The
sashimage reads files from the host, so the file to be analyzed must be mounted into the container. Theasash-docker.shwrapper 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 runasash file.shfrom anywhere. It runs under either Docker or Podman, auto-detecting whichever is installed (override withASASH_RUNTIME).# To pass extra `docker run` flags (e.g. '--privileged' for pausing/resuming # execution via CRIU), set ASASH_DOCKER_ARGS: ASASH_DOCKER_ARGS=--privileged asash file.sh # To run a differently-tagged image, set ASASH_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.sh
Consider 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:
$ asash 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 target
If 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:
$ asash 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 CRIU
This project uses pytest.
To run all tests, use uv run pytest.
To ensure correct test discovery when writing new tests:
test_ (e.g., test_example.py).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 atlas@brown.edu, or open an issue on GitHub.
Shell
46.9%
Python
46.7%
HTML
4.1%