Skip to content

e107 v2.3.6

Choose a tag to compare

@Deltik Deltik released this 24 May 10:34
· 544 commits to master since this release

Caution

v2.3.6 is a bug-fix release for sites on v2.3.5 or earlier.
Upgrade from v2.3.5 or earlier 2.x. If your site tracks the master branch, you are already past v2.3.6, so installing it would be a downgrade. v2.4.x is planned to be the next forward step.

Highlights

  • [Security] Command injection in resize_image() (GHSA-3j33-c9v4-4p42). The ImageMagick convert command line did not shell-escape its destination filename; reachable via news submission with the right combination of prefs. (794a179f)
  • Host-header recovery for sites locked out by v2.3.4 / v2.3.5. The GHSA-7pmw-jwvr-cq2x killswitch now case-folds, strips www., and strips trailing ports before comparison, and returns a logged 503 instead of a bare die(). Closes #5627 and #5634. (aba3b169)
  • trusted_hosts SitePref for parked, staging, and multi-host setups. Authorise additional incoming hostnames from Admin → Preferences without touching e107_config.php. (c2bb246b)
  • PHP 8 fatal-error sweep. thumb.php (#5664), fpw.php on v1-style themes (#5653), the FAQ cron registration, and the admin nav / user-handler bare-constant reads all stop crashing on PHP 8. (59aef4f1, 02a79729, 0635801a, b1290139)

For Administrators

Added

  • trusted_hosts SitePref. New textarea below the Site URL row in Admin → Preferences. Paste additional hostnames one per line (e.g. staging.example.com, example.org); the server-side normaliser strips schemes/paths and de-duplicates case-insensitively, so https://staging.example.com/ saves as staging.example.com. The pref is purely additive: requests whose Host matches the configured siteurl continue to pass without listing it here. Ref GHSA-7pmw-jwvr-cq2x, #5627. (c2bb246b)

Changed

  • Host-header validation (security). www. and the bare apex now match each other, hostnames compare case-insensitively, and trailing ports are stripped before comparison, so https://example.com:8080/ matches https://example.com/. Sites that were stuck on the "Site Configuration Issue Detected" page after upgrading from v2.3.3 should now boot again without manual SQL edits.

    The check itself still requires the request Host to match the configured siteurl (or fall under one of its subdomains, or be listed in trusted_hosts). Ref GHSA-7pmw-jwvr-cq2x. (aba3b169)

  • Host-mismatch response. The bare die('Site Configuration Issue Detected. ...') that returned 200 OK with one line of plain text is replaced by a 503 Service Unavailable with a short HTML body that points the operator at the server error log. The diagnostic detail (configured siteurl, request Host) is written via error_log(), which previously sat after the die() and never fired.

    The visible page intentionally does not echo the incoming Host, the configured siteurl, or any admin URL. The diagnostic surface stays in the log, which already requires server access. (aba3b169)

Fixed

  • Thumbnail rendering on PHP 8. thumb.php boots the framework directly without class2.php, so E107_DEBUG_LEVEL was never defined for that request; the first call that opened a DB handle (typically when the SitePrefs disk cache was cold) hit a PHP 8+ fatal in e_db_pdo::__construct() and broke every thumbnail on the site. Now guarded with defset(). Fixes #5664. (59aef4f1)
  • Thumbnail database credentials. With the E107_DEBUG_LEVEL fatal out of the way, thumb.php then failed with SQLSTATE[HY000] [2002] No such file or directory because its manual bootstrap chain never populated the MySQL config the PDO connector reads. The bootstrap now routes through e107::initCore() (the same call class2.php uses), so credentials are set before the first DB call. Follow-up to #5664 / #5665. (02a79729)
  • Forgot-password page on v1-style themes. fpw.php fataled with Undefined constant LAN_112 when the active theme had no theme.xml (the legacy theme path uses e107_core/templates/legacy/fpw_template.php, which still references the old constant). The BC shim that mapped LAN_112LAN_FPW22 only fired in the members-only branch; it now fires before every downstream template require, regardless of branch. Fixes #5653. (d9725b91)
  • Other legacy templates. Any legacy core template under e107_core/templates/legacy/ that referenced a dropped v1.x LAN_* constant could fatal on PHP 8 with a single missed reference. e107::predefineLegacyLans() now tokenises each legacy template before it loads and auto-defines any missing LAN_* with its own name as a value, emitting an E_USER_WARNING per auto-define so the maintainer trail stays visible. Wired into the six legacy require sites (fpw.php ×2, search.php, signup.php, user.php, usersettings.php). Refs #5653. (b1290139)
  • Admin navigation icons and user-handler permissions on PHP 8. sitelinks_class::setIconArray() built the admin nav map from ~40 E_32_* constants and user_handler::$core_perms referenced dozens of ADLAN_* / ADMSLAN_* / E_16_* / E_32_* entries, all loaded lazily by the admin language file as bare reads. PHP 8 promotes bare reads of undefined constants to fatal Errors, so any caller that hit these before the admin language file loaded crashed. Both sites are now wrapped in defset() so the undefined case returns the empty default. (0635801a)
  • FAQ plugin cron registration on PHP 8. e107_plugins/faqs/e_cron.php referenced LANA_FAQ_CRON_1, LANA_FAQ_CRON_2, and LAN_AUTOMATION at admin cron-registration time, which runs before English_admin.php is loaded. PHP 8 turned the bareword fallback into a fatal; the registration path now uses defset(). (21f7b584)
  • strftime() deprecation on PHP 8.1+. StrptimeTrait::buildMonthArrays() called PHP's strftime() to localise month names. PHP 8.1 deprecated the function and PHP 9 will remove it, so the @-suppressed call still landed in error_log. Both calls are now routed through the eShims::strftime() polyfill that already exists in the tree. (295ce2a1)

For Developers

Added

  • e107::predefineLegacyLans($path) token-scan safety net for legacy templates. Tokenises the requested template, finds bare LAN_* token references (skipping function/method/class/static contexts and call sites), and define()s any that are still missing with their own name as value. Token extraction is cached on hash_file('sha256', $path) keyed entries (APCu when available, otherwise a file under e_CACHE); a process-local memo short-circuits repeat resolutions. Warm-cache cost is roughly 6 µs vs the ~4 µs baseline.

    Wired into fpw.php, search.php, signup.php, user.php, and usersettings.php. The wrappers call it immediately before the existing require/include so caller-scope template variables ($FPW_TABLE, $SIGNUP_BODY, etc.) remain assignable. Refs #5653. (b1290139)

  • trusted_hosts SitePref plumbing. A new e107::isAllowedHost() private helper composes the allow-list from parse_url(siteurl)['host'] plus the entries saved in trusted_hosts. The pref reads as a newline-separated list; the saver normalises (scheme/path strip, case-fold dedup) before writing back. (c2bb246b)

  • Docker-based parallel test environments. e107_tests/bin/e107-tests is a new CLI that spins up an isolated PHP + Apache + MySQL stack per worktree (up | down | reset | clean | install | urls | status | logs | shell | db-shell | exec | run | list). Each worktree + matrix combo gets a deterministic compose project name derived from the worktree path, so parallel sessions never collide on container names, networks, volumes, or host ports. A new config.docker.yml layer slots into the existing config cascade (sampleymldockerlocal).

    Existing deployers (local, sftp, cpanel, none) and the CI workflows are untouched. (9b5f40ea)

  • Test coverage. resize_handlerTest exercises the ImageMagick branch of resize_image() with three command-substitution payloads and asserts no marker file is created and no id-style output leaks into a filename. e107HostValidationTest covers the case-fold, www-strip, and port-strip behaviour of e107::isAllowedHost() plus trusted_hosts composition. e107RequireLegacyTemplateTest covers the tokeniser context filtering, define/warn behaviour, scope preservation, missing-file return value, and the #5653 regression case. (794a179f, c2bb246b, b1290139)

Changed

  • language::bcDefs() no-argument default expanded. The boot-time call from class2.php:590 previously defined only LAN_180 → LAN_SEARCH. Legacy templates that referenced any other dropped v1.x constant whose replacement happens to live in English/English.php still fataled on PHP 8. The default now covers ~20 boot-resolvable aliases: generic actions (LAN_406 / LAN_419 / LAN_435), the v1 download prefix (LAN_dl_7..LAN_dl_35), and a defined-as-empty group for dropped-without-replacement constants.

    Conflict policy: where the same legacy constant had divergent mappings across per-entrypoint shims (LAN_7..LAN_10, LAN_112, LAN_122, etc.) the global default deliberately omits them and per-entrypoint shims remain authoritative. Mappings whose replacement lives in a lazy lan_*.php cannot be resolved at boot (defined() returns false and the entry silently no-ops); those are still handled by per-entrypoint language::bcDefs() calls after the relevant e107::coreLan(). Refs #5653. (d2ef6b10)

  • Host comparison helper. A new e107::normaliseHost() lowercases, strips a trailing :port, and strips a leading www. before comparison. Plugins that previously did their own Host-header guards should switch to e107::isAllowedHost() so the trusted_hosts allow-list composes correctly. (aba3b169)

  • resize_image() shell escaping. The convert command line now passes the destination through escapeshellarg() and casts the integer geometry/quality args. If you maintain a fork or call resize_image() directly from a plugin, no migration is required, but any caller that previously relied on quoting the destination itself can drop that quoting. Ref GHSA-3j33-c9v4-4p42. (794a179f)

Fixed

  • thumb.php bootstrap. The manual bootstrap chain (prepare_request / setDirs / set_constants / ...) is replaced by e107::initCore() fed the same mySQL-prefixed $sql_info compact() array class2.php passes.

    Note the v2.3.x e107::_init() does not translate keys the way master's e107::setMySQLConfig() does, so the array must go in prefixed. The master-style str_replace('mySQL', '', $k) translation breaks v2.3.x because e_db_pdo still reads $config['mySQLserver'] etc. Follow-up to #5664 / #5665. (02a79729)

  • submitnews.php title slug. The slug used to build the destination filename for resize is now confined to [A-Za-z0-9_] before reaching resize_image(), so shell metacharacters never escape the filter even if a future caller relaxes the filename quoting. Ref GHSA-3j33-c9v4-4p42. (794a179f)

  • resize_handlerTest teardown sentinel. tearDown() ran even when setUp() had short-circuited via markTestSkipped() (CI images without ImageMagick); in that case $savedPref === null took the unset($GLOBALS['pref']) branch and wiped the global pref array for every following test in the shuffled unit-suite run. A prefMutated sentinel now flips true only after _before() mutates $pref, and _after() bails out early otherwise.

    Locally invisible because ImageMagick was usually installed and the early-skip branch wasn't taken. (8cf19729)

  • CI on the legacy PHP cells. actions/checkout@v3 was reclassified to a Node 20 action, which requires glibc 2.27+; the official php:5.6 / php:7.0 images are on Debian 9 (glibc 2.24) and the action errors before its main logic. The two legacy cells now use an inline git fetch + git checkout against the run SHA.

    The php:5.6 × mysql:8.0 and php:7.0 × mysql:8.0 combinations have also been dropped via matrix.exclude. Mysql:8 starts with character-set-server=utf8mb4 by default and the PDO driver in those PHP versions doesn't recognise the charset, failing the connection handshake before any test runs. (d024d205, 756dc92c)

  • Composer audit no longer blocks resolution. Composer 2.9.x refuses to resolve package versions affected by a Packagist security advisory at install time. Every twig/twig between v1.28 and v3.20 carries at least one advisory, leaving v3.26.0 as the only resolution candidate; v3.26.0 requires PHP 8.1+, so the PHP 7.4 cell of the unit-test matrix failed composer update. The test harness is dev-only and never ships with a release, so the audit's resolution-time block is now disabled via config.audit.block-insecure: false. composer audit still runs after install and reports any advisories. (46351d1a)

  • Matrix CI design restored. The "Collapse 5×5 matrix into one host-orchestrated job" experiment that landed during the v2.3.5 cycle was reverted: single jobs ran ~20 minutes end-to-end vs ~5 minutes per cell under master's parallel matrix, and exhibited intermittent docker / mysql container startup races at the per-cell DB pre-create step. v2.3.x CI is back to the matrix layout that mirrors master, with the legacy PHP cells extended via the inline-checkout and matrix.exclude pair above. (da47684c)

  • language::bcDefs() graveyard collisions on v2.3.x. Pre-defining LAN_199, LAN_406, and LAN_419 at boot collided with v2.3.x's lazy lan_search.php and lan_upload.php (which still use define(); master converted them to array form in #5465). PHPUnit's convertWarningsToExceptions=true escalated the "Constant already defined" warning and aborted mid-load. Those three entries are dropped on this branch; the lazy LAN files remain authoritative. (f1b4bf27)