diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 0000000000..8e327b4723 --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,9 @@ +## 2025-05-14 - [Insecure postMessage Target Origin in OAuth Redirect] +**Vulnerability:** The OAuth `/redirect` endpoint used `window.postMessage` with a wildcard (`'*'`) target origin, allowing any origin to intercept the authorization code. +**Learning:** In a multi-platform environment like Activepieces, the correct target origin must be resolved dynamically using the platform context to ensure the code is only sent to the trusted frontend. +**Prevention:** Always use `platformUtils.getPlatformIdForRequest(request)` and `domainHelper.getPublicUrl({ platformId })` to resolve the specific origin for `postMessage`. + +## 2025-05-14 - [Improper Origin Verification in Frontend OAuth Utility] +**Vulnerability:** The frontend `getCode` function used `startsWith` to verify the origin of `postMessage` events, which could be bypassed (e.g., `https://trusted.com.malicious.com` starts with `https://trusted.com`). +**Learning:** `startsWith` is insufficient for origin verification. +**Prevention:** Always use strict equality (`===`) for origin comparison and extract the origin component correctly using `new URL(url).origin`. diff --git a/packages/react-ui/src/lib/oauth2-utils.ts b/packages/react-ui/src/lib/oauth2-utils.ts index 7b9ca004e9..f65ec9d5f8 100644 --- a/packages/react-ui/src/lib/oauth2-utils.ts +++ b/packages/react-ui/src/lib/oauth2-utils.ts @@ -72,11 +72,12 @@ function constructUrl(params: OAuth2PopupParams, pckeChallenge: string) { } function getCode(redirectUrl: string): Promise { + const expectedOrigin = new URL(redirectUrl).origin; return new Promise((resolve) => { window.addEventListener('message', function handler(event) { if ( redirectUrl && - redirectUrl.startsWith(event.origin) && + event.origin === expectedOrigin && event.data['code'] ) { resolve(decodeURIComponent(event.data.code)); diff --git a/packages/server/api/src/app/app.ts b/packages/server/api/src/app/app.ts index 671220419f..ed9f5918ae 100644 --- a/packages/server/api/src/app/app.ts +++ b/packages/server/api/src/app/app.ts @@ -82,6 +82,7 @@ import { pieceMetadataServiceHooks } from './pieces/piece-metadata-service/hooks import { pieceSyncService } from './pieces/piece-sync-service' import { platformModule } from './platform/platform.module' import { platformService } from './platform/platform.service' +import { platformUtils } from './platform/platform.utils' import { projectHooks } from './project/project-hooks' import { projectModule } from './project/project-module' import { storeEntryModule } from './store-entry/store-entry.module' @@ -250,12 +251,15 @@ export const setupApp = async (app: FastifyInstance): Promise = return reply.send('The code is missing in url') } else { + const platformId = await platformUtils.getPlatformIdForRequest(request) + const publicUrl = await domainHelper.getPublicUrl({ platformId }) + const targetOrigin = new URL(publicUrl).origin return reply .type('text/html') .send( ` Redirect succuesfully, this window should close now`, + )}' }, '${targetOrigin}')} Redirect successfully, this window should close now`, ) } },