From 503c689f37de6dcd7270207d1ecf4bb1efa68b43 Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Thu, 11 Jun 2026 16:41:22 +0200 Subject: [PATCH] security: upgrade typeorm to 0.3.26 (CVE-2025-60542) (#21456) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Context Retry of the typeorm upgrade that was pulled out of #21448 after CI showed "intermittently lossy metadata sync". **The investigation exonerated typeorm**: the postcard/seed failures were a pre-existing bug in `@ptc-org/nestjs-query-typeorm`'s batched relation paging (global LIMIT across parents) that scan-order luck had been hiding — reproduced byte-for-byte on typeorm **0.3.20** against a frozen repro DB. That bug is fixed in #21455, which this PR is stacked on (base branch = `charles/fix-nestjs-query-batch-relation-paging`; will retarget to main when it merges). ## Changes - typeorm `0.3.20` → `0.3.26` ([CVE-2025-60542](https://github.com/advisories/GHSA-q2pj-6v73-8rgj), MEDIUM). The CVE lives in TypeORM's MySQL path (`sqlstring`/`stringifyObjects`); Postgres-only Twenty never exercises it — this is scanner hygiene + staying current. - The local yarn patch (`PickKeysByType` + `DeleteResult.generatedMaps`) applies **verbatim** to 0.3.26 (verified against the pristine tarball) — renamed to `typeorm+0.3.26.patch`. - `WorkspaceRepository.query` restricted override adapted to the generic `query()` base signature introduced in 0.3.24 (one-line change, still throws `RAW_SQL_NOT_ALLOWED`). - 0.3.26 ships `uuid ^11` natively → the scoped `typeorm/uuid` resolution from #21441 and its `//resolutions` comment clause (including the now-disproven "lossy sync" warning) are removed. ## Why we're confident this time The original failure signature was fully understood, not just retried: - On a frozen failing DB, **all fieldMetadata rows + workspace columns were intact** — only the batched metadata API read was truncated (`LIMIT 501` over 558 rows, no ORDER BY). - Same DB, typeorm 0.3.20: identical truncation, identical SQL → not a typeorm regression. - With #21455 applied: postcard install/uninstall stress loop **12/12 green on typeorm 0.3.26** (previously failed within 1–2 iterations), API returns 558/558 fields. ## Verification - `npx nx typecheck twenty-server` — clean - Full `twenty-server` unit suite — green (5651 passed) - `group-by-resolver` integration suite — 19/19 on a fresh 0.3.26-seeded test DB - Postcard app-sync stress loop — 12/12 on this exact stack - Lockfile: typeorm 0.3.26 + new `sql-highlight` dep, `esbuild`/uuid entries untouched --- package.json | 3 +- packages/twenty-server/package.json | 2 +- ...eorm+0.3.20.patch => typeorm+0.3.26.patch} | 0 .../repository/workspace.repository.ts | 2 +- yarn.lock | 122 ++++++++---------- 5 files changed, 60 insertions(+), 69 deletions(-) rename packages/twenty-server/patches/{typeorm+0.3.20.patch => typeorm+0.3.26.patch} (100%) diff --git a/package.json b/package.json index 4d92dddd78..72c444bc12 100644 --- a/package.json +++ b/package.json @@ -44,11 +44,10 @@ "sockjs/uuid": "11.1.1", "@cypress/request/uuid": "11.1.1", "@ptc-org/nestjs-query-typeorm/uuid": "11.1.1", - "typeorm/uuid": "11.1.1", "googleapis-common/uuid": "11.1.1", "@cyntler/react-doc-viewer/ajv": "8.20.0" }, - "//resolutions": "Each entry is load-bearing: it forces a version OUTSIDE some parent's declared range where no fixed upstream release exists; remove each once its blocker ships. graphql 16.8.1 -> singleton pin held below msw's ^16.12.0 dep and @nestjs/graphql's ^16.11.0 peer; drop after a validated repo-wide bump to latest 16.x; graphql-redis-subscriptions/ioredis 5.10.1 -> TS type-identity dedup: twenty-server passes its ioredis client into RedisPubSub, so this must equal the exact ioredis version pinned by twenty-server and bullmq (bump in lockstep); @types/qs 6.9.16 -> holdback below the 6.9.17 ParsedQs typing break (node-saml wants ^6.9.18); @opentelemetry/api 1.9.1 -> singleton guard for the NoopMeterProvider bug (#20231): ai 6.0.x pins 1.9.0 exact vs @sentry/node ^1.9.1, drop when workspace ai >=6.0.178 AND @scalar/agent-chat moves off ai 6.0.33; chokidar ^3 -> NestJS CLI watch needs fsevents on macOS, removed in chokidar 4/5 (#20316); tmp ^0.2.7 -> CVE, zapier-platform-cli 19 (latest) pins 0.2.5 and inquirer 7/8's external-editor wants ^0.0.33; make-fetch-happen ^15 + @electron/{rebuild,node-gyp}/tar ^7.5.16 -> tar CVE eviction for the @electron/rebuild 3.x toolchain (rebuild 3.x pins tar ^6, its node-gyp fork pins tar ^6.2.1 + mfh ^10), drop when electron-forge declares @electron/rebuild >=4; @angular-devkit/core 19.2.24 -> picomatch CVE, blocked on @nestjs/cli >11.0.23 fixing the dist/src output regression (repo held at 11.0.16); yeoman-environment 6.0.1 -> CVE, zapier-platform-cli 19 (latest) pins 4.4.3; webpack-dev-server 5.2.4 -> CVE, @electron-forge/plugin-webpack (incl. 8.x alphas) still declares ^4; express/qs + @cypress/request/qs 6.15.2 -> qs CVE for old express 4.22.0/4.22.1 pinned by @mintlify/previewing and verdaccio (verdaccio also pins @cypress/request 3.0.10; all other qs parents resolve safe naturally); next/postcss 8.5.15 -> postcss CVE, every stable next pins 8.4.31 exact (fix only in 16.3.0 canaries; @react-email/ui also pins next 16.2.6); /uuid 11.1.1 -> uuid CVE for parents pinning uuid <11 with no fixed release (sockjs dormant since 2021; @cypress/request 3.0.10 via verdaccio; @ptc-org/nestjs-query-typeorm at latest; typeorm 0.3.20 patch:dep -- droppable by bumping to 0.3.26+ and re-rolling the patch, but 0.3.26 made workspace metadata sync intermittently lossy (see #21448), debug before retrying; googleapis 105 -> common 8 drops uuid but needs the googleapis >=152 migration). Preserves the intentional uuid 13.x; @cyntler/react-doc-viewer/ajv 8.20.0 -> CVE, upstream (latest 1.17.1) pins ajv ^7 but never imports it, forcing v8 is safe", + "//resolutions": "Each entry is load-bearing: it forces a version OUTSIDE some parent's declared range where no fixed upstream release exists; remove each once its blocker ships. graphql 16.8.1 -> singleton pin held below msw's ^16.12.0 dep and @nestjs/graphql's ^16.11.0 peer; drop after a validated repo-wide bump to latest 16.x; graphql-redis-subscriptions/ioredis 5.10.1 -> TS type-identity dedup: twenty-server passes its ioredis client into RedisPubSub, so this must equal the exact ioredis version pinned by twenty-server and bullmq (bump in lockstep); @types/qs 6.9.16 -> holdback below the 6.9.17 ParsedQs typing break (node-saml wants ^6.9.18); @opentelemetry/api 1.9.1 -> singleton guard for the NoopMeterProvider bug (#20231): ai 6.0.x pins 1.9.0 exact vs @sentry/node ^1.9.1, drop when workspace ai >=6.0.178 AND @scalar/agent-chat moves off ai 6.0.33; chokidar ^3 -> NestJS CLI watch needs fsevents on macOS, removed in chokidar 4/5 (#20316); tmp ^0.2.7 -> CVE, zapier-platform-cli 19 (latest) pins 0.2.5 and inquirer 7/8's external-editor wants ^0.0.33; make-fetch-happen ^15 + @electron/{rebuild,node-gyp}/tar ^7.5.16 -> tar CVE eviction for the @electron/rebuild 3.x toolchain (rebuild 3.x pins tar ^6, its node-gyp fork pins tar ^6.2.1 + mfh ^10), drop when electron-forge declares @electron/rebuild >=4; @angular-devkit/core 19.2.24 -> picomatch CVE, blocked on @nestjs/cli >11.0.23 fixing the dist/src output regression (repo held at 11.0.16); yeoman-environment 6.0.1 -> CVE, zapier-platform-cli 19 (latest) pins 4.4.3; webpack-dev-server 5.2.4 -> CVE, @electron-forge/plugin-webpack (incl. 8.x alphas) still declares ^4; express/qs + @cypress/request/qs 6.15.2 -> qs CVE for old express 4.22.0/4.22.1 pinned by @mintlify/previewing and verdaccio (verdaccio also pins @cypress/request 3.0.10; all other qs parents resolve safe naturally); next/postcss 8.5.15 -> postcss CVE, every stable next pins 8.4.31 exact (fix only in 16.3.0 canaries; @react-email/ui also pins next 16.2.6); /uuid 11.1.1 -> uuid CVE for parents pinning uuid <11 with no fixed release (sockjs dormant since 2021; @cypress/request 3.0.10 via verdaccio; @ptc-org/nestjs-query-typeorm at latest; googleapis 105 -> common 8 drops uuid but needs the googleapis >=152 migration). Preserves the intentional uuid 13.x; @cyntler/react-doc-viewer/ajv 8.20.0 -> CVE, upstream (latest 1.17.1) pins ajv ^7 but never imports it, forcing v8 is safe", "version": "0.2.1", "nx": {}, "scripts": { diff --git a/packages/twenty-server/package.json b/packages/twenty-server/package.json index 58e2a26c75..c36dc07681 100644 --- a/packages/twenty-server/package.json +++ b/packages/twenty-server/package.json @@ -166,7 +166,7 @@ "tsdav": "^2.2.0", "tslib": "2.8.1", "type-fest": "4.10.1", - "typeorm": "patch:typeorm@0.3.20#./patches/typeorm+0.3.20.patch", + "typeorm": "patch:typeorm@0.3.26#./patches/typeorm+0.3.26.patch", "unzipper": "^0.12.3", "uuid": "^11.1.1", "zod": "^4.1.11" diff --git a/packages/twenty-server/patches/typeorm+0.3.20.patch b/packages/twenty-server/patches/typeorm+0.3.26.patch similarity index 100% rename from packages/twenty-server/patches/typeorm+0.3.20.patch rename to packages/twenty-server/patches/typeorm+0.3.26.patch diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace.repository.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace.repository.ts index e2017c287e..b6295f5d63 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace.repository.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace.repository.ts @@ -918,7 +918,7 @@ export class WorkspaceRepository< /** * DEPRECATED AND RESTRICTED METHODS */ - override async query(): Promise { + override async query(): Promise { throw new PermissionsException( 'Method not allowed.', PermissionsExceptionCode.RAW_SQL_NOT_ALLOWED, diff --git a/yarn.lock b/yarn.lock index 4f1784228c..ef3a76a8cd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -31760,7 +31760,7 @@ __metadata: languageName: node linkType: hard -"dayjs@npm:^1.10.0, dayjs@npm:^1.11.9": +"dayjs@npm:^1.10.0, dayjs@npm:^1.11.13": version: 1.11.21 resolution: "dayjs@npm:1.11.21" checksum: 10c0/bd97dfdc4bfea3c66268635690313828b386faa040fbc1f829ff42a2bd748b72c9d9b3c8f9616ce9e61fcb78923f1461a462c969c54b1084458ae1b715898fb0 @@ -32592,7 +32592,7 @@ __metadata: languageName: node linkType: hard -"dotenv@npm:^16.0.3, dotenv@npm:^16.3.0, dotenv@npm:^16.4.5, dotenv@npm:^16.5.0": +"dotenv@npm:^16.3.0, dotenv@npm:^16.4.5, dotenv@npm:^16.4.7, dotenv@npm:^16.5.0": version: 16.6.1 resolution: "dotenv@npm:16.6.1" checksum: 10c0/15ce56608326ea0d1d9414a5c8ee6dcf0fffc79d2c16422b4ac2268e7e2d76ff5a572d37ffe747c377de12005f14b3cc22361e79fc7f1061cce81f77d2c973dc @@ -36197,7 +36197,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.0.0, glob@npm:^10.3.10, glob@npm:^10.3.7": +"glob@npm:^10.0.0, glob@npm:^10.3.10, glob@npm:^10.3.7, glob@npm:^10.4.5": version: 10.5.0 resolution: "glob@npm:10.5.0" dependencies: @@ -43879,15 +43879,6 @@ __metadata: languageName: node linkType: hard -"mkdirp@npm:^2.1.3": - version: 2.1.6 - resolution: "mkdirp@npm:2.1.6" - bin: - mkdirp: dist/cjs/src/bin.js - checksum: 10c0/96f551c651dd8f5f9435d53df1a7b9bfc553be769ee6da5192c37c1f303a376ef1c6996f96913d4a8d357060451d4526a346031d1919f92c58806a5fa3cd8dfe - languageName: node - linkType: hard - "mlly@npm:^1.7.4": version: 1.8.0 resolution: "mlly@npm:1.8.0" @@ -49578,7 +49569,7 @@ __metadata: languageName: node linkType: hard -"reflect-metadata@npm:0.2.2, reflect-metadata@npm:^0.2.1, reflect-metadata@npm:^0.2.2": +"reflect-metadata@npm:0.2.2, reflect-metadata@npm:^0.2.2": version: 0.2.2 resolution: "reflect-metadata@npm:0.2.2" checksum: 10c0/1cd93a15ea291e420204955544637c264c216e7aac527470e393d54b4bb075f10a17e60d8168ec96600c7e0b9fcc0cb0bb6e91c3fbf5b0d8c9056f04e6ac1ec2 @@ -52500,6 +52491,13 @@ __metadata: languageName: node linkType: hard +"sql-highlight@npm:^6.0.0": + version: 6.1.0 + resolution: "sql-highlight@npm:6.1.0" + checksum: 10c0/9614f4608bfde8ea7bf9b2fe9233dcc99a619c91cbc3f5cd85a6fb5ad4b2177f4ac8ca4a0191f4243ff8aea3b6f2a1229efc88635298269e0049b2ac08bde263 + languageName: node + linkType: hard + "sshpk@npm:^1.18.0": version: 1.18.0 resolution: "sshpk@npm:1.18.0" @@ -55245,7 +55243,7 @@ __metadata: twenty-emails: "workspace:*" twenty-shared: "workspace:*" type-fest: "npm:4.10.1" - typeorm: "patch:typeorm@0.3.20#./patches/typeorm+0.3.20.patch" + typeorm: "patch:typeorm@0.3.26#./patches/typeorm+0.3.26.patch" unzipper: "npm:^0.12.3" uuid: "npm:^11.1.1" zod: "npm:^4.1.11" @@ -55714,43 +55712,42 @@ __metadata: languageName: node linkType: hard -"typeorm@npm:0.3.20": - version: 0.3.20 - resolution: "typeorm@npm:0.3.20" +"typeorm@npm:0.3.26": + version: 0.3.26 + resolution: "typeorm@npm:0.3.26" dependencies: "@sqltools/formatter": "npm:^1.2.5" + ansis: "npm:^3.17.0" app-root-path: "npm:^3.1.0" buffer: "npm:^6.0.3" - chalk: "npm:^4.1.2" - cli-highlight: "npm:^2.1.11" - dayjs: "npm:^1.11.9" - debug: "npm:^4.3.4" - dotenv: "npm:^16.0.3" - glob: "npm:^10.3.10" - mkdirp: "npm:^2.1.3" - reflect-metadata: "npm:^0.2.1" + dayjs: "npm:^1.11.13" + debug: "npm:^4.4.0" + dedent: "npm:^1.6.0" + dotenv: "npm:^16.4.7" + glob: "npm:^10.4.5" sha.js: "npm:^2.4.11" - tslib: "npm:^2.5.0" - uuid: "npm:^9.0.0" - yargs: "npm:^17.6.2" + sql-highlight: "npm:^6.0.0" + tslib: "npm:^2.8.1" + uuid: "npm:^11.1.0" + yargs: "npm:^17.7.2" peerDependencies: - "@google-cloud/spanner": ^5.18.0 - "@sap/hana-client": ^2.12.25 - better-sqlite3: ^7.1.2 || ^8.0.0 || ^9.0.0 - hdb-pool: ^0.1.6 + "@google-cloud/spanner": ^5.18.0 || ^6.0.0 || ^7.0.0 + "@sap/hana-client": ^2.14.22 + better-sqlite3: ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0 ioredis: ^5.0.4 - mongodb: ^5.8.0 - mssql: ^9.1.1 || ^10.0.1 + mongodb: ^5.8.0 || ^6.0.0 + mssql: ^9.1.1 || ^10.0.1 || ^11.0.1 mysql2: ^2.2.5 || ^3.0.1 oracledb: ^6.3.0 pg: ^8.5.1 pg-native: ^3.0.0 pg-query-stream: ^4.0.0 - redis: ^3.1.1 || ^4.0.0 + redis: ^3.1.1 || ^4.0.0 || ^5.0.14 + reflect-metadata: ^0.1.14 || ^0.2.0 sql.js: ^1.4.0 sqlite3: ^5.0.3 ts-node: ^10.7.0 - typeorm-aurora-data-api-driver: ^2.0.0 + typeorm-aurora-data-api-driver: ^2.0.0 || ^3.0.0 peerDependenciesMeta: "@google-cloud/spanner": optional: true @@ -55758,8 +55755,6 @@ __metadata: optional: true better-sqlite3: optional: true - hdb-pool: - optional: true ioredis: optional: true mongodb: @@ -55790,47 +55785,46 @@ __metadata: typeorm: cli.js typeorm-ts-node-commonjs: cli-ts-node-commonjs.js typeorm-ts-node-esm: cli-ts-node-esm.js - checksum: 10c0/7e4be724641beef86ae36289c87b6e66bfaf19a4313f089926d36d2d6f0d67f9314d942711c9d83ab8a174b8622148c2f7e83e6c1448d638ee3ab24469257814 + checksum: 10c0/a09ed0609a5799718bc837450b84593f6a64e6698e0662162eee2f7f12591ad9cb6f9173b42ae1d16f784d1ea09712c6e886481f4a3bdc7f851402cfde54e65a languageName: node linkType: hard -"typeorm@patch:typeorm@0.3.20#./patches/typeorm+0.3.20.patch::locator=twenty-server%40workspace%3Apackages%2Ftwenty-server": - version: 0.3.20 - resolution: "typeorm@patch:typeorm@npm%3A0.3.20#./patches/typeorm+0.3.20.patch::version=0.3.20&hash=e61204&locator=twenty-server%40workspace%3Apackages%2Ftwenty-server" +"typeorm@patch:typeorm@0.3.26#./patches/typeorm+0.3.26.patch::locator=twenty-server%40workspace%3Apackages%2Ftwenty-server": + version: 0.3.26 + resolution: "typeorm@patch:typeorm@npm%3A0.3.26#./patches/typeorm+0.3.26.patch::version=0.3.26&hash=e61204&locator=twenty-server%40workspace%3Apackages%2Ftwenty-server" dependencies: "@sqltools/formatter": "npm:^1.2.5" + ansis: "npm:^3.17.0" app-root-path: "npm:^3.1.0" buffer: "npm:^6.0.3" - chalk: "npm:^4.1.2" - cli-highlight: "npm:^2.1.11" - dayjs: "npm:^1.11.9" - debug: "npm:^4.3.4" - dotenv: "npm:^16.0.3" - glob: "npm:^10.3.10" - mkdirp: "npm:^2.1.3" - reflect-metadata: "npm:^0.2.1" + dayjs: "npm:^1.11.13" + debug: "npm:^4.4.0" + dedent: "npm:^1.6.0" + dotenv: "npm:^16.4.7" + glob: "npm:^10.4.5" sha.js: "npm:^2.4.11" - tslib: "npm:^2.5.0" - uuid: "npm:^9.0.0" - yargs: "npm:^17.6.2" + sql-highlight: "npm:^6.0.0" + tslib: "npm:^2.8.1" + uuid: "npm:^11.1.0" + yargs: "npm:^17.7.2" peerDependencies: - "@google-cloud/spanner": ^5.18.0 - "@sap/hana-client": ^2.12.25 - better-sqlite3: ^7.1.2 || ^8.0.0 || ^9.0.0 - hdb-pool: ^0.1.6 + "@google-cloud/spanner": ^5.18.0 || ^6.0.0 || ^7.0.0 + "@sap/hana-client": ^2.14.22 + better-sqlite3: ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0 ioredis: ^5.0.4 - mongodb: ^5.8.0 - mssql: ^9.1.1 || ^10.0.1 + mongodb: ^5.8.0 || ^6.0.0 + mssql: ^9.1.1 || ^10.0.1 || ^11.0.1 mysql2: ^2.2.5 || ^3.0.1 oracledb: ^6.3.0 pg: ^8.5.1 pg-native: ^3.0.0 pg-query-stream: ^4.0.0 - redis: ^3.1.1 || ^4.0.0 + redis: ^3.1.1 || ^4.0.0 || ^5.0.14 + reflect-metadata: ^0.1.14 || ^0.2.0 sql.js: ^1.4.0 sqlite3: ^5.0.3 ts-node: ^10.7.0 - typeorm-aurora-data-api-driver: ^2.0.0 + typeorm-aurora-data-api-driver: ^2.0.0 || ^3.0.0 peerDependenciesMeta: "@google-cloud/spanner": optional: true @@ -55838,8 +55832,6 @@ __metadata: optional: true better-sqlite3: optional: true - hdb-pool: - optional: true ioredis: optional: true mongodb: @@ -55870,7 +55862,7 @@ __metadata: typeorm: cli.js typeorm-ts-node-commonjs: cli-ts-node-commonjs.js typeorm-ts-node-esm: cli-ts-node-esm.js - checksum: 10c0/2593c3ddf6b243c6be13e3d9deebad1e9ecfcbdda8bb518b7fb633077e2e20b99c1873cdc0848df8ba44c9bfb22ecfe345937aa82702a9c857ecd73b32de8373 + checksum: 10c0/66268d90c1019c0b1c39971985d0f3f673f0236ce9a21835c7de16d5db54e3dcee39cbfad1985b0e0feb603b4f440fb25c6c6222c55a17452ac1b746d3a9e0f5 languageName: node linkType: hard @@ -58568,7 +58560,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:17.7.2, yargs@npm:^17.0.0, yargs@npm:^17.0.1, yargs@npm:^17.3.1, yargs@npm:^17.5.1, yargs@npm:^17.6.2, yargs@npm:^17.7.2": +"yargs@npm:17.7.2, yargs@npm:^17.0.0, yargs@npm:^17.0.1, yargs@npm:^17.3.1, yargs@npm:^17.5.1, yargs@npm:^17.7.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: