Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-73069

CVE-2026-73069: Twenty CRM SQL Injection Vulnerability

CVE-2026-73069 is a SQL injection vulnerability in Twenty CRM allowing workspace admins to execute arbitrary PostgreSQL statements. This article covers the technical details, affected versions, impact, and mitigation.

Published:

CVE-2026-73069 Overview

Twenty is an open-source customer relationship management (CRM) platform. CVE-2026-73069 is a SQL injection vulnerability [CWE-89] affecting Twenty versions prior to 2.15.0. A workspace administrator holding the DATA_MODEL permission can supply a malicious settings.asExpression value for the system TS_VECTOR field searchVector. The unsanitized input is concatenated into a PostgreSQL GENERATED ALWAYS AS (...) clause and executed as the application database user. The issue is fixed in version 2.15.0.

Critical Impact

An authenticated workspace administrator can execute arbitrary PostgreSQL statements as the application database user, leading to full compromise of workspace data and adjacent tenants.

Affected Products

  • Twenty CRM versions prior to 2.15.0
  • Self-hosted Twenty deployments exposing the metadata REST and GraphQL APIs
  • Multi-tenant Twenty workspaces sharing a PostgreSQL application user

Discovery Timeline

  • 2026-08-11 - CVE-2026-73069 published to the National Vulnerability Database (NVD)
  • 2026-08-11 - Last updated in NVD database

Technical Details for CVE-2026-73069

Vulnerability Analysis

The flaw resides in Twenty's metadata management layer. Two entry points accept user-controlled input for the searchVector field: the REST endpoint PATCH /rest/metadata/fields/:id and the updateOneField GraphQL mutation. Both paths forward the settings.asExpression value to buildSqlColumnDefinition in packages/twenty-server/src/engine/twenty-orm/workspace-schema-manager/utils/build-sql-column-definition.util.ts. That utility inlines the value directly into a PostgreSQL GENERATED ALWAYS AS (...) column definition without escaping. When the schema migration executes, PostgreSQL parses the injected statements under the application's database role.

Root Cause

The root cause is missing input validation on the asExpression property of TS_VECTOR field metadata. buildSqlColumnDefinition trusted the caller and used string concatenation to assemble DDL. No allowlist governed which functions or operators could appear in a generated column expression, and no escaping was applied.

Attack Vector

An authenticated user with the DATA_MODEL workspace permission sends a crafted PATCH request or GraphQL mutation targeting the searchVector field. The attacker embeds arbitrary SQL inside settings.asExpression. When Twenty applies the schema change, PostgreSQL evaluates the injected payload as the application database user, granting the attacker read and write access to any object owned or reachable by that role.

typescript
// Security patch: validate-ts-vector-flat-field-metadata.util.ts
 import { msg } from '@lingui/core/macro';
+import { isNonEmptyString } from '@sniptt/guards';
 import { type FieldMetadataType } from 'twenty-shared/types';

 import { FieldMetadataExceptionCode } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
 import { type FlatFieldMetadataTypeValidationArgs } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-type-validator.type';
 import { type FlatFieldMetadataValidationError } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-validation-error.type';
+import { isSafeTsVectorExpression } from 'src/engine/workspace-manager/workspace-migration/utils/remove-sql-injection.util';

 export const validateTsVectorFlatFieldMetadata = ({
   flatEntityToValidate,
typescript
// Security patch: build-sql-column-definition.util.ts
 import { isDefined } from 'twenty-shared/utils';

 import { type WorkspaceSchemaColumnDefinition } from 'src/engine/twenty-orm/workspace-schema-manager/types/workspace-schema-column-definition.type';
-import { escapeIdentifier } from 'src/engine/workspace-manager/workspace-migration/utils/remove-sql-injection.util';
+import {
+  assertSafeTsVectorExpression,
+  escapeIdentifier,
+} from 'src/engine/workspace-manager/workspace-migration/utils/remove-sql-injection.util';

 const ALLOWED_GENERATED_TYPES = new Set(['STORED', 'VIRTUAL']);

Source: Twenty commit 0b8368c

Detection Methods for CVE-2026-73069

Indicators of Compromise

  • PATCH requests to /rest/metadata/fields/:id whose JSON body includes settings.asExpression with SQL keywords such as SELECT, PG_SLEEP, COPY, or pg_read_server_files.
  • GraphQL updateOneField mutations targeting a TS_VECTOR field where the expression contains function calls beyond to_tsvector and coalesce.
  • PostgreSQL server logs showing unexpected DDL or DML statements originating from the Twenty application role during schema migrations.

Detection Strategies

  • Audit HTTP and GraphQL request logs for modifications to searchVector fields and compare payloads against the allowlist enforced in Twenty 2.15.0.
  • Enable PostgreSQL log_statement = 'ddl' and alert on CREATE/ALTER TABLE ... GENERATED ALWAYS AS statements that contain unusual function calls.
  • Correlate workspace administrator actions with database role activity to identify privilege abuse.

Monitoring Recommendations

  • Track membership changes for the DATA_MODEL workspace permission and alert on new grants.
  • Forward Twenty application logs and PostgreSQL audit logs to a centralized analytics platform for retention and query.
  • Establish a baseline for legitimate searchVector expressions so deviations trigger review.

How to Mitigate CVE-2026-73069

Immediate Actions Required

  • Upgrade all Twenty deployments to version 2.15.0 or later without delay.
  • Review recent changes to TS_VECTOR fields and revert any suspicious asExpression values.
  • Rotate PostgreSQL credentials used by the Twenty application if abuse is suspected.
  • Restrict the DATA_MODEL workspace permission to a minimum set of trusted administrators.

Patch Information

The fix is delivered in Twenty 2.15.0. The patch introduces assertSafeTsVectorExpression and isSafeTsVectorExpression helpers in remove-sql-injection.util.ts and enforces validation before the expression reaches buildSqlColumnDefinition. Details are available in the GitHub Security Advisory GHSA-mm7j-q9q3-qqwj and pull request #21947.

Workarounds

  • Temporarily revoke the DATA_MODEL permission from all workspace administrators until upgrading is possible.
  • Place the Twenty metadata API behind a reverse proxy that blocks PATCH and GraphQL requests touching searchVector fields.
  • Run the Twenty PostgreSQL user with least privilege so injected statements cannot reach data outside the workspace schema.
bash
# Upgrade Twenty to the patched release
docker pull twentycrm/twenty:2.15.0
docker compose down
docker compose up -d

# Verify the running version
curl -s http://localhost:3000/healthz | jq '.version'

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.