Skip to content

Commit 60652e6

Browse files
authored
fix(security): credential-set invite email check + shopify authorize XSS (#4302)
1 parent 8863f11 commit 60652e6

2 files changed

Lines changed: 20 additions & 2 deletions

File tree

  • apps/sim/app/api

apps/sim/app/api/auth/shopify/authorize/route.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ export const GET = withRouteHandler(async (request: NextRequest) => {
3232
const returnUrl = request.nextUrl.searchParams.get('returnUrl')
3333

3434
if (!shopDomain) {
35-
const returnUrlParam = returnUrl ? encodeURIComponent(returnUrl) : ''
35+
const safeReturnUrl =
36+
returnUrl && isSameOrigin(returnUrl) ? encodeURIComponent(returnUrl) : ''
37+
const returnUrlJsLiteral = JSON.stringify(safeReturnUrl)
3638
return new NextResponse(
3739
`<!DOCTYPE html>
3840
<html>
@@ -120,7 +122,7 @@ export const GET = withRouteHandler(async (request: NextRequest) => {
120122
</div>
121123
122124
<script>
123-
const returnUrl = '${returnUrlParam}';
125+
const returnUrl = ${returnUrlJsLiteral};
124126
function handleSubmit(e) {
125127
e.preventDefault();
126128
let shop = document.getElementById('shop').value.trim().toLowerCase();

apps/sim/app/api/credential-sets/invite/[token]/route.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { and, eq } from 'drizzle-orm'
1212
import { type NextRequest, NextResponse } from 'next/server'
1313
import { getSession } from '@/lib/auth'
1414
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
15+
import { normalizeEmail } from '@/lib/invitations/core'
1516
import { syncAllWebhooksForCredentialSet } from '@/lib/webhooks/utils.server'
1617

1718
const logger = createLogger('CredentialSetInviteToken')
@@ -111,6 +112,21 @@ export const POST = withRouteHandler(
111112
return NextResponse.json({ error: 'Invitation has expired' }, { status: 410 })
112113
}
113114

115+
if (invitation.email) {
116+
const sessionEmail = session.user.email
117+
if (!sessionEmail || normalizeEmail(sessionEmail) !== normalizeEmail(invitation.email)) {
118+
logger.warn('Rejected credential set invitation accept due to email mismatch', {
119+
invitationId: invitation.id,
120+
credentialSetId: invitation.credentialSetId,
121+
userId: session.user.id,
122+
})
123+
return NextResponse.json(
124+
{ error: 'This invitation was sent to a different email address' },
125+
{ status: 403 }
126+
)
127+
}
128+
}
129+
114130
const existingMember = await db
115131
.select()
116132
.from(credentialSetMember)

0 commit comments

Comments
 (0)