From 7552760ba05a45f2738dd8708ee67f7e596abfbb Mon Sep 17 00:00:00 2001 From: jknapp Date: Wed, 10 Jun 2026 07:02:54 -0700 Subject: [PATCH] fix(cac-lsphp): normalize $_SERVER DOCUMENT_ROOT/SCRIPT_FILENAME to /home MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The symlink makes __FILE__/__DIR__/realpath/getcwd report /home//public_html (WordPress/frameworks), but $_SERVER['DOCUMENT_ROOT']/['SCRIPT_FILENAME'] are raw env vars OLS sets to its /mnt/users view — apps that build/compare paths from them would see /mnt/users. Added a tiny auto_prepend (cac-lsphp-normalize.php, wired via a scan-dir ini) that realpath-canonicalises those two back to /home. Customer sites have no auto_prepend by default, so no conflict. Verified clean-room (committed image, fresh boot): DOCUMENT_ROOT and SCRIPT_FILENAME both report /home//public_html through the shared OLS. Now byte-for-byte 1:1 with cac-fpm/cac-litespeed. Co-Authored-By: Claude Opus 4.8 (1M context) --- Dockerfile.lsphp | 1 + scripts/cac-lsphp-normalize.php | 30 ++++++++++++++++++++++++++++++ scripts/entrypoint-lsphp.sh | 9 +++++++++ 3 files changed, 40 insertions(+) create mode 100644 scripts/cac-lsphp-normalize.php diff --git a/Dockerfile.lsphp b/Dockerfile.lsphp index af1599e..c70de7b 100644 --- a/Dockerfile.lsphp +++ b/Dockerfile.lsphp @@ -41,6 +41,7 @@ RUN apt-get update && \ COPY ./scripts/entrypoint-lsphp.sh \ ./scripts/detect-memory-lsphp.sh \ ./scripts/healthcheck-lsphp.sh \ + ./scripts/cac-lsphp-normalize.php \ /scripts/ RUN chmod +x /scripts/entrypoint-lsphp.sh /scripts/detect-memory-lsphp.sh /scripts/healthcheck-lsphp.sh diff --git a/scripts/cac-lsphp-normalize.php b/scripts/cac-lsphp-normalize.php new file mode 100644 index 0000000..039a715 --- /dev/null +++ b/scripts/cac-lsphp-normalize.php @@ -0,0 +1,30 @@ +/mnt/users mount, + * so OLS sends lsphp $_SERVER['DOCUMENT_ROOT'] / ['SCRIPT_FILENAME'] under + * /mnt/users///... . The sidecar symlinks that back to the real + * /home/ mount, so file operations resolve and PHP's own __FILE__/__DIR__/ + * realpath()/getcwd() already report /home//public_html. But the RAW env + * strings OLS set still read /mnt/users, which would leak to the (uncommon) apps + * that build or compare paths from $_SERVER['DOCUMENT_ROOT']. + * + * Canonicalise those two via realpath() so cac-lsphp is byte-for-byte 1:1 with + * cac-fpm/cac-litespeed (where DOCUMENT_ROOT is natively /home//public_html). + * Cheap (two realpath calls, cached by realpath_cache) and side-effect-free. + * + * Customer sites have no auto_prepend by default, so this is the only prepend in + * play. If a site sets its own auto_prepend_file via .user.ini it overrides this + * (theirs wins) — acceptable: paths still resolve via the symlink, only the raw + * string differs. + */ +foreach (array('DOCUMENT_ROOT', 'SCRIPT_FILENAME') as $__cl_key) { + if (!empty($_SERVER[$__cl_key]) && strncmp($_SERVER[$__cl_key], '/mnt/users/', 11) === 0) { + $__cl_real = realpath($_SERVER[$__cl_key]); + if ($__cl_real !== false) { + $_SERVER[$__cl_key] = $__cl_real; + } + } +} +unset($__cl_key, $__cl_real); diff --git a/scripts/entrypoint-lsphp.sh b/scripts/entrypoint-lsphp.sh index a07da04..c2f9edb 100644 --- a/scripts/entrypoint-lsphp.sh +++ b/scripts/entrypoint-lsphp.sh @@ -90,6 +90,15 @@ if [ -n "$SCAN_DIR" ]; then ; rendered at container start by entrypoint-lsphp.sh error_log = /home/${user}/logs/php-fpm/error.log log_errors = On +EOF + ## Normalise \$_SERVER['DOCUMENT_ROOT']/['SCRIPT_FILENAME'] from the OLS-sent + ## /mnt/users path back to /home/ so cac-lsphp is byte-for-byte 1:1 with + ## cac-fpm. Customer sites have no auto_prepend by default, so this is safe; a + ## site that sets its own .user.ini auto_prepend overrides it (paths still + ## resolve via the symlink either way). + cat > "$SCAN_DIR/99-cac-lsphp-normalize.ini" <<'EOF' +; rendered at container start by entrypoint-lsphp.sh +auto_prepend_file = /scripts/cac-lsphp-normalize.php EOF ## Per-site opcache override (panel: Advanced Tuning → OpCache size); falls ## back to the baked lsphp-overrides.ini defaults when unset.