Replaces the `DOKKU_ARCHIVE_MAX_SIZE` and `DOKKU_ARCHIVE_MAX_FILES` environment variables with global git properties (`archive-max-size` and `archive-max-files`), configurable via `dokku git:set --global` and surfaced through `dokku git:report --global`. Defaults remain `1073741824` bytes and `10000` entries.
Archives passed to git:from-archive and certs:add were extracted without symlink or path validation, allowing a crafted archive to write arbitrary files anywhere writable by the dokku user via symlink traversal. Extraction now pre-scans entries for absolute paths, parent traversal, and unsafe symlinks, applies the GNU tar `--no-unsafe-links` flag when available, and validates symlinks after extraction.
The previous use of `touch` before `netrc set` allowed the file to inherit the umask and be world-readable, exposing stored git credentials to local users. The set and unset paths now explicitly chmod 0600 and chown to the dokku user, and the plugin install hook repairs permissions on already-affected installations.
Add defense-in-depth sanitization for OpenResty include files to prevent
OS command injection via malicious filenames that break shell quoting in eval.
- Add filename validation in core-post-extract using regex [^a-zA-Z0-9_.-]
- Validate both http-includes and location-includes paths
- Abort deploy via dokku_log_fail on unsafe filenames
- Skip non-regular files (symlinks, directories) during extraction
- Add security regression test with unsafe filename containing space
- Keep existing guards in docker-args-process-deploy as belt-and-suspenders
- Update documentation to clarify allowed filename characters
Addresses CVSS 9.9 vulnerability where filenames like poc'$(cmd)'x.conf
could escape shell quoting and execute arbitrary commands during deploy.
The previous form set `trap "rm -rf '$TMP_DIR'" RETURN` inside the test, but bats propagates `RETURN` traps to nested function calls, so the trap fired on the first `assert_success` and removed the work directory before the trigger script ran. Switching to bats's per-test `BATS_TEST_TMPDIR` removes the trap entirely and lets bats handle cleanup.
Both helpers wrap `run /bin/bash -c "..."` with the env vars dokku plugin scripts and `plugn` need, replacing the long inline boilerplate that was duplicated across `tests/unit/resource_3.bats` and the new `core-post-extract` regression tests in the builder bats files.
The builder-dockerfile, builder-lambda, builder-nixpacks, builder-pack and builder-railpack `core-post-extract` triggers assigned `$2` to a local `SOURCECODE_WORK_DIR` but called `pushd "$TMP_WORK_DIR"`, which was unset. Bash 5.2 silently accepted `pushd ""`, so the bug stayed dormant. Bash 5.3 (shipped with Ubuntu 26.04) makes it a hard error and `set -e` aborts the trigger, causing every `git push` to fail with `pushd: null directory`.
The release_and_deploy function and several proxy plugin scripts call fn-plugin-property-* helpers without sourcing `plugins/common/property-functions`, producing `command not found` errors during deploys. Source the helpers from `plugins/common/functions` and from each proxy plugin entry point that consumes them.
The ppa:cncf-buildpacks/pack-cli launchpad source is currently down, breaking the runtime image build and any bats run that calls install_pack. Pack is now tracked in contrib/dependencies.json and pulled from the buildpacks/pack GitHub release tarball, matching how other binary deps are managed. update-deb-dependencies gains hyphen-aware word-boundary matching so single-word names like pack cannot accidentally rewrite unrelated debian/control lines, keeping pack out of the package's hard requirements.
`SaveEntry` and `touchMigrationFlag` wrote files via `os.WriteFile` and `os.Create` and never chowned them, so the install-time legacy-mount migration produced root-owned `legacy-*.json` files in `/var/lib/dokku/data/storage-registry/entries/`. The dokku user that runs `ps:rebuild` could not read them and every rebuild on a 0.37.x to 0.38.0 upgrade failed with permission denied. The companion `repairRegistryOwnership` pass in `TriggerInstall` rewrites ownership across the whole registry tree so installs that already ran the buggy code are fixed on the next package upgrade, since the per-app migration flag would otherwise cause `MigrateLegacyMounts` to skip the broken files forever.
The previous regression case selected `--openresty-global-hsts`, but `hsts` is rejected by `openresty:set --global` because it is not in the plugin's GLOBAL_KEYS list. Switch to `--openresty-letsencrypt-server`, which the bats setup already pins to a known staging URL, so the test exercises the same `--global --<info-flag>` parsing path without needing a separate set step.
The argument-handling block in each plugin's report command overwrote APP with --global before checking whether $1 actually held an info flag, so `dokku <plugin>:report --global --<plugin>-foo` always emitted the full report instead of the requested value. Reorder the conditionals across the sixteen plugins that share the pattern so the info-flag check runs first, and add a regression bats case for each one.
The byjg/easy-haproxy image polls Docker for label changes every 10
seconds by default, which races with the haproxy bats suite and
intermittently produces curl exit 7. Expose `refresh-conf` as a
global-only haproxy property that maps to `EASYHAPROXY_REFRESH_CONF`,
lower it to 2 seconds in the bats setup, and wrap the localhost HTTP
assertions in a retry loop so checks wait for haproxy to converge
rather than failing on the first attempt.
The default nginx template rendered four near-identical server blocks per app, so the same listen, access_log, error_log, ssl_*, error_page, and proxy chain had to be maintained across http, https, grpc, and grpcs branches. Merging http and https into a single branch keyed on an `is_ssl` boolean, and likewise grpc and grpcs, removes the duplicate proxy_set_header chain and error-page locations and brings the structure in line with the openresty proxy template. Output is preserved up to whitespace and the existing `cat -s` pass already squashes the leftover blank lines. Adds plugins/nginx-vhosts/template_test.go exercising the rendered output via sigil as a Go library across HTTP-only, HTTPS, HTTP-to-HTTPS redirect, no-listeners 502 fallback, gRPC, gRPCs, gRPC-without-listeners skip, IPv4 bind, upstream blocks with and without keepalive, the access_log `off` short-circuit, the X-Forwarded-Ssl toggle, the http2 listen-parameter vs directive split, http2_push_preload conditional emission, and the nginx.conf.d/*.conf include in every code path; and tests/unit/nginx-vhosts_16.bats covering deploy plus `nginx -t` end-to-end for both HTTP and HTTPS apps.