1
0
Fork 0
mirror of https://github.com/imjasonh/terraform-playground synced 2026-07-08 07:44:57 +00:00

docs: document sdists as opt-in with security (host RCE) and single-arch caveats

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
This commit is contained in:
Cursor Agent 2026-06-11 01:42:12 +00:00
parent 872abd1ef7
commit bf565be0ce
No known key found for this signature in database
2 changed files with 41 additions and 18 deletions

View file

@ -73,6 +73,7 @@ the config value, which overrides the built-in default.
| `max-wheel-layers` | `--max-wheel-layers` | *(derived from `max-layers`)* |
| `push-concurrency` | `--push-concurrency` | auto (≥ 4, scales with CPUs) |
| `no-cache` | `--no-cache` | `false` (caching is on by default) |
| `build-sdists` | `--build-sdists` | `false` (sdists are opt-in; see warning below) |
| `extras` | `--extra` (repeatable) | — (enables uv project optional-dependency groups) |
| `package` | `--package` | — (build a single uv workspace member) |
| `python` | `--python` | auto-detected from the base |
@ -166,18 +167,31 @@ get from `uv sync --no-dev`), not every package in the lock:
Windows-only or stale-Python-only packages. Markers are evaluated per platform,
so each arch of a multi-arch build gets the correct set.
### Source distributions (sdists)
### Source distributions (sdists) — opt-in
When the lock pins a package that has **no compatible wheel** (an sdist-only
release, or a compiled package for a platform without a published wheel), pymage
builds a wheel from the sdist using the host's `pip` (`pip wheel --no-deps`).
This requires a Python toolchain (`python3`/`python` with `pip`) on the build
host.
By default pymage installs **only pre-built wheels**. If the lock pins a package
with no compatible wheel, the build fails fast and tells you to either supply a
wheel via `--find-links` or opt into sdist building.
- **Pure-python sdists** build to a `py3-none-any` wheel and work for any target.
- **Compiled sdists** can only be built for the **host platform**; building such
a package for a different architecture fails with a clear compatibility error.
Prefer a base/lock that provides pre-built wheels for those.
Building from an sdist is **off by default** and must be enabled with
`--build-sdists` (or `build-sdists = true` in `[tool.pymage]`). When enabled,
pymage downloads the hash-verified sdist and builds a wheel with the host's `pip`
(`pip wheel --no-deps`), which requires a Python toolchain (`python3`/`python`
with `pip`) on the build host. It is a power-user feature with two important
caveats:
- **Security: building an sdist runs arbitrary code from the dependency** on the
build host. An sdist's `setup.py` / build backend executes during the build,
and — unlike a `docker build` — pymage does **not** sandbox it. A malicious or
compromised dependency can therefore achieve code execution on your build
machine. Only enable this for locks you trust, ideally in an ephemeral/CI
environment, and prefer pre-built wheels (`--find-links`, or a registry that
publishes wheels) whenever possible.
- **Architecture: compiled sdists are single-arch.** A pure-python sdist builds
to a `py3-none-any` wheel that works on any target. A *compiled* sdist can only
be built for the **host** platform, so a multi-arch build that needs to build
such a package will fail the compatibility check for the non-host arch. Use a
base/lock that provides pre-built wheels for every target arch instead.
Built wheels are cached (keyed by the sdist hash and target) so they aren't
rebuilt on subsequent builds.
@ -241,6 +255,7 @@ base that advertises its version.
| `--package` | Build a single uv workspace member by name (default: union of all members). |
| `--cache-dir` | Cache root (default: `$PYMAGE_CACHE_DIR` or the per-user cache dir). Caches compressed layers, downloaded wheels, and base interpreter detection. |
| `--no-cache` | Disable all caching (layers, downloaded wheels, interpreter detection). |
| `--build-sdists` | Allow building wheels from sdists when no compatible wheel exists. **Runs the dependency's build code on the host (no sandbox); single-arch for compiled packages.** Off by default. |
| `--prefix` | install prefix / venv root (default `/app/.venv`). |
| `--workdir` | image working dir and source destination (default `/app`). |
| `--user` | image user, e.g. `65532`. |

View file

@ -123,13 +123,21 @@ the worst case is one bucket rather than the whole environment.)
1. **(FIXED) Installed the whole `uv.lock`, including dev groups.** Caused
5063% dependency bloat above. Now resolves the runtime closure.
2. **(FIXED) sdist-only dependencies.** `imgpush` previously failed on
`timeout-decorator==0.5.0`, which publishes no wheel. pymage now builds a
wheel from the sdist (`pip wheel --no-deps`) and feeds it into the existing
layer path; the build host needs `python`/`pip`. Pure-python sdists build to
`py3-none-any` (any target); compiled sdists build for the host platform only
(cross-arch builds of those still error clearly). `imgpush` now builds with 52
runtime wheels including the sdist-built `timeout-decorator`.
2. **(FIXED, opt-in) sdist-only dependencies.** `imgpush` previously failed on
`timeout-decorator==0.5.0`, which publishes no wheel. With `--build-sdists`
(or `build-sdists` in `[tool.pymage]`) pymage builds a wheel from the sdist
(`pip wheel --no-deps`, host `python`/`pip` required) and feeds it into the
existing layer path; `imgpush` then builds with 52 runtime wheels including
the sdist-built `timeout-decorator`. It is **off by default and intentionally
a power-user feature** for two reasons:
- **Security:** building an sdist runs the dependency's own build code on the
host with no container isolation (pymage has no daemon to sandbox it), so a
malicious dependency could get code execution on the build machine. Without
the flag, a wheelless package fails fast with guidance to supply a wheel via
`--find-links` or opt in.
- **Single-arch:** compiled sdists can only be built for the host platform, so
enabling them doesn't make a compiled package multi-arch — pure-python
sdists build to `py3-none-any` and remain portable.
3. **No system/OS packages (by design — documented).** pymage installs Python
wheels, not apt/apk packages. `imgpush` needs `libmagickwand` (for `Wand`) and
`nginx`; those must come from the **base image**. Projects with system-library
@ -162,7 +170,7 @@ the worst case is one bucket rather than the whole environment.)
After the runtime-closure fix and this round of work, **pymage is a smaller,
faster, reproducible alternative to a uv Dockerfile** for the projects studied:
all three now build (including `imgpush`, via sdist building), with no daemon and
all three now build (including `imgpush`, via opt-in sdist building), with no daemon and
no build tooling in the image, and incremental rebuilds re-upload only changed
layers. The main remaining caveat is **runtime system libraries**, which must be
provided by the base image (documented), plus the multi-platform default for