{"id":49960188,"url":"https://github.com/sandbox-utils/sandbox-venv","last_synced_at":"2026-05-18T02:04:29.453Z","repository":{"id":310409804,"uuid":"1035230572","full_name":"sandbox-utils/sandbox-venv","owner":"sandbox-utils","description":"Secure container sandbox Python virtualenv wrapper","archived":false,"fork":false,"pushed_at":"2025-10-31T01:59:45.000Z","size":91,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-01-29T21:26:45.942Z","etag":null,"topics":["bubblewrap","bubblewrap-scripts","bwrap","container","firejail","namespaces","opsec","posix-sh","python","sandbox","sandboxing","secure","security","security-tools","shell","venv","venv-python","virtualenv"],"latest_commit_sha":null,"homepage":"https://github.com/kernc/sandbox-venv/","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sandbox-utils.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-09T23:53:46.000Z","updated_at":"2025-11-29T05:09:21.000Z","dependencies_parsed_at":"2025-09-25T04:16:26.007Z","dependency_job_id":"6c59e4bd-45ed-4532-91d3-8fdda70dfdc3","html_url":"https://github.com/sandbox-utils/sandbox-venv","commit_stats":null,"previous_names":["kernc/sandbox-venv","sandbox-utils/sandbox-venv"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/sandbox-utils/sandbox-venv","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sandbox-utils%2Fsandbox-venv","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sandbox-utils%2Fsandbox-venv/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sandbox-utils%2Fsandbox-venv/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sandbox-utils%2Fsandbox-venv/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sandbox-utils","download_url":"https://codeload.github.com/sandbox-utils/sandbox-venv/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sandbox-utils%2Fsandbox-venv/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33162448,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-17T22:39:12.733Z","status":"online","status_checked_at":"2026-05-18T02:00:06.436Z","response_time":71,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bubblewrap","bubblewrap-scripts","bwrap","container","firejail","namespaces","opsec","posix-sh","python","sandbox","sandboxing","secure","security","security-tools","shell","venv","venv-python","virtualenv"],"created_at":"2026-05-18T02:04:27.526Z","updated_at":"2026-05-18T02:04:29.448Z","avatar_url":"https://github.com/sandbox-utils.png","language":"Shell","funding_links":["https://github.com/sponsors/kernc"],"categories":[],"sub_categories":[],"readme":"sandbox-venv: secure container virtualenv wrapper\n=================================================\n\n[![Build status](https://img.shields.io/github/actions/workflow/status/kernc/sandbox-venv/ci.yml?branch=master\u0026style=for-the-badge)](https://github.com/kernc/sandbox-venv/actions)\n[![Language: shell / Bash](https://img.shields.io/badge/lang-Shell-peachpuff?style=for-the-badge)](https://github.com/kernc/sandbox-venv)\n[![Source lines of code](https://img.shields.io/endpoint?url=https%3A%2F%2Fghloc.vercel.app%2Fapi%2Fkernc%2Fsandbox-venv%2Fbadge?filter=.sh%26format=human\u0026style=for-the-badge\u0026label=SLOC\u0026color=skyblue)](https://ghloc.vercel.app/kernc/sandbox-venv)\n[![Script size](https://img.shields.io/github/size/kernc/sandbox-venv/build/sandbox-venv?style=for-the-badge\u0026color=skyblue)](https://github.com/kernc/sandbox-venv)\n[![Issues](https://img.shields.io/github/issues/kernc/sandbox-venv?style=for-the-badge)](https://github.com/kernc/sandbox-venv/issues)\n[![Sponsors](https://img.shields.io/github/sponsors/kernc?color=pink\u0026style=for-the-badge)](https://github.com/sponsors/kernc)\n\n\n#### Problem statement\n\nPython virtual environments (package\n[`virtualenv`](https://virtualenv.pypa.io/en/latest/)\nor built-in module\n[venv](https://docs.python.org/3/library/venv.html))\nisolate your project’s interpreter and dependencies, but they offer\n**no security or execution sandboxing** like a virtual machine or a Docker\ncontainer would. Therefore, running virtualenv Python programs as-is (unsecured),\n**any [rogue dependency](https://www.google.com/search?q=malicious+python+packages\u0026tbm=nws)\\*\n🎯 or [hacked library code](https://www.google.com/search?q=(hacked+OR+hijacked+OR+backdoored+OR+\"supply+chain+attack\")+(npm+OR+pypi)\u0026tbm=nws\u0026num=100)\n:pirate_flag: ([et cet.](https://slsa.dev/spec/draft/threats-overview) :warning:)\ncan wreak havoc, including access all your private parts** :bangbang:—think\ncurrent user's credentials and personal bits like:\n* `~/.ssh/id_ed25519`,\n* `~/.pki/nssdb/`,\n* `~/.mozilla/firefox/\u003cprofile\u003e/key4.db`,\n* `~/.mozilla/firefox/\u003cprofile\u003e/formhistory.sqlite` ...\n\n\u003csub\u003e✱ Installing something as seemingly harmless as the popular **package _poetry_**\npulls in [nearly a hundred dependencies or over 70 MB](doc/deps-stats.txt)\nof Python sources! 😬\u003c/sub\u003e\n\nIn someone else's words:\n\n\u003e\u003e Using virtualenv is more secure?\n\u003e \n\u003e [No. Not in the slightest.](https://www.reddit.com/r/Python/comments/5sm6zm/using_virtualenv_is_more_secure/)\n\n#### Solution\n\nIn order to execute installed Python programs in secure virtual environments,\none is better advised to either look to OS VM primitives like those provided by Docker\nand [containers](https://github.com/containers/), e.g.:\n```shell\npodman run -it -v .:/src python:3 bash  # ...\n```\nThe simpler alternative is **automatic lightweight container wrapping with\n[bubblewrap](https://github.com/containers/bubblewrap)** (of\n[Flatpak](https://en.wikipedia.org/wiki/Flatpak) fame)\nusing `sandbox-venv` script from this repo.\n\n\nInstallation\n------------\nThere are **no dependencies other than a POSIX shell** with\n[its standard set of utilities](https://en.wikipedia.org/wiki/List_of_POSIX_commands)\n**and `bubblewrap`**.\nThe installation process, as well as the script runtime,\nshould behave similarly on all relevant compute platforms,\nincluding GNU/Linux and even\n[Windos/WSL](https://learn.microsoft.com/en-us/windows/wsl/install). 🤞\n\n```shell\n# Install the few, unlikely to be missing dependencies, e.g.\nsudo apt install coreutils binutils bubblewrap libseccomp2 python3\n# A working XDG Desktop Portal is recommended to xdg-open hyperlinks\nsudo apt install xdg-dbus-proxy xdg-desktop-portal*  # Note: only need one\n\n# Download the script and put it somewhere on PATH\ncurl -vL 'https://bit.ly/sandbox-venv' | sudo tee /usr/local/bin/sandbox-venv\nsudo chmod +x /usr/local/bin/sandbox-venv  # Mark executable\n\nsandbox-venv --help\n# Usage: sandbox-venv [VENV_DIR] [BWRAP_OPTS]\nsandbox-venv path/to/my-project/.venv\n```\n\nUsage\n-----\nWhenever you create a new virtual environment,\nsimply invoke `sandbox-venv` on it afterwards, e.g.:\n```shell\ncd project\npython -m venv .venv  # Create a new project virtualenv\nsandbox-venv .venv    # Passing virtualenv dir is optional; defaults to \".venv\"\n```\nFrom now on, directory _.venv_ and everything under it\n(in particular, everything in the _bin_ folder,\ne.g. `.venv/bin/python`, `.venv/bin/pip` etc.)\nsets up and transparently runs in a secure container sandbox.\n\n\n#### Extra Bubblewrap arguments\n\nOther than the optional virtualenv dir, **all arguments** initially passed to\n`sandbox-venv` are **forwarded to bubblewrap**. See `bubblewrap --help` or\n[`man 1 bwrap`](https://manpages.debian.org/unstable/bwrap).\n\nYou can also pass additional bubblewrap arguments to individual\nprocess invocations via **`$BWRAP_ARGS` environment variable**. E.g.:\n\n```sh\nBWRAP_ARGS='--bind /lib /lib' \\\n    python -c 'import os; print(os.listdir(\"/lib\"))'\n```\n\nNote, a **[`.env` file](https://stackoverflow.com/questions/68267862/what-is-an-env-or-dotenv-file-exactly)\nat project root** is sourced for the initial environment.\n\nSee more specific examples below.\n\n\n#### Filesystem mounts\n\nThe directory that contains your venv dir, i.e. `.venv/..` or\n**the project directory, is mounted with read-write permissions**,\nwhile everything else (including `project/.git`)\nis mounted read-only. In addition:\n\n* `\"$venv/home\"` is bind-mounted as `\"$HOME\"`,\n* `\"$venv/cache\"` is bind-mounted as `\"$HOME/.cache\"`,\n* `\"$HOME/.cache/pip\"` is bind-mounted as `\"$HOME/.cache/pip\"`\n  (only if environment variable `SANDBOX_USE_PIP_CACHE=` is set as this may\n  enable cache poisoning attacks).\n\nTo mount extra endpoints, use Bubblewrap switches `--bind` or `--bind-ro`.\nAnything else not explicitly mounted by an extra CLI switch\nis lost upon container termination.\n\n\n#### Linux Seccomp\n\nLinux kernel `seccomp` facility for restricting syscalls is\n**automatically enabled when the appropriate package is\navailable**—`apt install libseccomp2 python3-seccomp` (requires virtualenv with `--system-site-packages`)\nor `pip install pyseccomp` (also requires `libseccomp2`).\nThe initializing module `sitecustomize.py` installs a filter\nthat thereafter only allows syscalls listed in the environment variable\n`SANDBOX_SECCOMP_ALLOW=`\n(or, by default, some 200 syscalls that should cover all non-special cases).\nYou can populate the variable at runtime with a custom, stricter syscalls list\nor set it to blank\n(i.e. `export SANDBOX_SECCOMP_ALLOW=`) to force-disable seccomp completely.\n\n\n#### Runtime monitoring\n\nIf **environment variable `VERBOSE=`** is set to a non-empty value,\nthe full `bwrap` command line is emitted to stderr before execution.\n\nYou can list bubblewraped processes using the\n[command `lsns`](https://manpages.debian.org/unstable/lsns)\nor the following shell function:\n\n```sh\nlist_bwrap () { lsns -u -W | { IFS= read header; echo \"$header\"; grep bwrap; }; }\n\nlist_bwrap  # Function call\n```\n\nYou can run `$venv/bin/shell` to spawn **interactive shell inside the sandbox**.\n\n\n#### Environment variables\n\n* `BWRAP_ARGS=`– Extra arguments passed to `bwrap` process; space or line-delimited (if arguments such as paths themselves contain spaces).\n* `SANDBOX_SECCOMP_ALLOW=`– Space-separated list of system calls allowed by the installed Linux seccomp filter. Requires `libseccomp2` and `python3-seccomp` / Python package `pyseccomp`\n* `SANDBOX_USE_PIP_CACHE=`– Mount current user's `$HOME/.cache/pip` inside the sandbox. Insecure, but can be used to cache large, trusted package downloads. Otherwise, pip is always invoked with a clean cache as if `pip --no-cache-dir ...` were used.\n* `VERBOSE=`– Print full `exec bwrap` command line right before execution.\n\n\n#### Debugging\n\nTo see what's failing, run the sandbox with something like `strace -f -e '%file,%process' ...`.\n\n\nExamples\n--------\nThe examples here deal in environment variables,\nbut once configurations stabilize, you should probably use\n`sandbox-venv` **init params** and/or **`.env` files**.\n\nTo install a heavy package that requires a compiler, it is often easiest to\nsupply it with full _/usr_ and _/lib_:\n```sh\nBWRAP_ARGS='--ro-bind /usr /usr --ro-bind /lib /lib'  pip install ...\n```\n\nYou *may need* to expose your complex IDE bin/lib dirs.\nFor use with JetBrains IDEs such as PyCharm, you need to give the\nsandbox access to the IDE runtime dir. We show this done globally\nat **initialization via default args** rather than via `$BWRAP_ARGS` variable:\n```sh\nIDE_DIR=\"/home/my_username/Downloads/pycharm\"  # E.g.\nsandbox-venv .venv --ro-bind \"$IDE_DIR\" \"$IDE_DIR\"\n```\n\nTo pass extra environment variables, other than those filtered by default,\nuse `bwrap --setenv`, e.g.:\n```sh\nBWRAP_ARGS='--setenv OPENAI_API_KEY c4f3b4b3'  my-ai-prog\n# or a .env (dotenv) file\n```\n\nTo run the sandboxed process as **superuser**\n(while still retaining all the security functionality of the container sandbox),\ne.g. to open privileged ports, use args:\n```sh\nBWRAP_ARGS='--uid 0 --cap-add cap_net_bind_service'  python -m http.server 80\n```\n\nTo run **GUI (X11) apps**, some prior success was achieved using e.g.:\n```sh\nBWRAP_ARGS='--bind /tmp/.X11-unix/X0 /tmp/.X11-unix/X8 --setenv DISPLAY :8' \\\n    python -m tkinter\n```\nSee [more examples on the ArchWiki](https://wiki.archlinux.org/title/Bubblewrap#Using_X11).\n\n\nSecurity Model\n--------------\nEntrypoints in `$venv/bin` are wrapped with `exec bwrap`\nso that every invocation runs inside a **fresh** Bubblewrap container.\n\nIn addition, `$venv/bin/pip` wrapper re-wraps any **newly installed executables** in `$venv/bin`,\nensuring they always use the wrapped `$venv/bin/python`.\n\nRather than giving the sandbox full filesystem access,\nminimal shared library (`*.so`) dependencies,\nas well as specific host binaries (e.g. `/usr/bin/python3`, `/usr/bin/git`, `/bin/sh` etc.),\nare collected and made available inside the container.\n**Most paths are bind-mounted read-only**, while the **current working directory**\nis mounted with **RW** permissions (with the exclusion of\ndirectories `.venv` and `.git`, which are layered over RO so as to not allow\nthe sandboxed executable to modify them directly).\n\n**`BWRAP_ARGS=`** environment variable lets you **expand or relax the sandbox** at runtime.\n\nAdditional [confinement with `seccomp` is available](#linux-seccomp).\n\n**Paths inside the sandbox mirror the host paths**, potentially exposing your username,\ndirectory layout etc. This was done for simplicity—pull requests to solve paths anonymization are greatly appreciated!\n\n```mermaid\nflowchart TB\n  Attacker[Malicious package /\n           rogue dependency]\n  Attacker --\u003e|filesystem access| TryFS\n  Attacker --\u003e|use Linux syscalls| TrySys\n  Attacker --\u003e|network bind| TryNet\n\n  subgraph Threats\n    TryFS[try to read ~/.ssh, /etc,\n          or other host secrets]\n    TrySys[call a forbidden /\n           privileged syscall]\n    TryNet[bind to a privileged port]\n  end\n\n  TryFS --\u003e|failure| FS\n  TrySys --\u003e|blocked| Seccomp[seccomp sandbox]\n  TryNet --\u003e|optional| Caps\n\n  subgraph Mitigations\n    FS[**Mount policy**: project dir is RW;\n       everything else is RO or absent]\n    Seccomp[**seccomp** syscall whitelist\n            \u003ccode\u003eSANDBOX_SECCOMP_ALLOW=\u003c/code\u003e]\n    Caps[capabilities / UID mapping\n         controlled by \u003ccode\u003e\u003cb\u003eBWRAP_ARGS=\u003c/b\u003e\u003c/code\u003e]\n  end\n```\n\n\nContributing\n------------\nYou see a mistake—you fix it. Thanks!\n\n\nAlternatives\n------------\n1. A popular alternative are the aforementioned Docker/OCI containers\n   and manual management of their runtime. This comes free when the\n   worked on project itself deals in\n   [Continerfiles](https://manpages.debian.org/unstable/Containerfile). \n2. [`sandbox-run`](https://github.com/sandbox-utils/sandbox-run),\n   a similar bubblewrap-based shell script to sandbox arbitrary executables.\n3. On Linux, [AppArmor](https://apparmor.net), even with\n   [apparmor.d](https://github.com/roddhjav/apparmor.d)\n   applied, doesn't ship a generic `python` profile, so one would go\n   through explicit `aa-exec --profile my-custom-env`, but writing\n   custom AppArmor profiles is less common than simply using containers.\n4. [Firejail](https://github.com/netblue30/firejail/).\n   An indie C project with virtually no dependencies (which\n   [\u003cdel\u003eRed Hat\u003c/del\u003e\u003cins\u003eIBM\u003c/ins\u003e has a perfectly reasonable position on](https://github.com/containers/bubblewrap?tab=readme-ov-file#related-project-comparison-firejail))\n   that sets up its own sandbox. I guess it's a matter of trust.\n   Similarly to AppArmor, requires writing a custom profile.\n5. A custom\n   [`seccomp` initialization script](https://healeycodes.com/running-untrusted-python-code),\n   executed at interpreter startup using\n   [~~`PYTHONSTARTUP=`~~](https://docs.python.org/3/using/cmdline.html#envvar-PYTHONSTARTUP)\n   [`sitecustomize`](https://docs.python.org/3/library/site.html#module-sitecustomize)\n   startup hook.\n6. On macOS, [`sandbox-exec`](https://igorstechnoclub.com/sandbox-exec/)\n   or Apple Containerization®.\n\nIn comparison to the above, `sandbox-venv` is like `chroot` on steroids.\nIt uses the same isolation primitives that containers use\n(process sandbox via Linux namespaces, isolated filesystem view),\nbut without all of the container runtime baggage ... YMMV.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsandbox-utils%2Fsandbox-venv","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsandbox-utils%2Fsandbox-venv","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsandbox-utils%2Fsandbox-venv/lists"}