CakePHP SQL Injection, Fixed in 5.4.4 (CVE-2026-119418)
CakePHP window offsets and collation names allow SQL injection when fed untrusted input. Check exposure and upgrade to 5.4.4, 5.3.8 or 5.2.16.
TL;DR
- Two flaws in CakePHP's database expression objects splice attacker-controlled strings into SQL instead of binding them. VulnCheck published them on October 30, 2026 as CVE-2026-119418, window frame offsets, and CVE-2026-119419, collation names, crediting Abraxas Labs and Mark Story.
- An application is exposed only when its own code passes request data into the affected parameters: frame offsets through
range()orframe(), or collation names throughStringExpressionandIdentifierExpression. The default application skeleton exposes neither on a public form. - Both CVE records carry CVSS v4.0 9.2 and v3.1 8.1. No exploitation is known, and neither CVE is in CISA's Known Exploited Vulnerabilities catalog as of November 1, 2026. Public proof-of-concept labs have been available since October 12, 2026.
- Upgrade to CakePHP 5.4.4, 5.3.8 or 5.2.16, all released November 1, 2026. CakePHP 4.6.6 fixes only the collation flaw; see "How do I fix it?" for the 4.x path and the input-validation workaround.
Am I affected?
You are exposed when all of these hold:
- Your application runs CakePHP through 5.4.3, or a 5.x or 4.x version for which no fixed release exists, per the table below.
- Your own code passes a request-controlled value into one of the affected parameters. These are the
$startor$endofWindowExpression::range(), the offsets or directions offrame(), and collation names passed tonew StringExpression(),new IdentifierExpression()orsetCollation(). - An attacker can reach the request path that builds that query over the network.
The researcher's victim model names the two realistic shapes. One is an analytics report builder that forwards a "last N days" input into range(). The other is an application that forwards a locale or collation picker into the collation constructors. The stock cakephp/app skeleton does not forward either, so the framework alone does not create the exposure.
| Branch | Affected versions | Fixed version |
|---|---|---|
| CakePHP 5.4.x | 5.4.0 to 5.4.3 | 5.4.4 (November 1, 2026) |
| CakePHP 5.3.x | 5.3.0 to 5.3.7 | 5.3.8 (November 1, 2026) |
| CakePHP 5.2.x | 5.2.0 to 5.2.15 | 5.2.16 (November 1, 2026) |
| CakePHP 5.1.x and earlier 5.x | all, per the CVE records' range | none released |
| CakePHP 4.6.x | 4.6.0 to 4.6.5 for CVE-2026-119419; 4.6.0 to 4.6.6 for CVE-2026-119418 | 4.6.6 (November 1, 2026), collation flaw only |
| CakePHP 4.5.x and earlier 4.x | all | none released |
The window-frame flaw is fixed in the first three rows only. The 4.6.6 source still accepts raw string frame offsets, verified in its WindowExpression.php, and the 4.5 line's last release, 4.5.12 of August 2026, predates both fixes. The same patch series also validates neighbouring parameters that the CVE records do not describe: function names in FunctionExpression, aggregate order-by fields in StringAggExpression, and partition() and orderBy() strings.
Check what you run, then audit the code that calls the affected API:
# Framework version (the package the official skeleton requires)
composer show cakephp/cakephp | grep -E '^(name|versions)'
# If the database package was installed on its own
composer show cakephp/database 2>/dev/null | grep -E '^(name|versions)'
# Lock file
grep -A1 '"name": "cakephp/cakephp"' composer.lock | grep '"version"'
# Locate window APIs; inspect every argument and trace request data manually
grep -rnE '\->(range|frame)\b' src/ templates/ 2>/dev/null
# Collation names reaching a constructor or setter
grep -rnE 'setCollation\(\s*\$|new StringExpression\(|new IdentifierExpression\(' src/ templates/ 2>/dev/null
Review each grep hit for one question: can a request parameter reach the argument? The reach is wide. The packagist.org record for cakephp/cakephp shows 21,425,059 total installs with 557,686 in the last month. The split cakephp/database package, which carries these classes, shows 29,353,847 total installs with 857,825 in the last month. Both figures were checked on November 1, 2026.
How do I fix it?
- From the project root, run
composer update cakephp/cakephp, orcomposer update cakephp/databaseif you use the standalone database package. Within a5.4.*,5.3.*or5.2.*constraint this reaches 5.4.4, 5.3.8 or 5.2.16 respectively. - If your
composer.jsonpins an exact affected version, raise it to the fixed version for your line and run the update again. - Deploy the updated
vendor/directory. Reload the web PHP workers or invalidate OPcache according to your deployment procedure so cached code cannot retain the old classes. Long-running PHP processes, such as queue workers and daemonized commands, must be restarted to drop the old code from memory. - On the 4.6 line, update to 4.6.6 with the same commands; it closes the collation flaw only.
- Expect one behavior change: frame offsets containing spaces, such as interval literals, now throw
InvalidArgumentException. See the FAQ for the interval-literal case.
If you cannot upgrade yet, apply the fixed code's rule at your own call boundary:
- Pass only integers,
null, or trusted, pre-builtExpressionInterfaceobjects as frame offsets. - Wrap interval literals in
new QueryExpression("'1 day'")yourself. - For direct
frame()calls, use trusted framework constants for the frame type and both directions; never forward request strings into them. - Restrict collation names to an allowlist your application defines.
This workaround covers only your call sites. Any third-party code calling the same API with request data stays exposed, so treat it as a stopgap for both CVEs while the upgrade is scheduled.
What is the root cause?
The expression objects treat several builder arguments as SQL fragments rather than values: the binder exists in every signature but never binds these strings.
CakePHP's query builder separates bound values from structural SQL, and in August 2026 it learned this lesson once already. CVE-2026-79752 put ensureSimpleString() on four FunctionsBuilder methods whose arguments were concatenated fragments. Window frame offsets and collation names were not on that list, and Abraxas Labs found both leftovers in the October 12, 2026 pass that followed the fix.
The window-frame flaw sits in buildOffsetSql(), which assembles the BETWEEN clause of the OVER window:
// src/Database/Expression/WindowExpression.php, before 5.4.4
protected function buildOffsetSql(
ValueBinder $binder,
ExpressionInterface|string|int|null $offset,
string $direction,
): string {
if ($offset === 0) {
return 'CURRENT ROW';
}
if ($offset instanceof ExpressionInterface) {
$offset = $offset->sql($binder);
}
return sprintf(
'%s %s',
$offset ?? 'UNBOUNDED',
$direction,
);
}
The $binder sits in the signature but only acts when the offset is already an expression object. A plain string, including one from $request->getQuery('start'), goes through sprintf() into RANGE BETWEEN ... PRECEDING inside the OVER clause.
The sibling methods rows() and groups() take ?int and cannot carry this. range() takes a string because interval literals look like '1 day', and the framework's own test asserted range("'1 day'", "'10 days'") before the fix, as the researcher documented.
The collation flaw has the same shape, with the bound value safe and the second argument raw:
// src/Database/Expression/StringExpression.php, before 5.4.4
public function sql(ValueBinder $binder): string
{
$placeholder = $binder->placeholder('c');
$binder->bind($placeholder, $this->string, 'string');
return $placeholder . ' COLLATE ' . $this->collation;
}
The string value is bound to a placeholder. The collation is concatenated after COLLATE, and IdentifierExpression::sql() splices it the same way. On Postgres the driver's identifier wrapping is a trim-and-wrap, '"' . trim($collation, '"') . '"' per the researcher, so an embedded double quote closes the identifier early and everything after it escapes the intended statement.
How is it exploited?
The public proof-of-concept labs show both mechanisms end to end:
- The attacker identifies a reachable request parameter that the application forwards into
range(),frame(), or a collation constructor. This is the whole precondition; no framework endpoint exposes the parameters by itself. - For the window-frame flaw, a crafted offset string lands raw between
RANGE BETWEENandPRECEDING. The researcher's lab demonstrates the fragment appearing in the compiled SQL unbound and unquoted. It is a compile-only demonstration on SQLite, without a demonstrated table read. - For the collation flaw, the researcher's Postgres lab executes the injection. The control value
collation=Ccompiles toCOLLATE "C". The payloadC" AS text)) || (SELECT note FROM lab_secret LIMIT 1) --is public in the write-up. Its embedded quote breaks out of the trim-and-wrap, and the trailing comment eats Cake's closing text. - The injected
SELECTreturns a witness row, and the lab reports the read valuexCAKEPHP-COLLATE-SQLI-WITNESSwith the bound placeholder still holding the plain literal. The bound first argument was never the hole; the second argument was.
Both labs are on GitHub, linked in References, and the researcher's write-ups describe them as loopback-only reproductions.
How does the attack flow?
One sentence: a request parameter reaches an expression constructor, the value is concatenated as a SQL fragment instead of being bound, and the query runs with the application's database privileges.
range(), frame() or a
collation constructor"] --> B["Value stored as
a raw string"] B --> C["ValueBinder never binds
plain strings
(missing check)"] C --> D["buildOffsetSql / sql()
concatenates the fragment
into OVER or COLLATE"] D --> E["Injected SQL runs with
the database
connection's privileges"] E --> F["Lab-proven: table read
via Postgres collation
breakout"]
What can an attacker do?
- Run SQL with the privileges of the application's database connection. Both CVE records score full confidentiality, integrity and availability impact for the compromised data.
- Read data. The collation lab on Postgres demonstrates a
SELECTthat returns a row from a table the application did not intend to expose. - Modify data. The researcher rates this as inferred rather than demonstrated: the same injection runs under the connection role, so writes are possible, but the lab proves only a read oracle.
- Fail visibly on malformed input. A payload that does not parse produces a database error on that request, which is also what a probing attempt may leave behind.
Conditions the attacker needs: an application that forwards request data into the affected parameters, network reachability of that path, and nothing else. No framework-level authentication gate applies to the parameters themselves. Whether the vulnerable endpoint is authenticated depends on the application. That is also why the researcher scores the flaws with PR:L while the CVE records score PR:N with a high attack complexity.
Why does the fix work?
Commit 18de3e4c for the window family and commit ae8c354c for the collation constructors add QueryExpression::ensureSimpleString() to every affected argument, so a non-simple string throws before any SQL is built:
--- a/src/Database/Expression/WindowExpression.php
+++ b/src/Database/Expression/WindowExpression.php
@@ public function range(
ExpressionInterface|string|int|null $start,
ExpressionInterface|string|int|null $end = 0
) {
+ if (is_string($start)) {
+ QueryExpression::ensureSimpleString('start', $start);
+ }
+ if (is_string($end)) {
+ QueryExpression::ensureSimpleString('end', $end);
+ }
+
return $this->frame(self::RANGE, $start, self::PRECEDING, $end, self::FOLLOWING);
}
--- a/src/Database/Expression/StringExpression.php
+++ b/src/Database/Expression/StringExpression.php
@@ public function setCollation(string $collation): void
{
+ QueryExpression::ensureSimpleString('collation', $collation);
$this->collation = $collation;
}
// src/Database/Expression/QueryExpression.php, 5.4.4
public static function ensureSimpleString(string $parameterName, string $value): void
{
if (preg_match('/^(?:[\'"])?[a-zA-Z0-9_\.]+(?:[\'"])?$/', $value)) {
return;
}
throw new InvalidArgumentException("Argument `{$parameterName}` must be an alphanumeric string");
}
The rule allows an optionally quoted token of letters, digits, underscores and dots, and nothing else. Non-simple strings, such as interval literals containing spaces, now fail with InvalidArgumentException. The validator is a character allowlist, not an SQL keyword filter. Strings that need structure, such as interval literals, must arrive as expression objects the caller built deliberately.
The fix is complete for these parameters on the 5.2, 5.3 and 5.4 lines. The patch series also validates FunctionExpression's name in its constructor and setName(), StringAggExpression::setAggregateOrderBy(), WindowExpression::partition() and orderBy(), and both collation setters. It is not complete for the 4.x lines. The 4.6.6 release carries only the collation backport, and its WindowExpression::range(), frame() and buildOffsetSql() still accept and concatenate raw strings, verified in the 4.6.6 source.
How do I check if I was compromised?
No indicators of compromise are published for these flaws, and no exploitation has been reported anywhere as of November 1, 2026. A failed injection attempt may leave no trace beyond a database syntax error on the affected endpoint.
The concrete audit is the code review above. The grep commands are a starting point, not a complete audit: multiline calls, aliases and indirect calls need manual review. Each hit needs a one-line answer on whether request data reaches the argument.
In database logs, look for statements from those endpoints whose COLLATE clause or window BETWEEN clause contains quotes, comments or unexpected keywords. Treat a hit as a lead to investigate rather than proof of compromise.
Timeline
- 2026-08-27: CakePHP publishes advisory GHSA-vjqc-q4mp-2rvf for CVE-2026-79752, adding
ensureSimpleString()to fourFunctionsBuildermethods that concatenated fragment arguments. - 2026-10-12: Abraxas Labs publishes both write-ups with public proof-of-concept labs, rating each 8.1 High with an authenticated-precondition vector.
- 2026-10-24: The fixes land for the window-frame family (pull request 19735) and the collation constructors (pull request 19726), crediting abraxas, SunKimsroul and Akinlabi.
- 2026-10-25: The 4.x collation backport is authored as pull request 19734.
- 2026-10-30: VulnCheck publishes CVE-2026-119418 and CVE-2026-119419, each with CVSS v4.0 9.2 and v3.1 8.1.
- 2026-11-01: CakePHP releases 5.4.4, 5.3.8 and 5.2.16 with both fixes, and 4.6.6 with the collation fix only.
FAQ
Does the default CakePHP application expose these parameters?
No. The stock cakephp/app skeleton forwards neither frame offsets nor collation names from any public form, per the researcher's verification. The exposure comes from application code that passes request data into these expression methods, so the audit target is your own controllers, services and report builders.
My code passes interval literals like '1 day' to range(). Does 5.4.4 break it?
Yes, and the change is deliberate. A string with a space fails ensureSimpleString() and throws InvalidArgumentException, so wrap interval literals in an expression object: range(new QueryExpression("'1 day'"), new QueryExpression("'10 days'")). The framework's own test suite switched to exactly this form in 5.4.4.
Why do the CVE records say 9.2 while the researcher says 8.1 High?
Both agree on the v3.1 score of 8.1 but model the precondition differently. The CVE records use PR:N with high attack complexity and an attack-requirements value, treating the application's forwarding code as the barrier. The researcher uses PR:L with low complexity, scoring the authenticated request path of a realistic victim application. Both describe the same precondition: the application must forward request data into the affected parameters.
Is CakePHP 4.x affected?
Yes, partially. The collation flaw is fixed in 4.6.6, released November 1, 2026. The window-frame flaw has no fixed release in any 4.x line: 4.6.6's WindowExpression still concatenates raw string offsets, and the 4.5 line's last release, 4.5.12, predates both fixes. Applications on 4.x that forward request data into window frames must validate the inputs themselves, as described in "How do I fix it?".
Will composer audit flag these CVEs?
Not yet. Composer's audit command reads the FriendsOfPHP advisory database, which carried neither CVE as of November 1, 2026, so the reliable check this week is the version query above. The GitHub Advisory Database does mirror both records, classified critical, as unreviewed entries GHSA-h3r5-88vp-3qm7 and GHSA-6q38-2qcr-3x34.
Is the flaw being exploited in the wild?
No source reports attacks as of November 1, 2026, and neither CVE is in CISA's Known Exploited Vulnerabilities catalog, checked against the catalog's JSON feed on that date. The public artifacts are lab reproducers that talk to loopback, not attack tooling aimed at live targets.
Find the old framework versions across your repositories with Ostorlab
Ostorlab's software composition analysis maps the dependency tree across your repositories and container images. It lists every project still on CakePHP 5.4.3 or earlier, so you can schedule the 5.4.4, 5.3.8 or 5.2.16 upgrade before a proof of concept finds your report builder.
References
- CVE-2026-119418 record, VulnCheck CNA, CVSS v4.0 9.2 and v3.1 8.1, credits to Abraxas Labs and Mark Story: https://www.cve.org/CVERecord?id=CVE-2026-119418 (checked November 1, 2026)
- CVE-2026-119419 record, VulnCheck CNA, CVSS v4.0 9.2 and v3.1 8.1: https://www.cve.org/CVERecord?id=CVE-2026-119419 (checked November 1, 2026)
- GitHub Advisory Database mirrors, classified critical, unreviewed: https://github.com/advisories/GHSA-h3r5-88vp-3qm7 and https://github.com/advisories/GHSA-6q38-2qcr-3x34 (checked November 1, 2026)
- Abraxas Labs, "CakePHP, WindowExpression range/frame SQL injection", October 12, 2026: https://abraxaslabs.tech/research/cakephp-window-sqli (checked November 1, 2026)
- Abraxas Labs, "CakePHP, COLLATE collation SQL injection", October 12, 2026: https://abraxaslabs.tech/research/cakephp-collate-sqli (checked November 1, 2026)
- Proof-of-concept labs by Abraxas Labs, public since October 12, 2026: https://github.com/abraxas/cakephp-window-sqli and https://github.com/abraxas/cakephp-collate-sqli (checked November 1, 2026)
- Window-family fix commit 18de3e4cdabba049269421bf43c2ccd1a7451565, ADmad, merged October 24, 2026, crediting abraxas, SunKimsroul and Akinlabi: https://github.com/cakephp/cakephp/commit/18de3e4cdabba049269421bf43c2ccd1a7451565 (checked November 1, 2026)
- Collation fix commit ae8c354cf825aeb7090ea4b48ce98142446f3ac9, Mark Story, merged October 24, 2026: https://github.com/cakephp/cakephp/commit/ae8c354cf825aeb7090ea4b48ce98142446f3ac9 (checked November 1, 2026)
- 4.x collation backport commit 21951faea40060dcc5cc1da3159056df66816d3c, authored October 25, 2026: https://github.com/cakephp/cakephp/commit/21951faea40060dcc5cc1da3159056df66816d3c (checked November 1, 2026)
- CakePHP 5.4.4 release notes, November 1, 2026, naming 5.2.16 and 5.3.8 as the other security-fix releases: https://github.com/cakephp/cakephp/releases/tag/5.4.4 (checked November 1, 2026)
- The 4.6.5 to 4.6.6 comparison, showing the collation files changed and no WindowExpression change: https://github.com/cakephp/cakephp/compare/4.6.5...4.6.6 (checked November 1, 2026)
- WindowExpression before the fix, at the commit the CVE record cites: https://github.com/cakephp/cakephp/blob/6f3dd91371ac0698b64024ddb21d57def9fbba91/src/Database/Expression/WindowExpression.php (checked November 1, 2026)
- The fixed validator
ensureSimpleString()at 5.4.4: https://github.com/cakephp/cakephp/blob/5.4.4/src/Database/Expression/QueryExpression.php (checked November 1, 2026) - The fixed test suite wrapping interval literals in QueryExpression, at 5.4.4: https://github.com/cakephp/cakephp/blob/5.4.4/tests/TestCase/Database/Expression/WindowExpressionTest.php (checked November 1, 2026)
- Predecessor advisory GHSA-vjqc-q4mp-2rvf, CVE-2026-79752, published August 27, 2026: https://github.com/cakephp/cakephp/security/advisories/GHSA-vjqc-q4mp-2rvf (checked November 1, 2026)
- Packagist install statistics for cakephp/cakephp, 21,425,059 total and 557,686 monthly: https://packagist.org/packages/cakephp/cakephp (checked November 1, 2026)
- Packagist install statistics for cakephp/database, 29,353,847 total and 857,825 monthly: https://packagist.org/packages/cakephp/database (checked November 1, 2026)
- FriendsOfPHP security-advisories database, which
composer auditreads, carrying neither CVE as of the check date: https://github.com/FriendsOfPHP/security-advisories (checked November 1, 2026) - CISA Known Exploited Vulnerabilities catalog JSON feed, checked for both CVEs on November 1, 2026 (not listed): https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (checked November 1, 2026)
- PHP OPcache deployment requirements when timestamp validation is disabled: https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.validate-timestamps (checked November 1, 2026)