Skip to content

fix: replace deny-list with allow-list in getConnectedApps#28928

Open
avasis-ai wants to merge 1 commit intocalcom:mainfrom
avasis-ai:fix/getconnectedapps-allowlist
Open

fix: replace deny-list with allow-list in getConnectedApps#28928
avasis-ai wants to merge 1 commit intocalcom:mainfrom
avasis-ai:fix/getconnectedapps-allowlist

Conversation

@avasis-ai
Copy link
Copy Markdown

Summary

Fixes #28923

Replaced the deny-list destructuring approach in getConnectedApps.ts with an explicit allow-list that only exposes known-safe App metadata fields to the frontend.

Problem

The previous implementation used:

({ credentials: _, credential, key: _2, ...app }) => { ... return { ...app } }

This deny-list approach implicitly exposes all non-excluded properties via the spread operator. Any new fields added to the app data (e.g., internal configs, metadata) would be automatically leaked to the frontend. The code even had a TODO acknowledging this issue.

Changes

  • Replaced ...app spread in the return with explicit field picks for all safe App type fields
  • Removed the TODO comment
  • Sensitive fields (credentials, credential, key, locationOption) are no longer destructured into the app variable at all
  • All existing fields used downstream (slug, categories, dirName, isGlobal, dependencies, variant, extendsFeature, etc.) are explicitly included

Security Impact

This prevents accidental exposure of:

  • Any future internal fields added to the app data model
  • The locationOption field that was previously leaked
  • Any database-level fields not intended for frontend consumption

Replace the spread-based deny-list approach that excluded specific
sensitive fields (credentials, key) with an explicit allow-list that
only exposes known safe App metadata fields. This prevents potential
data leakage if new internal fields are added in the future.

Fixes calcom#28923
@github-actions github-actions bot added the 🐛 bug Something isn't working label Apr 18, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Welcome to Cal.diy, @avasis-ai! Thanks for opening this pull request.

A few things to keep in mind:

  • This is Cal.diy, not Cal.com. Cal.diy is a community-driven, fully open-source fork of Cal.com licensed under MIT. Your changes here will be part of Cal.diy — they will not be deployed to the Cal.com production app.
  • Please review our Contributing Guidelines if you haven't already.
  • Make sure your PR title follows the Conventional Commits format.

A maintainer will review your PR soon. Thanks for contributing!

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 18, 2026

📝 Walkthrough

Walkthrough

The getConnectedApps.ts utility function was refactored in its per-app mapping logic. The destructuring pattern changed from removing specific fields from an app object to explicitly extracting appCredentials, credential, and a rest object from enabledApp. All subsequent field references were updated from app.* to rest.* throughout the function. The returned object structure changed from spreading the entire app object to explicitly enumerating fields from rest. Computed fields including credentialOwner, teams, userCredentialIds, invalidCredentialIds, isInstalled, isSetupAlready, and conditional dependencyData are preserved.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely describes the main change: replacing a deny-list with an allow-list approach in the getConnectedApps function.
Description check ✅ Passed The description provides relevant context about the change, explaining both the problem and the solution, and links directly to the related issue.
Linked Issues check ✅ Passed The PR implementation directly addresses all requirements from issue #28923: it replaces the deny-list pattern with an explicit allow-list, prevents implicit leakage of properties, removes the TODO comment, and ensures sensitive fields are not exposed.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the stated objective of converting from a deny-list to an allow-list pattern in getConnectedApps.ts; no unrelated modifications are present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
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.

🧹 Nitpick comments (1)
packages/app-store/_utils/getConnectedApps.ts (1)

202-246: Consider extracting the allow-list mapper into a typed helper to reduce drift risk.

The inline allow-list is correct, but long. A small helper (e.g., toPublicConnectedApp(rest)) with an explicit return type would make future field changes safer and easier to review.

♻️ Proposed refactor
+type PublicConnectedApp = {
+  slug: string;
+  name: string;
+  description: string;
+  logo: string;
+  variant: string;
+  categories: AppCategories[];
+  extendsFeature: "EventType" | null;
+  publisher: string;
+  url: string;
+  docsUrl: string | null;
+  verified: boolean;
+  trending: boolean;
+  rating: number | null;
+  reviews: number | null;
+  isGlobal: boolean;
+  simplePath: string | null;
+  email: string | null;
+  feeType: string | null;
+  price: number | null;
+  commission: number | null;
+  licenseRequired: boolean;
+  appData: unknown;
+  paid: boolean;
+  dirName: string;
+  isTemplate: boolean;
+  dependencies: string[] | null;
+  concurrentMeetings: boolean;
+  createdAt: Date;
+  isOAuth: boolean;
+  type: string;
+  installed: boolean;
+  title: string;
+  delegationCredential: boolean;
+  enabled: boolean;
+};
+
+const toPublicConnectedApp = (rest: typeof enabledApps[number]): PublicConnectedApp => ({
+  slug: rest.slug,
+  name: rest.name,
+  description: rest.description,
+  logo: rest.logo,
+  variant: rest.variant,
+  categories: rest.categories,
+  extendsFeature: rest.extendsFeature,
+  publisher: rest.publisher,
+  url: rest.url,
+  docsUrl: rest.docsUrl,
+  verified: rest.verified,
+  trending: rest.trending,
+  rating: rest.rating,
+  reviews: rest.reviews,
+  isGlobal: rest.isGlobal,
+  simplePath: rest.simplePath,
+  email: rest.email,
+  feeType: rest.feeType,
+  price: rest.price,
+  commission: rest.commission,
+  licenseRequired: rest.licenseRequired,
+  appData: rest.appData,
+  paid: rest.paid,
+  dirName: rest.dirName,
+  isTemplate: rest.isTemplate,
+  dependencies: rest.dependencies,
+  concurrentMeetings: rest.concurrentMeetings,
+  createdAt: rest.createdAt,
+  isOAuth: rest.isOAuth,
+  type: rest.type,
+  installed: rest.installed,
+  title: rest.title,
+  delegationCredential: rest.delegationCredential,
+  enabled: rest.enabled,
+});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/app-store/_utils/getConnectedApps.ts` around lines 202 - 246,
Extract the long inline allow-list object in getConnectedApps into a typed
helper function (e.g., toPublicConnectedApp(rest: ConnectedAppRaw):
PublicConnectedApp) and replace the current inline return with a call to that
helper; the helper should have an explicit return type, list the same fields
currently returned (slug, name, description, logo, variant, categories,
extendsFeature, publisher, url, docsUrl, verified, trending, rating, reviews,
isGlobal, simplePath, email, feeType, price, commission, licenseRequired,
appData, paid, dirName, isTemplate, dependencies, concurrentMeetings, createdAt,
isOAuth, type, installed, title, delegationCredential, enabled,
userCredentialIds, invalidCredentialIds, teams, isSetupAlready, dependencyData
when rest.dependencies, and credentialOwner when teams.length), preserve the
computed fields (isInstalled: !!userCredentialIds.length || !!teams.length ||
rest.isGlobal) and conditional spreads for teams and dependencies, and update
callers to use the new toPublicConnectedApp helper so future field drift is
caught by the explicit PublicConnectedApp type.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/app-store/_utils/getConnectedApps.ts`:
- Around line 202-246: Extract the long inline allow-list object in
getConnectedApps into a typed helper function (e.g., toPublicConnectedApp(rest:
ConnectedAppRaw): PublicConnectedApp) and replace the current inline return with
a call to that helper; the helper should have an explicit return type, list the
same fields currently returned (slug, name, description, logo, variant,
categories, extendsFeature, publisher, url, docsUrl, verified, trending, rating,
reviews, isGlobal, simplePath, email, feeType, price, commission,
licenseRequired, appData, paid, dirName, isTemplate, dependencies,
concurrentMeetings, createdAt, isOAuth, type, installed, title,
delegationCredential, enabled, userCredentialIds, invalidCredentialIds, teams,
isSetupAlready, dependencyData when rest.dependencies, and credentialOwner when
teams.length), preserve the computed fields (isInstalled:
!!userCredentialIds.length || !!teams.length || rest.isGlobal) and conditional
spreads for teams and dependencies, and update callers to use the new
toPublicConnectedApp helper so future field drift is caught by the explicit
PublicConnectedApp type.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4c0e24e3-8ab2-4765-96c7-0e0ff12d05a5

📥 Commits

Reviewing files that changed from the base of the PR and between 9efd0e6 and 6f4d760.

📒 Files selected for processing (1)
  • packages/app-store/_utils/getConnectedApps.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐛 bug Something isn't working size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🔒 Security/Refactor: Prevent potential data leak in getConnectedApps by explicitly picking safe fields

2 participants