fix: replace deny-list with allow-list in getConnectedApps#28928
fix: replace deny-list with allow-list in getConnectedApps#28928avasis-ai wants to merge 1 commit intocalcom:mainfrom
Conversation
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
|
Welcome to Cal.diy, @avasis-ai! Thanks for opening this pull request. A few things to keep in mind:
A maintainer will review your PR soon. Thanks for contributing! |
📝 WalkthroughWalkthroughThe 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
🧹 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
📒 Files selected for processing (1)
packages/app-store/_utils/getConnectedApps.ts
Summary
Fixes #28923
Replaced the deny-list destructuring approach in
getConnectedApps.tswith an explicit allow-list that only exposes known-safe App metadata fields to the frontend.Problem
The previous implementation used:
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
TODOacknowledging this issue.Changes
...appspread in the return with explicit field picks for all safeApptype fieldscredentials,credential,key,locationOption) are no longer destructured into the app variable at allSecurity Impact
This prevents accidental exposure of:
locationOptionfield that was previously leaked