Skip to content

Conversation

@JivusAyrus
Copy link
Member

@JivusAyrus JivusAyrus commented Dec 3, 2025

Summary by CodeRabbit

  • New Features

    • Proposed-schema retrieval now accepts a subgraph identifier to return per-subgraph proposed schemas.
    • Proposal operations are scoped to an organization for stronger tenant isolation.
  • Bug Fixes

    • Added explicit existence checks and clearer not-found responses for missing federated graphs, subgraphs, or proposed schemas.
  • Tests

    • Added comprehensive tests validating proposal/data isolation across organizations, graphs, namespaces, and subgraphs.

✏️ Tip: You can customize this high-level summary in your review settings.

Checklist

  • I have discussed my proposed changes in an issue and have received approval to proceed.
  • I have followed the coding standards of the project.
  • Tests or benchmarks have been added or updated.
  • Documentation has been updated on https://github.com/wundergraph/cosmo-docs.
  • I have read the Contributors Guide.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 3, 2025

Walkthrough

Adds a per-subgraph identifier subgraphId to the GetProposedSchemaOfCheckedSubgraph request and propagates it through protobuf/generated code, services, and repository; introduces organizationId scoping to ProposalRepository, adds federated-graph existence checks, and adds comprehensive cross-organization isolation tests.

Changes

Cohort / File(s) Summary
Protobuf + Generated
proto/wg/cosmo/platform/v1/platform.proto, connect/src/wg/cosmo/platform/v1/platform_pb.ts
Added string subgraphId = 3; to GetProposedSchemaOfCheckedSubgraphRequest and updated generated TS to include subgraphId.
Schema check service
controlplane/src/core/bufservices/check/getProposedSchemaOfCheckedSubgraph.ts
Look up subgraph via SubgraphRepository using req.subgraphId; return ERR_NOT_FOUND if subgraph or proposed schema missing; pass subgraphId into repository call and return found proposedSchema.
Repository — Schema checks
controlplane/src/core/repositories/SchemaCheckRepository.ts
getProposedSchemaOfCheckedSubgraph now accepts subgraphId: string and filters by schemaCheckSubgraphs.subgraphId in the query.
ProposalRepository core & scoping
controlplane/src/core/repositories/ProposalRepository.ts
Added organizationId parameter to constructor and applied organization-scoped filters/joins across many queries; removed organizationId param from getLatestCheckForProposal signature.
ProposalRepository call-site updates
controlplane/src/core/bufservices/proposal/*, controlplane/src/core/bufservices/subgraph/*, controlplane/src/core/bufservices/check/getCheckSummary.ts, controlplane/src/core/repositories/SubgraphRepository.ts
Updated numerous files to instantiate ProposalRepository(db, organizationId) and adjusted related call signatures to match new proposal-repo API.
Federated graph validation
controlplane/src/core/bufservices/proposal/getProposalChecks.ts, controlplane/src/core/repositories/FederatedGraphRepository.js
Added FederatedGraphRepository usage and existence checks for federated graph; short-circuits with ERR_NOT_FOUND when missing.
Frontend
studio/src/pages/[organizationSlug]/[namespace]/graph/[slug]/checks/[checkId]/index.tsx
Passes subgraphId: activeSubgraph?.subgraphId into getProposedSchemaOfCheckedSubgraph variables.
Tests
controlplane/test/proposal/proposal-data-isolation.test.ts
New comprehensive tests exercising cross-organization/federated-graph/namespace/subgraph isolation for proposals (access, updates, listings, checks, config, publish flows).

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120+ minutes

  • Areas needing focused review:
    • ProposalRepository core: verify all added joins/organizationId filters and composed SQL conditions for correctness and performance.
    • All ProposalRepository call sites: ensure organizationId is passed consistently and updated method signatures (e.g., getLatestCheckForProposal) are used everywhere.
    • SchemaCheckRepository + getProposedSchemaOfCheckedSubgraph flow: verify the new subgraphId filter and not-found semantics.
    • Proto → generated TS alignment: confirm field numbers/types and RPC serialization compatibility.
    • Federated graph existence checks: ensure error codes/details are consistent with API/clients.
    • New isolation tests: run and inspect for flakiness and correct fixture cleanup.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main objective of this PR: adding organization ownership validation for proposals. The changes across multiple files consistently implement multi-tenant scoping for the ProposalRepository and related entities to ensure data isolation.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe34111 and c471fe7.

⛔ Files ignored due to path filters (1)
  • connect-go/gen/proto/wg/cosmo/platform/v1/platform.pb.go is excluded by !**/*.pb.go, !**/gen/**
📒 Files selected for processing (3)
  • connect/src/wg/cosmo/platform/v1/platform_pb.ts (2 hunks)
  • controlplane/src/core/repositories/SubgraphRepository.ts (1 hunks)
  • proto/wg/cosmo/platform/v1/platform.proto (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • controlplane/src/core/repositories/SubgraphRepository.ts
  • proto/wg/cosmo/platform/v1/platform.proto
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/bufservices/check/getCheckSummary.ts:0-0
Timestamp: 2025-08-31T18:51:32.185Z
Learning: In the SchemaCheckRepository.getLinkedSchemaCheck method, organization-level security is implemented through post-query validation by checking `check.subgraphs[0].namespace.organizationId !== organizationId` and returning undefined if the linked check doesn't belong to the caller's organization, preventing cross-tenant data leakage.
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/repositories/SubgraphRepository.ts:1746-1763
Timestamp: 2025-09-08T20:57:07.946Z
Learning: The checkSubgraphSchema.ts file already correctly implements linked subgraph functionality, using byName(linkedSubgraph.name, linkedSubgraph.namespace) to fetch target subgraphs and properly handles parse(newSchemaSDL) for schema building. The implementation doesn't need fixes for byId usage or schema parsing as it's already correct.
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/repositories/SubgraphRepository.ts:1749-1751
Timestamp: 2025-08-29T10:28:04.846Z
Learning: In the controlplane codebase, authentication and authorization checks (including organization scoping) are handled at the service layer in files like unlinkSubgraph.ts before calling repository methods. Repository methods like unlinkSubgraph() in SubgraphRepository.ts can focus purely on data operations without redundant security checks.
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/repositories/SubgraphRepository.ts:1749-1751
Timestamp: 2025-08-29T10:28:04.846Z
Learning: In the controlplane codebase, authentication and authorization checks (including organization scoping) are handled at the service layer in files like unlinkSubgraph.ts before calling repository methods. Repository methods like unlinkSubgraph() in SubgraphRepository.ts can focus purely on data operations without redundant security checks.
📚 Learning: 2025-09-08T20:57:07.946Z
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/repositories/SubgraphRepository.ts:1746-1763
Timestamp: 2025-09-08T20:57:07.946Z
Learning: The checkSubgraphSchema.ts file already correctly implements linked subgraph functionality, using byName(linkedSubgraph.name, linkedSubgraph.namespace) to fetch target subgraphs and properly handles parse(newSchemaSDL) for schema building. The implementation doesn't need fixes for byId usage or schema parsing as it's already correct.

Applied to files:

  • connect/src/wg/cosmo/platform/v1/platform_pb.ts
📚 Learning: 2025-09-10T09:53:42.914Z
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: studio/src/pages/[organizationSlug]/[namespace]/graph/[slug]/checks/index.tsx:189-197
Timestamp: 2025-09-10T09:53:42.914Z
Learning: In protobuf-generated TypeScript code, repeated fields (arrays) are always initialized with default empty arrays and cannot be undefined, making defensive programming checks like `|| []` or optional chaining unnecessary for these fields.

Applied to files:

  • connect/src/wg/cosmo/platform/v1/platform_pb.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: build-router
  • GitHub Check: build_push_image
  • GitHub Check: build_test
  • GitHub Check: build_push_image (nonroot)
  • GitHub Check: build_push_image
  • GitHub Check: build_test
  • GitHub Check: image_scan
  • GitHub Check: image_scan (nonroot)
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: integration_test (./events)
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: build_test
  • GitHub Check: build_push_image
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
🔇 Additional comments (2)
connect/src/wg/cosmo/platform/v1/platform_pb.ts (2)

21589-21592: LGTM!

The new subgraphId field is correctly added following the existing protobuf-generated code patterns. This field enables organization ownership validation for the proposed schema fetch operation, addressing the IDOR vulnerability mentioned in the PR objectives.


21601-21604: LGTM!

The field list entry is correctly structured with the proper field number, name, and type matching the proto definition.


Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Dec 3, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@44916c7). Learn more about missing BASE report.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2389   +/-   ##
=======================================
  Coverage        ?   31.14%           
=======================================
  Files           ?      340           
  Lines           ?    33837           
  Branches        ?      241           
=======================================
  Hits            ?    10537           
  Misses          ?    22384           
  Partials        ?      916           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions
Copy link

github-actions bot commented Dec 3, 2025

Router image scan passed

✅ No security vulnerabilities found in image:

ghcr.io/wundergraph/cosmo/router:sha-ce6e9d3e9120cdc9d00b3e68a0077d33423289c7

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
studio/src/pages/[organizationSlug]/[namespace]/graph/[slug]/checks/[checkId]/index.tsx (1)

166-176: Gate proposed-schema query on presence of subgraphId to avoid spurious 404s

Because the backend now validates req.subgraphId against SubgraphRepository, calling this query when activeSubgraph?.subgraphId is unset (e.g. newly created or deleted subgraphs that don’t have a persisted subgraph row yet) will always come back as "Subgraph not found".

You’re already falling back to the sdl prop when sdlData?.proposedSchema is empty, so you can avoid unnecessary failing calls by also requiring a concrete subgraphId in the enabled flag:

   const { data: sdlData, isLoading: fetchingSdl } = useQuery(
     getProposedSchemaOfCheckedSubgraph,
     {
       checkId,
       checkedSubgraphId: activeSubgraph?.id,
       subgraphId: activeSubgraph?.subgraphId,
     },
     {
-      enabled: !!activeSubgraph && !!activeSubgraphName,
+      enabled: !!activeSubgraph && !!activeSubgraphName && !!activeSubgraph?.subgraphId,
     },
   );

This keeps behavior for subgraphs without an id the same as before (use the sdl prop) while still exercising the stricter backend path when an id is available.

controlplane/src/core/bufservices/check/getProposedSchemaOfCheckedSubgraph.ts (1)

11-42: Org-scoped subgraph lookup adds security, but RPC now requires non-null subgraphId

Using SubgraphRepository(logger, opts.db, authContext.organizationId) and validating byId(req.subgraphId) is solid for org-scoped access control.

However, this introduces a backwards compatibility constraint: the repository query uses eq(schema.schemaCheckSubgraphs.subgraphId, subgraphId), which will not match rows where subgraphId is NULL. Since the database schema allows subgraphId to be nullable (with onDelete: 'set null'), old checks whose schema_check_subgraphs rows have NULL subgraphId will silently fail to return data, even if the proposed SDL exists.

Studio mitigates this risk by guarding the query with enabled: !!activeSubgraph && !!activeSubgraphName, so the RPC won't execute unless activeSubgraph is populated. Since Studio is the only known consumer, the practical impact is limited. However, if other clients are added later, or if checks with NULL subgraphId exist in production, they will hit ERR_NOT_FOUND.

♻️ Duplicate comments (1)
controlplane/src/core/repositories/SchemaCheckRepository.ts (1)

571-585: Additional subgraphId filter further constrains lookups; watch out for rows with NULL subgraphId

Extending getProposedSchemaOfCheckedSubgraph to require subgraphId and adding eq(schema.schemaCheckSubgraphs.subgraphId, subgraphId) in the WHERE clause is a good defense against mismatched (checkId, checkedSubgraphId, subgraphId) tuples.

Given that createSchemaCheckSubgraph sometimes inserts subgraphId as undefined/NULL for new subgraphs (no persisted subgraph row yet), the combination of this filter and the new handler logic means those rows will no longer be returned once a non-empty subgraphId is supplied. That’s aligned with the stricter backend behavior discussed in getProposedSchemaOfCheckedSubgraph.ts; just make sure that’s acceptable for your existing data and clients, or consider treating subgraphId === "" as “no filter” if you deliberately want to keep the old behavior for those historical rows.

🧹 Nitpick comments (1)
controlplane/src/core/bufservices/proposal/getProposalChecks.ts (1)

12-52: Org-scoped federated graph check correctly gates proposal checks

Creating an org-scoped FederatedGraphRepository and short-circuiting when byId(proposal.proposal.federatedGraphId) returns nothing is a good way to ensure callers only see checks for proposals whose federated graph belongs to their organization.

One thing to consider (non-blocking): callers can distinguish between a truly missing proposal ("Proposal with ID … not found") and a proposal whose federated graph is inaccessible ("Federated graph of the proposal not found"). If you want to avoid that enumeration difference, you could normalize both paths to a single "Proposal not found"-style detail.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5376bd9 and 7e3ebee.

⛔ Files ignored due to path filters (1)
  • connect-go/gen/proto/wg/cosmo/platform/v1/platform.pb.go is excluded by !**/*.pb.go, !**/gen/**
📒 Files selected for processing (7)
  • connect/src/wg/cosmo/platform/v1/platform_pb.ts (2 hunks)
  • controlplane/src/core/bufservices/check/getProposedSchemaOfCheckedSubgraph.ts (2 hunks)
  • controlplane/src/core/bufservices/proposal/getProposal.ts (1 hunks)
  • controlplane/src/core/bufservices/proposal/getProposalChecks.ts (3 hunks)
  • controlplane/src/core/repositories/SchemaCheckRepository.ts (1 hunks)
  • proto/wg/cosmo/platform/v1/platform.proto (1 hunks)
  • studio/src/pages/[organizationSlug]/[namespace]/graph/[slug]/checks/[checkId]/index.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/repositories/SubgraphRepository.ts:1746-1763
Timestamp: 2025-09-08T20:57:07.946Z
Learning: The checkSubgraphSchema.ts file already correctly implements linked subgraph functionality, using byName(linkedSubgraph.name, linkedSubgraph.namespace) to fetch target subgraphs and properly handles parse(newSchemaSDL) for schema building. The implementation doesn't need fixes for byId usage or schema parsing as it's already correct.
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/repositories/SubgraphRepository.ts:1749-1751
Timestamp: 2025-08-29T10:28:04.846Z
Learning: In the controlplane codebase, authentication and authorization checks (including organization scoping) are handled at the service layer in files like unlinkSubgraph.ts before calling repository methods. Repository methods like unlinkSubgraph() in SubgraphRepository.ts can focus purely on data operations without redundant security checks.
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/repositories/SubgraphRepository.ts:1749-1751
Timestamp: 2025-08-29T10:28:04.846Z
Learning: In the controlplane codebase, authentication and authorization checks (including organization scoping) are handled at the service layer in files like unlinkSubgraph.ts before calling repository methods. Repository methods like unlinkSubgraph() in SubgraphRepository.ts can focus purely on data operations without redundant security checks.
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/bufservices/check/getCheckSummary.ts:0-0
Timestamp: 2025-08-31T18:51:32.185Z
Learning: In the SchemaCheckRepository.getLinkedSchemaCheck method, organization-level security is implemented through post-query validation by checking `check.subgraphs[0].namespace.organizationId !== organizationId` and returning undefined if the linked check doesn't belong to the caller's organization, preventing cross-tenant data leakage.
Learnt from: endigma
Repo: wundergraph/cosmo PR: 2155
File: router/core/router.go:1857-1866
Timestamp: 2025-08-20T10:08:17.857Z
Learning: router/pkg/config/config.schema.json forbids null values for traffic_shaping.subgraphs: additionalProperties references $defs.traffic_shaping_subgraph_request_rule with type "object". Therefore, in core.NewSubgraphTransportOptions, dereferencing each subgraph rule pointer is safe under schema-validated configs, and a nil-check is unnecessary.
📚 Learning: 2025-09-08T20:57:07.946Z
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/repositories/SubgraphRepository.ts:1746-1763
Timestamp: 2025-09-08T20:57:07.946Z
Learning: The checkSubgraphSchema.ts file already correctly implements linked subgraph functionality, using byName(linkedSubgraph.name, linkedSubgraph.namespace) to fetch target subgraphs and properly handles parse(newSchemaSDL) for schema building. The implementation doesn't need fixes for byId usage or schema parsing as it's already correct.

Applied to files:

  • proto/wg/cosmo/platform/v1/platform.proto
  • connect/src/wg/cosmo/platform/v1/platform_pb.ts
  • controlplane/src/core/bufservices/check/getProposedSchemaOfCheckedSubgraph.ts
  • studio/src/pages/[organizationSlug]/[namespace]/graph/[slug]/checks/[checkId]/index.tsx
  • controlplane/src/core/repositories/SchemaCheckRepository.ts
📚 Learning: 2025-09-10T09:53:42.914Z
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: studio/src/pages/[organizationSlug]/[namespace]/graph/[slug]/checks/index.tsx:189-197
Timestamp: 2025-09-10T09:53:42.914Z
Learning: In protobuf-generated TypeScript code, repeated fields (arrays) are always initialized with default empty arrays and cannot be undefined, making defensive programming checks like `|| []` or optional chaining unnecessary for these fields.

Applied to files:

  • connect/src/wg/cosmo/platform/v1/platform_pb.ts
📚 Learning: 2025-08-29T10:28:04.846Z
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/repositories/SubgraphRepository.ts:1749-1751
Timestamp: 2025-08-29T10:28:04.846Z
Learning: In the controlplane codebase, authentication and authorization checks (including organization scoping) are handled at the service layer in files like unlinkSubgraph.ts before calling repository methods. Repository methods like unlinkSubgraph() in SubgraphRepository.ts can focus purely on data operations without redundant security checks.

Applied to files:

  • controlplane/src/core/bufservices/check/getProposedSchemaOfCheckedSubgraph.ts
  • studio/src/pages/[organizationSlug]/[namespace]/graph/[slug]/checks/[checkId]/index.tsx
  • controlplane/src/core/repositories/SchemaCheckRepository.ts
  • controlplane/src/core/bufservices/proposal/getProposalChecks.ts
📚 Learning: 2025-09-10T11:15:52.157Z
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/repositories/ProposalRepository.ts:562-572
Timestamp: 2025-09-10T11:15:52.157Z
Learning: The getLatestCheckForProposal function in controlplane/src/core/repositories/ProposalRepository.ts is only called during proposal creation or updates, so proposal match error checking (hasProposalMatchError) is not needed since the proposal is being modified itself rather than being matched against.

Applied to files:

  • controlplane/src/core/bufservices/check/getProposedSchemaOfCheckedSubgraph.ts
  • studio/src/pages/[organizationSlug]/[namespace]/graph/[slug]/checks/[checkId]/index.tsx
  • controlplane/src/core/repositories/SchemaCheckRepository.ts
  • controlplane/src/core/bufservices/proposal/getProposalChecks.ts
  • controlplane/src/core/bufservices/proposal/getProposal.ts
📚 Learning: 2025-08-31T18:51:32.185Z
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/bufservices/check/getCheckSummary.ts:0-0
Timestamp: 2025-08-31T18:51:32.185Z
Learning: In the SchemaCheckRepository.getLinkedSchemaCheck method, organization-level security is implemented through post-query validation by checking `check.subgraphs[0].namespace.organizationId !== organizationId` and returning undefined if the linked check doesn't belong to the caller's organization, preventing cross-tenant data leakage.

Applied to files:

  • controlplane/src/core/bufservices/check/getProposedSchemaOfCheckedSubgraph.ts
  • studio/src/pages/[organizationSlug]/[namespace]/graph/[slug]/checks/[checkId]/index.tsx
  • controlplane/src/core/repositories/SchemaCheckRepository.ts
  • controlplane/src/core/bufservices/proposal/getProposalChecks.ts
📚 Learning: 2025-08-20T10:08:17.857Z
Learnt from: endigma
Repo: wundergraph/cosmo PR: 2155
File: router/core/router.go:1857-1866
Timestamp: 2025-08-20T10:08:17.857Z
Learning: router/pkg/config/config.schema.json forbids null values for traffic_shaping.subgraphs: additionalProperties references $defs.traffic_shaping_subgraph_request_rule with type "object". Therefore, in core.NewSubgraphTransportOptions, dereferencing each subgraph rule pointer is safe under schema-validated configs, and a nil-check is unnecessary.

Applied to files:

  • controlplane/src/core/bufservices/check/getProposedSchemaOfCheckedSubgraph.ts
  • studio/src/pages/[organizationSlug]/[namespace]/graph/[slug]/checks/[checkId]/index.tsx
🧬 Code graph analysis (2)
controlplane/src/core/bufservices/check/getProposedSchemaOfCheckedSubgraph.ts (1)
controlplane/src/core/repositories/SubgraphRepository.ts (1)
  • SubgraphRepository (84-2196)
controlplane/src/core/bufservices/proposal/getProposalChecks.ts (1)
controlplane/src/core/repositories/FederatedGraphRepository.ts (1)
  • FederatedGraphRepository (98-1836)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: build-router
  • GitHub Check: image_scan (nonroot)
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: build_push_image (nonroot)
  • GitHub Check: integration_test (./events)
  • GitHub Check: build_push_image
  • GitHub Check: image_scan
  • GitHub Check: build_test
  • GitHub Check: build_push_image
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
  • GitHub Check: build_push_image
  • GitHub Check: build_test
🔇 Additional comments (4)
controlplane/src/core/bufservices/proposal/getProposal.ts (1)

41-49: Generic not-found message for federated graph is an improvement

Switching to the generic "Federated graph of the proposal not found" detail avoids exposing the internal federatedGraphId while keeping behavior unchanged. Looks good.

proto/wg/cosmo/platform/v1/platform.proto (1)

2736-2745: New subgraphId / proposedSchema fields are consistent with backend and UI usage

Adding string subgraphId = 3; to GetProposedSchemaOfCheckedSubgraphRequest and string proposedSchema = 2; to the response cleanly reflects how the handler and UI now work: they pass an explicit subgraph identifier and receive a concrete SDL string.

The field numbers are appended (1,2 → 3 and 1 → 2), so wire compatibility is preserved at the protobuf level; the behavioral tightening is entirely in the handler.

connect/src/wg/cosmo/platform/v1/platform_pb.ts (2)

21589-21596: Auto-generated protobuf field addition looks correct.

The new subgraphId field follows the established pattern for string fields in this generated file, with proper initialization and partial message support. This addition supports the organization ownership validation implemented in the service layer.


21601-21605: Field descriptor correctly added.

The new field entry maintains sequential field numbering and uses the correct scalar type for strings.

Copy link
Contributor

@wilsonrivera wilsonrivera left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@StarpTech StarpTech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we test this easily?

…posal operations and ensure data isolation across organizations
Copy link
Contributor

@StarpTech StarpTech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
controlplane/src/core/repositories/ProposalRepository.ts (1)

633-649: Consider documenting the organizationId parameter usage.

The organizationId parameter is accepted but only used for getLinkedSchemaChecks (line 715), not for filtering the main query. This is safe because the caller validates the proposal's organization ownership before calling this method, but the inconsistency could be confusing.

Consider either:

  1. Adding a comment explaining that proposal ownership is validated by the caller
  2. Or adding an explicit organization filter to the main query for defense-in-depth
controlplane/test/proposal/proposal-data-isolation.test.ts (2)

24-24: Consider using a more specific type instead of any.

The client parameter is typed as any, which reduces type safety. Consider using the actual client type from your test utilities or the generated protobuf types.


899-899: Clarify the duplicate comment.

The comment on line 899 is identical to line 887, but the test scenarios are different. The first test (lines 887-897) attempts to approve Company A's proposal using Company A's federated graph name, while the second test (lines 899-909) attempts to approve using Company B's federated graph name. Consider updating the comment to reflect this distinction:

-    // Try to approve Company A's proposal from Company B - should fail
+    // Try to approve Company A's proposal using Company B's federated graph - should fail
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8b58f1c and fe34111.

📒 Files selected for processing (14)
  • controlplane/src/core/bufservices/check/getCheckSummary.ts (1 hunks)
  • controlplane/src/core/bufservices/proposal/configureNamespaceProposalConfig.ts (1 hunks)
  • controlplane/src/core/bufservices/proposal/createProposal.ts (1 hunks)
  • controlplane/src/core/bufservices/proposal/enableProposalsForNamespace.ts (1 hunks)
  • controlplane/src/core/bufservices/proposal/getNamespaceProposalConfig.ts (1 hunks)
  • controlplane/src/core/bufservices/proposal/getProposal.ts (2 hunks)
  • controlplane/src/core/bufservices/proposal/getProposalChecks.ts (3 hunks)
  • controlplane/src/core/bufservices/proposal/getProposalsByFederatedGraph.ts (2 hunks)
  • controlplane/src/core/bufservices/proposal/updateProposal.ts (1 hunks)
  • controlplane/src/core/bufservices/subgraph/deleteFederatedSubgraph.ts (1 hunks)
  • controlplane/src/core/bufservices/subgraph/publishFederatedSubgraph.ts (1 hunks)
  • controlplane/src/core/repositories/ProposalRepository.ts (15 hunks)
  • controlplane/src/core/repositories/SubgraphRepository.ts (1 hunks)
  • controlplane/test/proposal/proposal-data-isolation.test.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/repositories/SubgraphRepository.ts:1746-1763
Timestamp: 2025-09-08T20:57:07.946Z
Learning: The checkSubgraphSchema.ts file already correctly implements linked subgraph functionality, using byName(linkedSubgraph.name, linkedSubgraph.namespace) to fetch target subgraphs and properly handles parse(newSchemaSDL) for schema building. The implementation doesn't need fixes for byId usage or schema parsing as it's already correct.
📚 Learning: 2025-09-10T11:15:52.157Z
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/repositories/ProposalRepository.ts:562-572
Timestamp: 2025-09-10T11:15:52.157Z
Learning: The getLatestCheckForProposal function in controlplane/src/core/repositories/ProposalRepository.ts is only called during proposal creation or updates, so proposal match error checking (hasProposalMatchError) is not needed since the proposal is being modified itself rather than being matched against.

Applied to files:

  • controlplane/src/core/bufservices/proposal/createProposal.ts
  • controlplane/src/core/bufservices/subgraph/publishFederatedSubgraph.ts
  • controlplane/src/core/repositories/SubgraphRepository.ts
  • controlplane/src/core/bufservices/proposal/getNamespaceProposalConfig.ts
  • controlplane/src/core/bufservices/proposal/configureNamespaceProposalConfig.ts
  • controlplane/src/core/bufservices/proposal/getProposalsByFederatedGraph.ts
  • controlplane/src/core/repositories/ProposalRepository.ts
  • controlplane/src/core/bufservices/check/getCheckSummary.ts
  • controlplane/src/core/bufservices/proposal/updateProposal.ts
  • controlplane/test/proposal/proposal-data-isolation.test.ts
  • controlplane/src/core/bufservices/proposal/getProposalChecks.ts
  • controlplane/src/core/bufservices/subgraph/deleteFederatedSubgraph.ts
  • controlplane/src/core/bufservices/proposal/enableProposalsForNamespace.ts
  • controlplane/src/core/bufservices/proposal/getProposal.ts
📚 Learning: 2025-08-29T10:28:04.846Z
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/repositories/SubgraphRepository.ts:1749-1751
Timestamp: 2025-08-29T10:28:04.846Z
Learning: In the controlplane codebase, authentication and authorization checks (including organization scoping) are handled at the service layer in files like unlinkSubgraph.ts before calling repository methods. Repository methods like unlinkSubgraph() in SubgraphRepository.ts can focus purely on data operations without redundant security checks.

Applied to files:

  • controlplane/src/core/bufservices/proposal/createProposal.ts
  • controlplane/src/core/bufservices/subgraph/publishFederatedSubgraph.ts
  • controlplane/src/core/repositories/SubgraphRepository.ts
  • controlplane/src/core/bufservices/proposal/getNamespaceProposalConfig.ts
  • controlplane/src/core/bufservices/proposal/configureNamespaceProposalConfig.ts
  • controlplane/src/core/bufservices/proposal/getProposalsByFederatedGraph.ts
  • controlplane/src/core/repositories/ProposalRepository.ts
  • controlplane/src/core/bufservices/check/getCheckSummary.ts
  • controlplane/src/core/bufservices/proposal/updateProposal.ts
  • controlplane/test/proposal/proposal-data-isolation.test.ts
  • controlplane/src/core/bufservices/proposal/getProposalChecks.ts
  • controlplane/src/core/bufservices/subgraph/deleteFederatedSubgraph.ts
  • controlplane/src/core/bufservices/proposal/enableProposalsForNamespace.ts
  • controlplane/src/core/bufservices/proposal/getProposal.ts
📚 Learning: 2025-08-31T18:51:32.185Z
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/bufservices/check/getCheckSummary.ts:0-0
Timestamp: 2025-08-31T18:51:32.185Z
Learning: In the SchemaCheckRepository.getLinkedSchemaCheck method, organization-level security is implemented through post-query validation by checking `check.subgraphs[0].namespace.organizationId !== organizationId` and returning undefined if the linked check doesn't belong to the caller's organization, preventing cross-tenant data leakage.

Applied to files:

  • controlplane/src/core/repositories/SubgraphRepository.ts
  • controlplane/src/core/repositories/ProposalRepository.ts
  • controlplane/src/core/bufservices/check/getCheckSummary.ts
  • controlplane/test/proposal/proposal-data-isolation.test.ts
  • controlplane/src/core/bufservices/proposal/getProposalChecks.ts
📚 Learning: 2025-09-08T20:57:07.946Z
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/repositories/SubgraphRepository.ts:1746-1763
Timestamp: 2025-09-08T20:57:07.946Z
Learning: The checkSubgraphSchema.ts file already correctly implements linked subgraph functionality, using byName(linkedSubgraph.name, linkedSubgraph.namespace) to fetch target subgraphs and properly handles parse(newSchemaSDL) for schema building. The implementation doesn't need fixes for byId usage or schema parsing as it's already correct.

Applied to files:

  • controlplane/src/core/repositories/SubgraphRepository.ts
  • controlplane/src/core/repositories/ProposalRepository.ts
  • controlplane/test/proposal/proposal-data-isolation.test.ts
📚 Learning: 2025-08-29T10:10:06.969Z
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: studio/src/pages/[organizationSlug]/[namespace]/graph/[slug]/checks/[checkId]/index.tsx:0-0
Timestamp: 2025-08-29T10:10:06.969Z
Learning: The isCheckSuccessful function in both controlplane/src/core/util.ts and studio/src/components/check-badge-icon.tsx includes early exits for linked check validation. It returns false immediately if either isLinkedTrafficCheckFailed or isLinkedPruningCheckFailed is true, which means linked check failures automatically cause the overall check to fail.

Applied to files:

  • controlplane/src/core/repositories/ProposalRepository.ts
📚 Learning: 2025-09-10T09:59:17.257Z
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/bufservices/subgraph/checkSubgraphSchema.ts:417-419
Timestamp: 2025-09-10T09:59:17.257Z
Learning: When skipTrafficCheck is true in schema checks, the traffic inspection loop is completely bypassed (as shown in line 2040 of SubgraphRepository.ts with the continue statement), which means hasClientTraffic will be false. The traffic analysis doesn't run at all when skipTrafficCheck is true, unlike the previous understanding that it always ran to collect usage data.

Applied to files:

  • controlplane/src/core/repositories/ProposalRepository.ts
📚 Learning: 2025-09-10T09:52:46.326Z
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/bufservices/subgraph/checkSubgraphSchema.ts:417-419
Timestamp: 2025-09-10T09:52:46.326Z
Learning: When skipTrafficCheck is true in schema checks, traffic analysis still runs to collect usage data, but hasClientTraffic can still be true if unsafe client traffic is detected. The difference is that traffic issues don't cause the overall check to fail, but the hasClientTraffic flag itself remains accurate based on the actual traffic analysis results.

Applied to files:

  • controlplane/src/core/repositories/ProposalRepository.ts
📚 Learning: 2025-09-10T11:31:32.502Z
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/repositories/ProposalRepository.ts:0-0
Timestamp: 2025-09-10T11:31:32.502Z
Learning: When traffic checks or pruning checks are skipped in schema validation, the corresponding hasClientTraffic and hasGraphPruningErrors flags are automatically set to false by default. This means that checking clientTrafficCheckSkipped or graphPruningCheckSkipped flags in linked check failure conditions is redundant, as skipped checks will naturally have false values for their respective has* flags.

Applied to files:

  • controlplane/src/core/repositories/ProposalRepository.ts
📚 Learning: 2025-07-01T13:53:54.146Z
Learnt from: wilsonrivera
Repo: wundergraph/cosmo PR: 1919
File: controlplane/src/core/repositories/OrganizationGroupRepository.ts:193-224
Timestamp: 2025-07-01T13:53:54.146Z
Learning: In the Cosmo codebase, database transactions are typically managed at the service layer (e.g., in buf services like deleteOrganizationGroup.ts), where repositories are instantiated with the transaction handle and all operations within those repositories are automatically part of the same transaction.

Applied to files:

  • controlplane/src/core/bufservices/proposal/enableProposalsForNamespace.ts
🧬 Code graph analysis (12)
controlplane/src/core/bufservices/proposal/createProposal.ts (1)
controlplane/src/core/repositories/ProposalRepository.ts (1)
  • ProposalRepository (21-845)
controlplane/src/core/bufservices/subgraph/publishFederatedSubgraph.ts (1)
controlplane/src/core/repositories/ProposalRepository.ts (1)
  • ProposalRepository (21-845)
controlplane/src/core/repositories/SubgraphRepository.ts (1)
controlplane/src/core/repositories/ProposalRepository.ts (1)
  • ProposalRepository (21-845)
controlplane/src/core/bufservices/proposal/getNamespaceProposalConfig.ts (1)
controlplane/src/core/repositories/ProposalRepository.ts (1)
  • ProposalRepository (21-845)
controlplane/src/core/bufservices/proposal/configureNamespaceProposalConfig.ts (1)
controlplane/src/core/repositories/ProposalRepository.ts (1)
  • ProposalRepository (21-845)
controlplane/src/core/bufservices/proposal/getProposalsByFederatedGraph.ts (1)
controlplane/src/core/repositories/ProposalRepository.ts (1)
  • ProposalRepository (21-845)
controlplane/src/core/bufservices/check/getCheckSummary.ts (1)
controlplane/src/core/repositories/ProposalRepository.ts (1)
  • ProposalRepository (21-845)
controlplane/src/core/bufservices/proposal/updateProposal.ts (1)
controlplane/src/core/repositories/ProposalRepository.ts (1)
  • ProposalRepository (21-845)
controlplane/src/core/bufservices/proposal/getProposalChecks.ts (2)
controlplane/src/core/repositories/ProposalRepository.ts (1)
  • ProposalRepository (21-845)
controlplane/src/core/repositories/FederatedGraphRepository.ts (1)
  • FederatedGraphRepository (98-1836)
controlplane/src/core/bufservices/subgraph/deleteFederatedSubgraph.ts (1)
controlplane/src/core/repositories/ProposalRepository.ts (1)
  • ProposalRepository (21-845)
controlplane/src/core/bufservices/proposal/enableProposalsForNamespace.ts (1)
controlplane/src/core/repositories/ProposalRepository.ts (1)
  • ProposalRepository (21-845)
controlplane/src/core/bufservices/proposal/getProposal.ts (1)
controlplane/src/core/repositories/ProposalRepository.ts (1)
  • ProposalRepository (21-845)
🔇 Additional comments (32)
controlplane/src/core/bufservices/subgraph/deleteFederatedSubgraph.ts (1)

38-38: LGTM! Organization-scoped repository instantiation.

The ProposalRepository is now correctly instantiated with the organization context by passing authContext.organizationId. This ensures that all proposal-related queries are scoped to the authenticated user's organization, implementing proper multi-tenant data isolation.

This change is consistent across all affected files in this PR and aligns with the security objective of validating organization ownership for proposal operations.

controlplane/src/core/bufservices/check/getCheckSummary.ts (1)

39-39: LGTM! Organization-scoped repository instantiation.

The ProposalRepository is now correctly instantiated with the organization context. This ensures proposal-related queries in the check summary flow are properly scoped to the authenticated organization.

controlplane/src/core/bufservices/proposal/createProposal.ts (1)

42-42: LGTM! Organization-scoped repository instantiation.

The ProposalRepository is now correctly instantiated with the organization context. This ensures that proposal creation operations are properly scoped to the authenticated organization, preventing cross-tenant data access.

controlplane/src/core/bufservices/subgraph/publishFederatedSubgraph.ts (1)

60-60: LGTM! Organization-scoped repository instantiation.

The ProposalRepository is now correctly instantiated with the organization context. This ensures that proposal matching during subgraph publishing is properly scoped to the authenticated organization.

controlplane/src/core/repositories/SubgraphRepository.ts (1)

1839-1839: LGTM! Organization-scoped repository instantiation.

The ProposalRepository is now correctly instantiated with the organization context within the performSchemaCheck method. This ensures that proposal matching during schema checks is properly scoped to the repository's organization.

controlplane/src/core/bufservices/proposal/getNamespaceProposalConfig.ts (1)

28-28: LGTM! Organization-scoped repository instantiation.

The ProposalRepository is now correctly instantiated with the organization context. This ensures that proposal configuration retrieval is properly scoped to the authenticated organization.

controlplane/src/core/bufservices/proposal/enableProposalsForNamespace.ts (1)

30-30: LGTM! Organization-scoped repository instantiation.

The ProposalRepository is now correctly instantiated with the organization context. This ensures that enabling/disabling proposals for a namespace is properly scoped to the authenticated organization.

controlplane/src/core/bufservices/proposal/configureNamespaceProposalConfig.ts (1)

31-31: LGTM! Organization-scoped repository instantiation.

The ProposalRepository is now correctly instantiated with the organization context. This ensures that configuring proposal settings is properly scoped to the authenticated organization.

controlplane/src/core/bufservices/proposal/updateProposal.ts (1)

41-41: Organization scoping correctly applied.

The ProposalRepository instantiation now includes authContext.organizationId, ensuring all proposal queries in this handler are properly scoped to the authenticated user's organization. This aligns with the IDOR fix pattern applied across the codebase.

controlplane/src/core/bufservices/proposal/getProposalChecks.ts (2)

25-27: Organization scoping correctly applied to repositories.

Both ProposalRepository and FederatedGraphRepository are now instantiated with the authenticated user's organizationId, ensuring data isolation.


42-52: Defense-in-depth validation for federated graph.

The additional federated graph lookup provides an extra layer of validation. Since proposalRepo.ById already filters by organizationId, this check serves as defense-in-depth—if the proposal passes the org filter, its associated federated graph should also exist within the same organization. This is a sound security pattern.

controlplane/src/core/bufservices/proposal/getProposalsByFederatedGraph.ts (2)

29-29: Organization scoping correctly applied.

The ProposalRepository instantiation now includes authContext.organizationId, ensuring proposal queries are properly scoped.


121-121: Simplified method signature aligns with repository refactor.

The getLatestCheckForProposal call no longer requires an explicit organizationId parameter since the repository now stores it as an instance member. This is cleaner and ensures consistent organization scoping across all repository methods.

controlplane/src/core/bufservices/proposal/getProposal.ts (3)

28-28: Organization scoping correctly applied.

The ProposalRepository instantiation now includes authContext.organizationId, ensuring all proposal queries are properly scoped to the authenticated user's organization.


46-46: Improved error message avoids leaking internal identifiers.

The error message now uses a generic description instead of including the federatedGraphId, which is a good security practice to avoid information leakage.


52-52: Simplified method signature aligns with repository refactor.

The getLatestCheckForProposal call now uses the repository's internal organizationId member instead of requiring it as a parameter.

controlplane/src/core/repositories/ProposalRepository.ts (9)

22-25: Core security fix: organization scoping via constructor.

Storing organizationId as an instance member and using it across all query methods is the right approach for enforcing multi-tenant data isolation. This ensures consistent organization scoping without requiring each method call to pass the parameter.


98-101: Organization filter correctly applied via join path.

The query now joins through federatedGraphstargets and filters by targets.organizationId, ensuring proposals from other organizations cannot be accessed even if an attacker guesses a valid proposal ID.


163-172: Organization filter correctly applied to ByName query.

The same join pattern through federatedGraphstargets with organizationId filter is consistently applied here.


229-260: Conditions array pattern with organization scoping.

Using (SQL<unknown> | undefined)[] for building dynamic conditions is a clean pattern. The organization filter is correctly included in the and() clause.


330-352: Count query consistently scoped by organization.

The count query mirrors the list query's organization scoping pattern, ensuring accurate counts within the tenant boundary.


438-444: Proposal config now organization-scoped.

The join to namespaces and filter by organizationId prevents cross-tenant access to namespace proposal configurations.


604-607: Linked checks query now uses repository's organizationId.

Using this.organizationId for the linked schema checks query ensures consistent organization scoping without requiring the caller to pass it explicitly.


802-806: Organization filter correctly applied to getProposalByCheckId.

The join path through federatedGraphstargets with organizationId filter prevents cross-tenant proposal lookups by check ID.


833-839: Organization filter correctly applied to proposal schema matches.

Consistent application of the organization scoping pattern for this query method.

controlplane/test/proposal/proposal-data-isolation.test.ts (7)

41-140: LGTM! Well-structured cross-organization access test.

This test properly validates that proposals from one organization cannot be accessed by another organization. The test setup, execution, and assertions are correct.


142-261: LGTM! Comprehensive cross-organization update test.

This test effectively validates that proposals from one organization cannot be modified by users from another organization. The inclusion of a "maliciousField" in the update attempt clearly demonstrates the security concern being addressed.


263-368: LGTM! Thorough listing isolation test.

This test properly validates that proposal listings are scoped to the organization. Creating multiple proposals (proposalNameA1 and proposalNameA2) in Company A ensures the test is comprehensive. The dual assertion checking both the error code and empty results list is appropriate.


370-469: LGTM! Proper isolation test for proposal checks.

This test validates that proposal checks cannot be accessed across organizations. The comment on line 404 helpfully clarifies that creating a proposal automatically creates a schema check, which is important context for understanding the test.


471-511: LGTM! Clear namespace configuration isolation test.

This test properly validates that namespace proposal configurations are organization-scoped. The comments clearly explain that Company B should see its own default namespace configuration (disabled) rather than Company A's configuration (enabled).


513-797: LGTM! Comprehensive federated graph isolation test.

This test thoroughly validates that proposals are properly isolated between different federated graphs within the same organization. It covers multiple operations (create, list, get, update) and includes an important negative test (lines 774-794) that verifies a proposal cannot be updated using a different federated graph name.


799-924: LGTM! Thorough state transition isolation test.

This test comprehensively validates that state transitions (approve and close) for proposals are properly isolated across organizations. The three negative test scenarios effectively cover different attack vectors:

  1. Using Company A's federated graph name (lines 887-897)
  2. Using Company B's federated graph name with Company A's proposal name (lines 899-909)
  3. Attempting to close the proposal (lines 911-921)

…09-wun-q425-15-insecure-direct-object-reference-via-proposals
@JivusAyrus JivusAyrus merged commit 395a5f5 into main Dec 4, 2025
45 checks passed
@JivusAyrus JivusAyrus deleted the suvij/eng-8609-wun-q425-15-insecure-direct-object-reference-via-proposals branch December 4, 2025 13:39
asoorm pushed a commit that referenced this pull request Dec 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants