Jun 19, 2026·6 min read·5 visits
TypeORM's UpdateQueryBuilder and SoftDeleteQueryBuilder allowed SQL injection via the orderBy direction parameter when targeting MySQL or MariaDB. An initial fix was bypassed via string-based API signatures, but the vulnerability is fully mitigated in the latest releases.
A critical SQL injection vulnerability was discovered in TypeORM's UpdateQueryBuilder and SoftDeleteQueryBuilder when targeting MySQL and MariaDB backends. The flaw allows unauthenticated remote attackers to execute arbitrary SQL commands because input validation was bypassed on certain method signatures. The initial patch was incomplete, leaving a bypass open, which was resolved in the final security update.
TypeORM is an Object-Relational Mapper (ORM) widely integrated into enterprise Node.js ecosystems to model database entities and execute queries safely. Within TypeORM, the UpdateQueryBuilder and SoftDeleteQueryBuilder components translate programmatic JavaScript or TypeScript API calls into compiled database updates. These query builders expose critical methods, including orderBy and addOrderBy, designed to let developers specify the order in which rows should be processed.
In standard relational databases, sorting rows during write-based events is highly restricted. However, the MySQL and MariaDB engines explicitly support ORDER BY syntax inside standard UPDATE and DELETE commands. This capability means that TypeORM must generate target SQL statements containing ordering instructions when running on top of MySQL or MariaDB backends.
This architectural detail exposes a significant vulnerability surface area if user input is allowed to reach the sorting direction parameter. Without rigorous validation, arbitrary characters injected into this parameter pass through the query compiler directly to the database. This failure leads to unauthenticated SQL injection, exposing relational schemas and application data to exploitation.
The root cause of this vulnerability lies in the inconsistent validation architecture across TypeORM query builder subclasses. While the SelectQueryBuilder implemented early syntactic validations, the builders responsible for database writes did not possess matching safeguards. Specifically, UpdateQueryBuilder and SoftDeleteQueryBuilder permitted raw string parameters to pass directly to compilation routines.
When a developer invokes the orderBy() method with string arguments, the query builder stores these options inside the query expression map. During compilation, the builder invokes the internal method createOrderByExpression(). This method reads from the expression map and directly concatenates the raw value string into the final query string template.
In an initial attempt to mitigate this issue (implemented in PR #12217), developers introduced the centralized validation helper validateOrderByCondition(). However, this fix contained a fatal design flaw: it only checked the input parameters if the root argument was structured as an object. When a developer invoked the string signature—such as orderBy(columnName, orderDirection)—the validation logic was entirely bypassed, writing the raw direction input straight to the expression map.
To trace the flaw, we can analyze the control flow and structural design of the vulnerable paths. The diagram below illustrates how string-based arguments bypass the validation routine introduced in the partial patch.
The vulnerable TypeScript code within UpdateQueryBuilder.ts before the final patch illustrates this critical architectural blind spot:
// Vulnerable code in UpdateQueryBuilder.ts
orderBy(
sort?: string | OrderByCondition,
order: "ASC" | "DESC" = "ASC",
nulls?: "NULLS FIRST" | "NULLS LAST",
): this {
if (sort) {
if (typeof sort === "object") {
// Centralized validation helper is ONLY triggered for object structures
this.validateOrderByCondition(sort)
this.expressionMap.orderBys = sort
} else {
// String signature bypasses validation entirely and writes raw values
if (nulls) {
this.expressionMap.orderBys = {
[sort as string]: { order, nulls },
}
} else {
this.expressionMap.orderBys = { [sort as string]: order }
}
}
}
return this;
}During query compilation, the createOrderByExpression method simply performed direct string concatenation, as shown below:
protected createOrderByExpression() {
const orderBys = this.expressionMap.orderBys
if (Object.keys(orderBys).length > 0)
return (
" ORDER BY " +
Object.keys(orderBys)
.map((columnName) => {
if (typeof orderBys[columnName] === "string") {
return (
this.replacePropertyNames(columnName) +
" " +
orderBys[columnName] // Direct unvalidated string concatenation
)
}
// Object handling logic omitted...
})
)
}An attacker exploits this vulnerability by injecting SQL structures into the sorting direction parameter. In MySQL and MariaDB, the ORDER BY clause accepts lists of expressions separated by commas. Consequently, injecting a comma allows the attacker to append a secondary, completely arbitrary SQL statement to the database processor.
To trigger this behavior, an attacker submits a payload structured to introduce a time-based delay, such as ASC, (SELECT SLEEP(5)). If the underlying query builder compiles this string, the resulting database command will call the SLEEP function sequentially for each record matching the update filter. This produces a measurable delay in the HTTP response time.
By systematically altering the parameters of the subquery, the attacker can verify true/false conditions. This allows character-by-character extraction of tables, schemas, and system metadata. Furthermore, because this injection occurs inside an UPDATE or SOFT DELETE statement, it can alter database state as part of the primary transaction.
The impact of this SQL injection vulnerability is classified as high because it allows malicious actors to execute arbitrary commands within the database engine. Attackers can bypass built-in access controls, modify critical operational tables, or purge complete datasets. In configurations where database services run with elevated operational privileges, the risk increases dramatically.
If the database configuration permits reading or writing local system files, attackers can leverage SQL injection to write arbitrary web shells to the underlying host. This turns database manipulation into remote code execution. Additionally, attackers can retrieve system variables, environment credentials, and active configuration settings, which compromises connected upstream services.
The CVSS v3.1 vector evaluates to CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N. While the vulnerability is restricted to MySQL and MariaDB installations, these engines power a large percentage of web projects, expanding the threat landscape for unpatched Node.js backends.
Remediation requires upgrading the typeorm dependency to a version containing the definitive fix merged in commit 1b66c44d0410bdc56a0dcefb46be41867ec0fffc. This patch secures the library by executing centralized validation across all signatures and adding compilation-time schema checks. This defense-in-depth ensures that malformed payloads fail to compile even if they bypass setter checks.
If upgrading the package immediately is not feasible, developers must implement strict validation layers at the application boundary. Ensure that any variable mapped to the sort direction parameter is restricted strictly to an explicit allowlist consisting of "ASC" or "DESC". This stops the malicious input before it can be processed by TypeORM.
Furthermore, implement database privileges following the principle of least privilege. Limit database users from accessing administrative functions such as administrative sleep APIs or shell-execution privileges. Deploying Web Application Firewalls (WAF) configured with patterns to block semicolon usage, nested subqueries, and commas inside order-by fields provides an additional layer of security.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
typeorm TypeORM | < 0.3.20 | 0.3.20 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-89 |
| Attack Vector | Network |
| CVSS v3.1 Score | 8.1 (High) |
| Affected Dialects | MySQL, MariaDB |
| Vulnerability Class | SQL Injection |
| Exploit Status | Proof-of-Concept Available |
| Patch Status | Fully Patched (Commit 1b66c44) |
The software constructs an SQL command using externally-influenced input, but it does not neutralize or incorrectly neutralizes special elements that can modify the intended SQL command.
Parse Server prior to versions 8.6.80 and 9.9.1-alpha.6 contains an authorization bypass vulnerability in its relation query handling. A database query utilizing the `$relatedTo` operator can read the membership details of a Relation field even when that field is hidden via `protectedFields` or restricted by object-level Access Control Lists (ACLs).
Hugo versions v0.123.0 through v0.163.0 are vulnerable to a directory confinement bypass. A regression in the virtual filesystem layer causes symbolic links to be followed during template execution, allowing templates to read arbitrary host files.
A critical missing authorization vulnerability exists in the API Pages Controller of Alchemy CMS. An unauthenticated remote attacker can exploit the 'nested' action to retrieve the entire nested page tree. Furthermore, by appending the query parameter '?elements=true', the attacker can extract sensitive content from draft, unpublished, and restricted pages, bypassing all access controls.
Nokogiri is a popular Ruby gem used for parsing XML and HTML documents. A Use-After-Free (UAF) vulnerability exists in its CRuby implementation during XInclude processing. When an application traverses an XML document and exposes nodes to Ruby before calling `do_xinclude`, the underlying C library `libxml2` can free these structures in-place. This leaves active Ruby objects holding pointers to freed memory, leading to potential segmentation faults, memory corruption, or information disclosure.
A use-after-free (UAF) vulnerability exists in the CRuby native extension of the Nokogiri gem when updating XML attribute values. If child nodes of an XML attribute are wrapped by Ruby objects prior to setting the attribute's value, the underlying C memory structures are freed while the Ruby wrapper retains a dangling pointer. This results in memory corruption, invalid pointer dereferences, and application crashes during execution or garbage collection.
A client-side Stored Cross-Site Scripting (XSS) vulnerability exists in the JupyterLab Extension Manager. This vulnerability allows an attacker to register a malicious package on the Python Package Index (PyPI) with a crafted metadata homepage URL using the 'javascript:' pseudo-protocol. When a JupyterLab user opens the Extension Manager and clicks the extension name, the browser executes arbitrary JavaScript code within the context of the JupyterLab origin. This can lead to the theft of active workspace documents, credentials, and API tokens. The issue affects all versions of JupyterLab prior to version 4.5.9.