diff --git a/CHANGELOG.md b/CHANGELOG.md index af0754e06..907dadf7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to Instatic will be documented here. This project is pre-1.0. Breaking changes may appear in minor or patch releases until a stable release line exists. +## 0.0.19 - 2026-09-10 + +### Security + +- Closed a server-side request forgery in the media storage write path ([GHSA-9pq7-m5wf-r7f6](https://github.com/CoreBunch/Instatic/security/advisories/GHSA-9pq7-m5wf-r7f6)). A plugin holding only `media.storage.adapter` supplies the step URLs in an upload plan, and the executor streamed the bytes to them with an unguarded `fetch()`, so the grant carried the network reach of `network.outbound` without asking for it: arbitrary `PUT` and `POST` at loopback, private, link-local, and cloud-metadata addresses, with plugin-chosen headers and the validated media bytes as the body. This is the write-side sibling of the media migration SSRF fixed in 0.0.18, which closed the read path and left this one open. Upload plan steps now go through the same SSRF-safe guard as the read path: internal addresses are refused before a connection opens, the connection is pinned to the checked IP, and every redirect hop is re-validated. Reported by [@skeletonsec](https://github.com/skeletonsec). + +### Publishing and runtime + +- Fixed the runtime dependency package server returning 404 for every package asset on Windows hosts ([GHSA-hwp9-vc7h-gvvf](https://github.com/CoreBunch/Instatic/security/advisories/GHSA-hwp9-vc7h-gvvf)). The containment check that keeps a resolved path inside the cache directory compared against a hard-coded forward slash, but `path.resolve` produces backslashes on Windows, so the check was false for every legitimate path and the endpoint refused all runtime package assets. It fails closed, so nothing was exposed. Containment is now decided with `path.relative()`, which is correct on both separators, and the same helper replaced a prefix comparison in the site-script workspace that carried no separator at all and would have accepted a sibling directory whose name merely began with the root. Reported by [@uziii2208](https://github.com/uziii2208). + ## 0.0.18 - 2026-09-01 ### Security diff --git a/docs/deployment/README.md b/docs/deployment/README.md index 998ce0a5a..df5019b0a 100644 --- a/docs/deployment/README.md +++ b/docs/deployment/README.md @@ -57,7 +57,7 @@ INSTATIC_IMAGE=ghcr.io/corebunch/instatic:latest docker compose -f compose.prod. Pin a semver tag for predictable upgrades: ```sh -INSTATIC_IMAGE=ghcr.io/corebunch/instatic:0.0.18 docker compose -f compose.prod.yml -f compose.sqlite.yml up -d +INSTATIC_IMAGE=ghcr.io/corebunch/instatic:0.0.19 docker compose -f compose.prod.yml -f compose.sqlite.yml up -d ``` Source builds remain supported for contributors and release-candidate testing: diff --git a/docs/deployment/docker-image.md b/docs/deployment/docker-image.md index 3a92efc50..67a890d91 100644 --- a/docs/deployment/docker-image.md +++ b/docs/deployment/docker-image.md @@ -36,10 +36,10 @@ GHCR is the canonical image registry: ```sh docker pull ghcr.io/corebunch/instatic:latest -docker pull ghcr.io/corebunch/instatic:0.0.18 +docker pull ghcr.io/corebunch/instatic:0.0.19 ``` -The v0.0.18 published image is built for `linux/amd64`. Use it on Railway and x86_64 VPS/container hosts. ARM64 hosts should build from source for now, or wait for the native arm64 release job before pulling GHCR images directly. +The v0.0.19 published image is built for `linux/amd64`. Use it on Railway and x86_64 VPS/container hosts. ARM64 hosts should build from source for now, or wait for the native arm64 release job before pulling GHCR images directly. ## Run With SQLite @@ -92,7 +92,7 @@ Replace `instatic:local` with `ghcr.io/corebunch/instatic:` when deploying Create an app service from Docker image source: ```txt -ghcr.io/corebunch/instatic:0.0.18 +ghcr.io/corebunch/instatic:0.0.19 ``` Attach a Railway volume at `/app/storage`, set the health check path to `/health`, and set app variables: @@ -109,7 +109,7 @@ RAILWAY_RUN_UID=0 `RAILWAY_RUN_UID=0` is required because Railway volumes are mounted as `root` and the published image otherwise runs as the non-root `bun` user. `PUBLIC_ORIGIN=https://${{RAILWAY_PUBLIC_DOMAIN}}` gives Instatic the public origin for its CSRF check now that Railway terminates HTTPS at the edge; the server would auto-detect the same value from `RAILWAY_PUBLIC_DOMAIN`, but setting it explicitly survives custom-domain edits. -Enable Railway Image Auto Updates when you want Railway to move the service forward automatically during a maintenance window. Use `:latest` for "always follow the newest image", or a semver tag such as `:0.0.18` if you want Railway's semver update controls. +Enable Railway Image Auto Updates when you want Railway to move the service forward automatically during a maintenance window. Use `:latest` for "always follow the newest image", or a semver tag such as `:0.0.19` if you want Railway's semver update controls. ## Run On Render From The Image diff --git a/docs/deployment/railway.md b/docs/deployment/railway.md index 0db737e03..c12ba1438 100644 --- a/docs/deployment/railway.md +++ b/docs/deployment/railway.md @@ -16,7 +16,7 @@ Railway is the simplest managed target for Instatic because it can run the publi Both templates use: ```txt -Image=ghcr.io/corebunch/instatic:0.0.18 +Image=ghcr.io/corebunch/instatic:0.0.19 PORT=8080 UPLOADS_DIR=/app/storage/uploads STATIC_DIR=/app/dist @@ -32,7 +32,7 @@ Configure the app service health check path as `/health`. If Railway asks which Use a Docker image source for production installs: ```txt -ghcr.io/corebunch/instatic:0.0.18 +ghcr.io/corebunch/instatic:0.0.19 ``` The image already runs: @@ -48,7 +48,7 @@ Recommended service settings: | Setting | Value | |---|---| | Source | Docker image | -| Image | `ghcr.io/corebunch/instatic:0.0.18` | +| Image | `ghcr.io/corebunch/instatic:0.0.19` | | Public networking | HTTP enabled | | Target port | `8080` | | Healthcheck path | `/health` | @@ -135,7 +135,7 @@ Railway volume backups apply to mounted volumes. For Postgres, use Railway's dat Enable Railway Image Auto Updates on the app service: - Use `ghcr.io/corebunch/instatic:latest` when you want the service to redeploy whenever the `latest` tag moves. -- Use a semver tag like `ghcr.io/corebunch/instatic:0.0.18` when you want Railway to stage matching patch or minor updates according to the service's auto-update preference. +- Use a semver tag like `ghcr.io/corebunch/instatic:0.0.19` when you want Railway to stage matching patch or minor updates according to the service's auto-update preference. Set a maintenance window before enabling automatic updates on sites with attached volumes. diff --git a/package.json b/package.json index 9fee8320d..33e5050d0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "instatic", "private": true, - "version": "0.0.18", + "version": "0.0.19", "engines": { "bun": ">=1.3.0 <1.4.0" },