From 7b19f9490f4b8091324768d8ce3cc30a127b8e74 Mon Sep 17 00:00:00 2001 From: Wilson Rivera Date: Wed, 26 Nov 2025 13:26:43 -0500 Subject: [PATCH 1/5] feat: prevent error when passed invalid uuid --- .../src/core/repositories/FeatureFlagRepository.ts | 8 +++----- .../src/core/repositories/SubgraphRepository.ts | 13 ++++--------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/controlplane/src/core/repositories/FeatureFlagRepository.ts b/controlplane/src/core/repositories/FeatureFlagRepository.ts index 66c19210ae..d8224a9a3a 100644 --- a/controlplane/src/core/repositories/FeatureFlagRepository.ts +++ b/controlplane/src/core/repositories/FeatureFlagRepository.ts @@ -1,8 +1,9 @@ import { Subgraph } from '@wundergraph/composition'; import { joinLabel, splitLabel } from '@wundergraph/cosmo-shared'; -import { SQL, and, asc, count, eq, getTableName, inArray, like, or, sql } from 'drizzle-orm'; +import { SQL, and, asc, count, eq, inArray, like, or, sql } from 'drizzle-orm'; import { PostgresJsDatabase } from 'drizzle-orm/postgres-js'; import { FastifyBaseLogger } from 'fastify'; +import { validate as isValidUuid } from 'uuid'; import { parse } from 'graphql'; import * as schema from '../../db/schema.js'; import { @@ -312,10 +313,7 @@ export class FeatureFlagRepository { if (query) { conditions.push( - or( - like(schema.targets.name, `%${query}%`), - sql.raw(`${getTableName(schema.subgraphs)}.${schema.subgraphs.id.name}::text like '%${query}%'`), - ), + isValidUuid(query) ? eq(subgraphs.id, query) : like(schema.targets.name, `%${query}%`), ); } diff --git a/controlplane/src/core/repositories/SubgraphRepository.ts b/controlplane/src/core/repositories/SubgraphRepository.ts index 60ab3815a8..da9c8e2f8a 100644 --- a/controlplane/src/core/repositories/SubgraphRepository.ts +++ b/controlplane/src/core/repositories/SubgraphRepository.ts @@ -9,7 +9,8 @@ import { } from '@wundergraph/cosmo-connect/dist/platform/v1/platform_pb'; import { joinLabel, normalizeURL, splitLabel } from '@wundergraph/cosmo-shared'; import { addDays } from 'date-fns'; -import { and, asc, count, desc, eq, getTableName, gt, inArray, like, lt, notInArray, or, SQL, sql } from 'drizzle-orm'; +import { and, asc, count, desc, eq, gt, inArray, like, lt, notInArray, or, SQL, sql } from 'drizzle-orm'; +import { validate as isValidUuid } from 'uuid'; import { PostgresJsDatabase } from 'drizzle-orm/postgres-js'; import { FastifyBaseLogger } from 'fastify'; import { GraphQLSchema } from 'graphql'; @@ -698,10 +699,7 @@ export class SubgraphRepository { if (opts.query) { conditions.push( - or( - like(schema.targets.name, `%${opts.query}%`), - sql.raw(`${getTableName(schema.subgraphs)}.${schema.subgraphs.id.name}::text like '%${opts.query}%'`), - ), + isValidUuid(opts.query) ? eq(schema.subgraphs.id, opts.query) : like(schema.targets.name, `%${opts.query}%`), ); } @@ -760,10 +758,7 @@ export class SubgraphRepository { if (opts.query) { conditions.push( - or( - like(schema.targets.name, `%${opts.query}%`), - sql.raw(`${getTableName(schema.subgraphs)}.${schema.subgraphs.id.name}::text like '%${opts.query}%'`), - ), + isValidUuid(opts.query) ? eq(schema.subgraphs.id, opts.query) : like(schema.targets.name, `%${opts.query}%`), ); } From b78d0d68eec78edc0f89bed757d72c99c2c75c43 Mon Sep 17 00:00:00 2001 From: Wilson Rivera Date: Wed, 26 Nov 2025 13:27:37 -0500 Subject: [PATCH 2/5] chore: fix linting --- controlplane/src/core/repositories/FeatureFlagRepository.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/controlplane/src/core/repositories/FeatureFlagRepository.ts b/controlplane/src/core/repositories/FeatureFlagRepository.ts index d8224a9a3a..280e29a501 100644 --- a/controlplane/src/core/repositories/FeatureFlagRepository.ts +++ b/controlplane/src/core/repositories/FeatureFlagRepository.ts @@ -312,9 +312,7 @@ export class FeatureFlagRepository { } if (query) { - conditions.push( - isValidUuid(query) ? eq(subgraphs.id, query) : like(schema.targets.name, `%${query}%`), - ); + conditions.push(isValidUuid(query) ? eq(subgraphs.id, query) : like(schema.targets.name, `%${query}%`)); } if (!this.applyRbacConditionsToQuery(rbac, conditions)) { From 4f38d2a65f21d51ab8e0f9e37c321dfdffab4692 Mon Sep 17 00:00:00 2001 From: Wilson Rivera Date: Wed, 26 Nov 2025 13:32:26 -0500 Subject: [PATCH 3/5] chore: escape query --- controlplane/src/core/repositories/FeatureFlagRepository.ts | 3 ++- controlplane/src/core/repositories/SubgraphRepository.ts | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/controlplane/src/core/repositories/FeatureFlagRepository.ts b/controlplane/src/core/repositories/FeatureFlagRepository.ts index 280e29a501..5889da2a7e 100644 --- a/controlplane/src/core/repositories/FeatureFlagRepository.ts +++ b/controlplane/src/core/repositories/FeatureFlagRepository.ts @@ -312,7 +312,8 @@ export class FeatureFlagRepository { } if (query) { - conditions.push(isValidUuid(query) ? eq(subgraphs.id, query) : like(schema.targets.name, `%${query}%`)); + const escapedQuery = query.replace(/‘/g, "''"); + conditions.push(isValidUuid(query) ? eq(subgraphs.id, query) : like(schema.targets.name, `%${escapedQuery}%`)); } if (!this.applyRbacConditionsToQuery(rbac, conditions)) { diff --git a/controlplane/src/core/repositories/SubgraphRepository.ts b/controlplane/src/core/repositories/SubgraphRepository.ts index da9c8e2f8a..469d4f91c8 100644 --- a/controlplane/src/core/repositories/SubgraphRepository.ts +++ b/controlplane/src/core/repositories/SubgraphRepository.ts @@ -698,8 +698,9 @@ export class SubgraphRepository { } if (opts.query) { + const escapedQuery = opts.query.replace(/‘/g, "''"); conditions.push( - isValidUuid(opts.query) ? eq(schema.subgraphs.id, opts.query) : like(schema.targets.name, `%${opts.query}%`), + isValidUuid(opts.query) ? eq(schema.subgraphs.id, opts.query) : like(schema.targets.name, `%${escapedQuery}%`), ); } @@ -757,8 +758,9 @@ export class SubgraphRepository { } if (opts.query) { + const escapedQuery = opts.query.replace(/‘/g, "''"); conditions.push( - isValidUuid(opts.query) ? eq(schema.subgraphs.id, opts.query) : like(schema.targets.name, `%${opts.query}%`), + isValidUuid(opts.query) ? eq(schema.subgraphs.id, opts.query) : like(schema.targets.name, `%${escapedQuery}%`), ); } From c0d742e3b586b991dd80e64e13e4b1278a440ab5 Mon Sep 17 00:00:00 2001 From: Wilson Rivera Date: Wed, 26 Nov 2025 14:00:06 -0500 Subject: [PATCH 4/5] chore: remove unnecessary `sql.raw` --- .../core/repositories/FeatureFlagRepository.ts | 13 ++++++++----- .../repositories/FederatedGraphRepository.ts | 8 ++++---- .../src/core/repositories/SubgraphRepository.ts | 16 +++++++++------- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/controlplane/src/core/repositories/FeatureFlagRepository.ts b/controlplane/src/core/repositories/FeatureFlagRepository.ts index 5889da2a7e..3802b6f4dc 100644 --- a/controlplane/src/core/repositories/FeatureFlagRepository.ts +++ b/controlplane/src/core/repositories/FeatureFlagRepository.ts @@ -1,6 +1,6 @@ import { Subgraph } from '@wundergraph/composition'; import { joinLabel, splitLabel } from '@wundergraph/cosmo-shared'; -import { SQL, and, asc, count, eq, inArray, like, or, sql } from 'drizzle-orm'; +import { SQL, and, asc, count, eq, inArray, like, or, sql, arrayContains } from 'drizzle-orm'; import { PostgresJsDatabase } from 'drizzle-orm/postgres-js'; import { FastifyBaseLogger } from 'fastify'; import { validate as isValidUuid } from 'uuid'; @@ -312,8 +312,7 @@ export class FeatureFlagRepository { } if (query) { - const escapedQuery = query.replace(/‘/g, "''"); - conditions.push(isValidUuid(query) ? eq(subgraphs.id, query) : like(schema.targets.name, `%${escapedQuery}%`)); + conditions.push(isValidUuid(query) ? eq(subgraphs.id, query) : like(schema.targets.name, `%${query}%`)); } if (!this.applyRbacConditionsToQuery(rbac, conditions)) { @@ -666,9 +665,13 @@ export class FeatureFlagRepository { const conditions: SQL[] = []; for (const labels of groupedLabels) { - const labelsSQL = labels.map((l) => `"${joinLabel(l)}"`).join(', '); // At least one common label - conditions.push(sql.raw(`labels && '{${labelsSQL}}'`)); + conditions.push( + arrayContains( + featureFlags.labels, + labels.map((l) => joinLabel(l)), + ), + ); } // Only get feature flags that do not have any labels if the label matchers are empty. diff --git a/controlplane/src/core/repositories/FederatedGraphRepository.ts b/controlplane/src/core/repositories/FederatedGraphRepository.ts index eea8a089a9..edcd4f3115 100644 --- a/controlplane/src/core/repositories/FederatedGraphRepository.ts +++ b/controlplane/src/core/repositories/FederatedGraphRepository.ts @@ -10,6 +10,7 @@ import { import { joinLabel, normalizeURL } from '@wundergraph/cosmo-shared'; import { and, + arrayContains, asc, desc, eq, @@ -745,10 +746,9 @@ export class FederatedGraphRepository { not( // We created a GIN index on the label_matcher column, so we can look up // very quickly if the label matcher matches the given subgraph labels. - sql.raw( - `${targetLabelMatchers.labelMatcher.name} && ARRAY[${uniqueLabels.map( - (ul) => "'" + joinLabel(ul) + "'", - )}]`, + arrayContains( + targetLabelMatchers.labelMatcher, + uniqueLabels.map((ul) => joinLabel(ul)), ), ), ), diff --git a/controlplane/src/core/repositories/SubgraphRepository.ts b/controlplane/src/core/repositories/SubgraphRepository.ts index 469d4f91c8..2a40fd471f 100644 --- a/controlplane/src/core/repositories/SubgraphRepository.ts +++ b/controlplane/src/core/repositories/SubgraphRepository.ts @@ -9,7 +9,7 @@ import { } from '@wundergraph/cosmo-connect/dist/platform/v1/platform_pb'; import { joinLabel, normalizeURL, splitLabel } from '@wundergraph/cosmo-shared'; import { addDays } from 'date-fns'; -import { and, asc, count, desc, eq, gt, inArray, like, lt, notInArray, or, SQL, sql } from 'drizzle-orm'; +import { and, arrayContains, asc, count, desc, eq, gt, inArray, like, lt, notInArray, or, SQL } from 'drizzle-orm'; import { validate as isValidUuid } from 'uuid'; import { PostgresJsDatabase } from 'drizzle-orm/postgres-js'; import { FastifyBaseLogger } from 'fastify'; @@ -698,9 +698,8 @@ export class SubgraphRepository { } if (opts.query) { - const escapedQuery = opts.query.replace(/‘/g, "''"); conditions.push( - isValidUuid(opts.query) ? eq(schema.subgraphs.id, opts.query) : like(schema.targets.name, `%${escapedQuery}%`), + isValidUuid(opts.query) ? eq(schema.subgraphs.id, opts.query) : like(schema.targets.name, `%${opts.query}%`), ); } @@ -758,9 +757,8 @@ export class SubgraphRepository { } if (opts.query) { - const escapedQuery = opts.query.replace(/‘/g, "''"); conditions.push( - isValidUuid(opts.query) ? eq(schema.subgraphs.id, opts.query) : like(schema.targets.name, `%${escapedQuery}%`), + isValidUuid(opts.query) ? eq(schema.subgraphs.id, opts.query) : like(schema.targets.name, `%${opts.query}%`), ); } @@ -1391,9 +1389,13 @@ export class SubgraphRepository { const conditions: SQL[] = []; for (const labels of groupedLabels) { - const labelsSQL = labels.map((l) => `"${joinLabel(l)}"`).join(', '); // At least one common label - conditions.push(sql.raw(`labels && '{${labelsSQL}}'`)); + conditions.push( + arrayContains( + targets.labels, + labels.map((l) => joinLabel(l)), + ), + ); } // Only get subgraphs that do not have any labels if the label matchers are empty. From 5836ce9946d48b17c8b4d9928e9ff1d6df2990a6 Mon Sep 17 00:00:00 2001 From: Wilson Rivera Date: Wed, 26 Nov 2025 14:23:03 -0500 Subject: [PATCH 5/5] chore: replace `arrayContains` to `arrayOverlaps` --- controlplane/src/core/repositories/FeatureFlagRepository.ts | 4 ++-- .../src/core/repositories/FederatedGraphRepository.ts | 3 ++- controlplane/src/core/repositories/SubgraphRepository.ts | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/controlplane/src/core/repositories/FeatureFlagRepository.ts b/controlplane/src/core/repositories/FeatureFlagRepository.ts index 3802b6f4dc..05b7b044c0 100644 --- a/controlplane/src/core/repositories/FeatureFlagRepository.ts +++ b/controlplane/src/core/repositories/FeatureFlagRepository.ts @@ -1,6 +1,6 @@ import { Subgraph } from '@wundergraph/composition'; import { joinLabel, splitLabel } from '@wundergraph/cosmo-shared'; -import { SQL, and, asc, count, eq, inArray, like, or, sql, arrayContains } from 'drizzle-orm'; +import { SQL, and, asc, count, eq, inArray, like, or, sql, arrayOverlaps } from 'drizzle-orm'; import { PostgresJsDatabase } from 'drizzle-orm/postgres-js'; import { FastifyBaseLogger } from 'fastify'; import { validate as isValidUuid } from 'uuid'; @@ -667,7 +667,7 @@ export class FeatureFlagRepository { for (const labels of groupedLabels) { // At least one common label conditions.push( - arrayContains( + arrayOverlaps( featureFlags.labels, labels.map((l) => joinLabel(l)), ), diff --git a/controlplane/src/core/repositories/FederatedGraphRepository.ts b/controlplane/src/core/repositories/FederatedGraphRepository.ts index edcd4f3115..7418dc4ff8 100644 --- a/controlplane/src/core/repositories/FederatedGraphRepository.ts +++ b/controlplane/src/core/repositories/FederatedGraphRepository.ts @@ -11,6 +11,7 @@ import { joinLabel, normalizeURL } from '@wundergraph/cosmo-shared'; import { and, arrayContains, + arrayOverlaps, asc, desc, eq, @@ -746,7 +747,7 @@ export class FederatedGraphRepository { not( // We created a GIN index on the label_matcher column, so we can look up // very quickly if the label matcher matches the given subgraph labels. - arrayContains( + arrayOverlaps( targetLabelMatchers.labelMatcher, uniqueLabels.map((ul) => joinLabel(ul)), ), diff --git a/controlplane/src/core/repositories/SubgraphRepository.ts b/controlplane/src/core/repositories/SubgraphRepository.ts index 2a40fd471f..755db8c5b6 100644 --- a/controlplane/src/core/repositories/SubgraphRepository.ts +++ b/controlplane/src/core/repositories/SubgraphRepository.ts @@ -9,7 +9,7 @@ import { } from '@wundergraph/cosmo-connect/dist/platform/v1/platform_pb'; import { joinLabel, normalizeURL, splitLabel } from '@wundergraph/cosmo-shared'; import { addDays } from 'date-fns'; -import { and, arrayContains, asc, count, desc, eq, gt, inArray, like, lt, notInArray, or, SQL } from 'drizzle-orm'; +import { and, arrayOverlaps, asc, count, desc, eq, gt, inArray, like, lt, notInArray, or, SQL } from 'drizzle-orm'; import { validate as isValidUuid } from 'uuid'; import { PostgresJsDatabase } from 'drizzle-orm/postgres-js'; import { FastifyBaseLogger } from 'fastify'; @@ -1391,7 +1391,7 @@ export class SubgraphRepository { for (const labels of groupedLabels) { // At least one common label conditions.push( - arrayContains( + arrayOverlaps( targets.labels, labels.map((l) => joinLabel(l)), ),