CVE-2026-85055 Overview
Twenty is an open-source customer relationship management (CRM) platform. Versions prior to 2.22.0 enforce field-level read permissions only on selected output fields, not on GraphQL or REST filter predicates. A workspace member or API key with permission to read an object but not a specific field can reference the denied field inside direct filters, relation filters, or persisted view filters. The returned totalCount and row presence form a boolean and count oracle. Attackers can iteratively guess values and reconstruct denied field data for any record exposed by row-level policy. The issue is tracked under [CWE-200] and is fixed in version 2.22.0.
Critical Impact
Low-privileged workspace members and API keys can exfiltrate values of restricted fields such as salaries, private notes, or PII by abusing GraphQL filter predicates as a side-channel oracle.
Affected Products
- Twenty CRM versions prior to 2.22.0
- Twenty GraphQL API filter parsers
- Twenty REST API filter predicates and persisted view filters
Discovery Timeline
- 2026-09-22 - CVE CVE-2026-85055 published to NVD
- 2026-09-23 - Last updated in NVD database
Technical Details for CVE-2026-85055
Vulnerability Analysis
The vulnerability is a broken access control flaw in Twenty's query layer. Twenty implements field-level read permissions to prevent principals from selecting restricted columns in query output. However, the permission check is applied only to the projection stage. Filter predicates parsed from GraphQL where clauses, REST filter parameters, and persisted view filters bypass the same enforcement.
An attacker with row-level access to an object can construct filters that reference denied fields. The server evaluates the predicate against the underlying column and returns totalCount and row identifiers based on the match. This turns any restricted field into a queryable oracle. Repeated queries with narrowed predicates such as startsWith, gt, or equality checks allow full reconstruction of denied values one character or range at a time.
Root Cause
The root cause is missing authorization on filter inputs in the GraphQL and REST query parsers. The GraphqlQueryFilterFieldParser and related common query runners resolved field references without consulting the caller's RestrictedFieldsPermissions map. Only the output selection path enforced field ACLs, leaving predicate evaluation as an unauthenticated path to protected data.
Attack Vector
Exploitation requires an authenticated principal, either a workspace member or an API key, with the ability to read some records of the target object. The attacker issues GraphQL or REST queries that include filter conditions on fields the principal cannot read. By observing changes in totalCount or row presence across successive queries, the attacker infers protected values without ever selecting them in the response payload.
// Security patch: block filter predicates that reference restricted fields
// packages/twenty-server/src/engine/api/graphql/graphql-query-runner/
// graphql-query-parsers/graphql-query-filter/graphql-query-filter-field.parser.ts
import {
PermissionsException,
PermissionsExceptionCode,
PermissionsExceptionMessage,
} from 'src/engine/metadata-modules/permissions/permissions.exception';
import { type WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder';
import { GraphqlQueryFilterConditionParser } from './graphql-query-filter-condition.parser';
const ARRAY_OPERATORS = ['in', 'contains', 'notContains'];
export class GraphqlQueryFilterFieldParser {
private flatObjectMetadata: FlatObjectMetadata;
private flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
private flatObjectMetadataMaps?: FlatEntityMaps<FlatObjectMetadata>;
private fieldIdByName: Record<string, string>;
Source: GitHub Commit a5108d5
Detection Methods for CVE-2026-85055
Indicators of Compromise
- High-volume GraphQL or REST queries from a single workspace member or API key that repeatedly filter on the same object with incrementally changing predicates.
- Filter predicates referencing field names that the principal is not authorized to select in output.
- Query patterns that consistently return only totalCount or minimal identifier fields while iterating filter values.
Detection Strategies
- Audit application logs for GraphQL where clauses and REST filter parameters that name fields excluded from the caller's role permissions.
- Correlate query rate, response totalCount variance, and predicate diversity per API key to identify oracle-style enumeration.
- Baseline normal filter usage per object and alert on principals issuing filter predicates against fields they have never selected in output.
Monitoring Recommendations
- Enable verbose query logging on the Twenty server and forward it to a centralized analytics platform for review.
- Track per-principal query counts against sensitive objects and alert on sustained spikes indicating enumeration.
- Review persisted view definitions for filters that reference restricted fields and remove unauthorized configurations.
How to Mitigate CVE-2026-85055
Immediate Actions Required
- Upgrade all Twenty deployments to version 2.22.0 or later without delay.
- Rotate API keys that have been used against affected instances to invalidate any credentials that may have been leveraged for enumeration.
- Review workspace member and API key permissions and remove access from principals that do not require it.
Patch Information
The fix is delivered in Twenty v2.22.0 via pull request #22873 and commit a5108d5. The patch introduces RestrictedFieldsPermissions enforcement inside filter parsers, raising a PermissionsException when a principal references a denied field in any predicate. See GHSA-v93q-4jcx-7p9m for full advisory details.
Workarounds
- Restrict object-level read access for principals who should not access sensitive fields, since row-level exclusion also prevents oracle exploitation.
- Audit and remove persisted views that filter on restricted fields until the upgrade is applied.
- Place a query-inspection proxy in front of the API to reject GraphQL and REST filter predicates that name protected field identifiers.
# Verify installed Twenty server version and upgrade via Docker
docker inspect twentycrm/twenty --format '{{.Config.Image}}'
docker pull twentycrm/twenty:v2.22.0
docker compose down && docker compose up -d
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

