Skip to content

feat(ui): add app connection id permission condition to secret syncs#5325

Merged
IgorHorta merged 14 commits intomainfrom
igor/platfrm-175-add-app-connection-id-permission-condition-to-secret-syncs
Jan 31, 2026
Merged

feat(ui): add app connection id permission condition to secret syncs#5325
IgorHorta merged 14 commits intomainfrom
igor/platfrm-175-add-app-connection-id-permission-condition-to-secret-syncs

Conversation

@IgorHorta
Copy link
Contributor

@IgorHorta IgorHorta commented Jan 30, 2026

Add app connection Id permission condition to secret syncs and rotations

Linear: PLATFRM-175

Context

Admins can now restrict access to secret syncs and rotations based on their app connection. This allows hiding sensitive syncs/rotations that use org-level app connections from sub-users.

Changes:

  • Added connectionId condition to secret-syncs and secret-rotation permission schemas
  • Enforced per-sync/per-rotation permission checks including connectionId in services
  • Added $GLOB operator support to SecretRotationConditionSchema environment field for consistency
  • Updated docs to document connectionId condition for both subjects

Before: Users with Read on secret-syncs saw all syncs in the project.

After: Users only see syncs/rotations they have permission for based on environment, secretPath, and connectionId conditions.

Screenshots

image image

Steps to verify the change

Test: connectionId-based filtering

  1. Create two secret syncs using different org-level app connections (Sync A with conn-1, Sync B with conn-2)
  2. Create a custom role with Read on secret-syncs only when connectionId = conn-1 (or use permission inversion to deny conn-2)
  3. Assign role to a sub-user
  4. As sub-user, list secret syncs via API
  5. Verify: User only sees Sync A (not Sync B)
  6. As sub-user, try to get Sync B by ID
  7. Verify: Returns 403 Forbidden
  8. As admin, list syncs
  9. Verify: Admin sees both syncs

Regression test

  1. Assign unconditional Read on secret-syncs to a user
  2. Verify: User sees all syncs (no change in behavior)

Type

  • Fix
  • Feature
  • Improvement
  • Breaking
  • Docs
  • Chore

Checklist

  • Title follows the conventional commit format: type(scope): short description (scope is optional, e.g. fix: prevent crash on sync or fix(api): handle null response).
  • Tested locally
  • Updated docs (if needed)
  • Read the contributing guide

IgorHorta and others added 2 commits January 29, 2026 18:24
…et syncs and secret rotations

- Backend: add connectionId to SecretSync/SecretRotation subject types and condition schemas
- Backend: pass connectionId in all secret-sync and secret-rotation-v2 permission checks
- Frontend: add Connection ID condition option for Secret Syncs and Secret Rotation in role permissions
- Docs: document connectionId condition for secret-syncs and secret-rotation in project-permissions.mdx
- Add unit tests for schema validation and CASL can() with connectionId condition

Co-authored-by: Cursor <cursoragent@cursor.com>
@maidul98
Copy link
Collaborator

maidul98 commented Jan 30, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@IgorHorta IgorHorta changed the title Igor/platfrm 175 add app connection id permission condition to secret syncs feat(ui): add app connection id permission condition to secret syncs Jan 30, 2026
@IgorHorta IgorHorta marked this pull request as ready for review January 30, 2026 15:48
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Jan 30, 2026

Greptile Overview

Greptile Summary

This PR adds connectionId as a new permission condition to secret syncs and secret rotations, allowing admins to restrict access based on which app connection is used. This enables hiding sensitive syncs/rotations that use org-level connections from sub-users.

Key Changes:

  • Added connectionId field to SecretSyncSubjectFields and SecretRotationsSubjectFields type definitions
  • Created new SecretRotationConditionSchema with $GLOB operator support for environment field
  • Updated SecretSyncConditionV2Schema to include connectionId with $EQ, $NEQ, and $IN operators
  • Enforced per-sync/per-rotation permission checks throughout both services, including connectionId in permission subjects
  • Updated frontend UI to expose connectionId as a selectable condition in role permission configuration
  • Documented the new connectionId condition for both subjects in project permissions docs

Critical Issue Found:

  • Permission bypass vulnerability in secret-sync-service.ts updateSecretSync function: when updating a sync's connectionId, the permission check validates against the OLD connection ID, not the NEW one. This allows users to escalate their access to syncs they shouldn't have permission for by changing the connection ID.

Positive Notes:

  • Secret rotation correctly prevents connectionId updates by excluding it from TUpdateSecretRotationV2DTO
  • Permission filtering is properly implemented in list operations
  • All read, delete, and action operations include the connectionId in permission checks
  • Documentation is thorough and clear

Confidence Score: 2/5

  • This PR has a critical permission bypass vulnerability that must be fixed before merging
  • Score reflects a critical security flaw in the updateSecretSync function that allows unauthorized access escalation by changing connection IDs without proper permission validation. The rest of the implementation is solid, but this vulnerability poses significant security risk.
  • backend/src/services/secret-sync/secret-sync-service.ts requires immediate attention to fix the permission bypass vulnerability in the updateSecretSync function

Important Files Changed

Filename Overview
backend/src/services/secret-sync/secret-sync-service.ts Added connectionId permission checks to all operations, but missing permission check when updating to a new connectionId
backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-service.ts Added connectionId to all permission checks consistently; update DTO correctly excludes connectionId from updates
backend/src/ee/services/permission/project-permission.ts Added connectionId field to schemas with proper operators; includes $GLOB operator for rotation environments

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

3 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Jan 30, 2026

Additional Comments (2)

backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-service.ts
The unconditional permission check should be removed here (lines 261-264) to match the behavior implemented in secret-sync-service.ts:95-98 where the unconditional check was removed. According to the PR description, the behavior change is: "We no longer require unconditional Read at list time. We load all syncs for the project and filter each one with the permission subject that includes environment, secretPath, and connectionId." This service still requires unconditional Read permission before filtering by connectionId, which defeats the purpose of conditional permissions.

    const secretRotations = await secretRotationV2DAL.find({

backend/src/services/secret-sync/secret-sync-service.ts
Removing the unconditional permission check enables proper conditional permission filtering by connectionId. However, this creates a potential security issue: a user with NO permissions at all on secret-syncs can bypass this check (line 95-98) and proceed to load ALL syncs from the database (line 100-103), then filter them (line 108-119). If the user has zero permissions, they should be denied immediately without loading any data. Consider adding a check to verify the user has at least SOME permission on secret-syncs (conditional or unconditional) before proceeding.

Example attack: A user with zero secret-syncs permissions could still trigger database queries to load all syncs, potentially causing performance issues or revealing timing information about the number of syncs in the project.

@IgorHorta
Copy link
Contributor Author

@greptile review again plz!

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

3 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Jan 30, 2026

Additional Comments (1)

backend/src/services/secret-sync/secret-sync-service.ts
Critical Security Issue: Permission bypass when updating connectionId

The permission checks use the OLD connectionId from the existing sync, but when params.connectionId is provided (line 502-511), the update allows changing to a NEW connection without verifying permission against that new connectionId.

Attack scenario:

  1. User has Read/Edit on syncs with connectionId=conn-1
  2. User does NOT have access to connectionId=conn-2
  3. User calls update with params.connectionId=conn-2
  4. Permission check at line 436-443 passes (checks OLD conn-1)
  5. Line 502-511 only validates the connection exists
  6. User successfully updates sync to use conn-2, bypassing permission restrictions

Fix required:
When params.connectionId is provided, add a permission check against the NEW connection:

if (params.connectionId && params.connectionId !== connectionId) {
  // Check permission against NEW connectionId
  ForbiddenError.from(permission).throwUnlessCan(
    ProjectPermissionSecretSyncActions.Edit,
    secretSync.environment?.slug && secretSync.folder?.path
      ? subject(ProjectPermissionSub.SecretSyncs, {
          environment: secretSync.environment.slug,
          secretPath: secretSync.folder.path,
          connectionId: params.connectionId
        })
      : ProjectPermissionSub.SecretSyncs
  );
}

Note: Secret rotation correctly prevents this by excluding connectionId from TUpdateSecretRotationV2DTO.

@IgorHorta IgorHorta requested a review from akhilmhdh January 30, 2026 22:40
Copy link
Contributor

@scott-ray-wilson scott-ray-wilson left a comment

Choose a reason for hiding this comment

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

last thing, always want to check connection id

…lint in detail components

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Contributor

@scott-ray-wilson scott-ray-wilson left a comment

Choose a reason for hiding this comment

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

LGTM, tested rotations and syncs granularly

… sync components

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Contributor

@scott-ray-wilson scott-ray-wilson left a comment

Choose a reason for hiding this comment

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

LGTM, I granularly tested sync and rotation permissions

@IgorHorta IgorHorta merged commit 9257c78 into main Jan 31, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants