CVE-2026-59989 Overview
CVE-2026-59989 is a code injection vulnerability [CWE-94] in the Phalcon PHP framework's Volt template engine. The flaw resides in the resolveFilter function within phalcon/Mvc/View/Engine/Volt/Compiler.zep. The join filter builds compiled PHP by inserting raw separator and array token values directly into generated code without passing them through expression(). An attacker who can influence Volt template source can place quote-breaking content in a join argument to inject arbitrary PHP into the compiled cache file. That code executes when Phalcon\Mvc\View\Engine\Volt::render() loads the template.
Critical Impact
Attackers who control Volt template input achieve remote code execution on the web server through injected PHP written to the compiled template cache.
Affected Products
- Phalcon PHP framework (cphalcon) versions 5.15.0 and earlier
- Applications using Phalcon\Mvc\View\Engine\Volt with untrusted template sources
- Web applications caching compiled Volt templates on disk
Discovery Timeline
- 2026-08-21 - CVE-2026-59989 published to NVD
- 2026-08-27 - Last updated in NVD database
Technical Details for CVE-2026-59989
Vulnerability Analysis
The Volt template engine compiles Volt syntax into PHP source and writes the result to a cache file for execution. Most filter handlers in resolveFilter route arguments through the expression() method, which safely emits PHP literals. The join filter branch bypasses this routing. It concatenates funcArguments[1]["expr"]["value"] and funcArguments[0]["expr"]["value"] directly into the compiled join(...) call surrounded by single quotes.
Because the raw token value is spliced into PHP source, a string containing a single quote terminates the literal early. Following characters become executable PHP within the compiled cache file. On the next render() call, PHP includes the cache file and evaluates the attacker-supplied code with the privileges of the web server process.
Root Cause
The root cause is missing output encoding during template compilation. The join filter branch treats parser token values as trusted PHP source rather than as data. Peer filters use this->expression() to emit safe PHP representations of arguments, but the join branch omitted that call, breaking the compiler's invariant that user-controlled tokens never reach generated code verbatim.
Attack Vector
Exploitation requires the ability to control or influence a Volt template rendered by the target application. This includes environments where templates are user-supplied, sourced from a database, or generated from user input passed through Volt syntax. The attacker injects a join filter call whose separator argument contains a single quote followed by PHP code. Once Volt compiles and caches the template, subsequent renders execute the injected payload.
case "format":
return "sprintf(" . arguments . ")";
case "join":
- return "join('" . funcArguments[1]["expr"]["value"]
- . "', " . funcArguments[0]["expr"]["value"] . ")";
+ return "join(" . this->expression(funcArguments[1]["expr"])
+ . ", " . this->expression(funcArguments[0]["expr"]) . ")";
case "json_encode":
return "json_encode(" . arguments . ")";
Source: GitHub Commit e434061. The patch routes both join arguments through this->expression(), ensuring the compiler emits safe PHP literals instead of raw token values.
Detection Methods for CVE-2026-59989
Indicators of Compromise
- Unexpected PHP files or modifications in the Volt compiled template cache directory
- Compiled Volt cache files containing PHP constructs outside expected join(...) output such as system(, passthru(, backticks, or eval(
- Web server processes spawning shells or outbound network connections shortly after template rendering
- Volt template sources containing |join filters with separator arguments that include single quotes or PHP syntax
Detection Strategies
- Perform static analysis of Volt template sources for join filter usage where arguments derive from user input or database content
- Scan compiled Volt cache files for PHP tokens that should not appear inside a simple join() expression
- Alert on file integrity changes to the Volt cache directory outside deployment windows
- Correlate PHP-FPM or Apache child process anomalies with recent template renders
Monitoring Recommendations
- Enable audit logging on the directory Phalcon uses for compiledPath and forward events to a central log store
- Monitor for outbound connections and unexpected child processes originating from the web server user
- Track deployments of Phalcon and compare installed version against the fixed 5.16.0 release
How to Mitigate CVE-2026-59989
Immediate Actions Required
- Upgrade cphalcon to version 5.16.0 or later, which contains the fix from pull request #17217
- Audit all Volt templates for use of the join filter with dynamic separators or arrays sourced from untrusted input
- Purge existing compiled Volt cache files after upgrading to remove any pre-compiled poisoned templates
- Restrict write access to template source directories to trusted deployment pipelines only
Patch Information
The fix is included in Phalcon v5.16.0. Patch commit e434061 modifies the join branch in resolveFilter to route both arguments through this->expression(). Full advisory details are published as GHSA-hrwp-4hh9-c8r8.
Workarounds
- Remove or disable Volt templates whose source can be influenced by untrusted users until the framework is upgraded
- Replace |join filter usage with server-side pre-joined values passed as pre-formatted strings to the template
- Set the Volt compiled cache directory to read-only for the web server user after regenerating templates through a trusted build process
- Deploy a web application firewall rule that blocks request parameters containing Volt filter syntax with embedded single quotes
# Upgrade Phalcon via PECL and clear the Volt compiled cache
sudo pecl upgrade phalcon-5.16.0
sudo systemctl restart php-fpm
find /var/cache/volt -type f -name '*.php' -delete
php -r 'echo Phalcon\Version::get();'
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

