Merge commit from fork

This commit is contained in:
Max Peintner
2026-06-22 10:34:26 +02:00
committed by GitHub
parent 10087e7389
commit f691851d32
4 changed files with 64 additions and 4 deletions
+8 -2
View File
@@ -16,8 +16,14 @@ export function handleServerActionResponse(
}
if ("redirect" in response && response.redirect) {
router.push(response.redirect);
return true;
if (isSafeRedirectUri(response.redirect)) {
router.push(response.redirect);
return true;
} else {
console.warn("handleServerActionResponse: Blocked unsafe redirect URI:", response.redirect);
setError("Unsafe redirect URI was blocked");
return true;
}
}
if ("samlData" in response && response.samlData) {
+48
View File
@@ -188,4 +188,52 @@ describe("loginWithOIDCAndSession", () => {
expect(result).toEqual({ error: "Unknown error occurred" });
});
it("should reject javascript: defaultRedirectUri in FailedPrecondition path (XSS prevention)", async () => {
const { ConnectError } = await import("@zitadel/client");
vi.mocked(sessionModule.isSessionValid).mockResolvedValue(true);
vi.mocked(zitadelModule.createCallback).mockRejectedValue(new ConnectError("already handled", 9));
vi.mocked(zitadelModule.getLoginSettings).mockResolvedValue({
defaultRedirectUri: "javascript:alert(document.cookie)",
} as any);
const result = await loginWithOIDCAndSession({
serviceUrl: mockServiceUrl,
authRequest: mockAuthRequest,
sessionId: mockSessionId,
sessions: mockSessions,
sessionCookies: mockCookies,
});
// Should NOT redirect to the javascript: URI — must fall back to /signedin
expect(result).toHaveProperty("redirect");
if ("redirect" in result) {
expect(result.redirect).not.toContain("javascript:");
expect(result.redirect).toContain("/signedin");
}
});
it("should reject data: defaultRedirectUri in FailedPrecondition path (XSS prevention)", async () => {
const { ConnectError } = await import("@zitadel/client");
vi.mocked(sessionModule.isSessionValid).mockResolvedValue(true);
vi.mocked(zitadelModule.createCallback).mockRejectedValue(new ConnectError("already handled", 9));
vi.mocked(zitadelModule.getLoginSettings).mockResolvedValue({
defaultRedirectUri: "data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==",
} as any);
const result = await loginWithOIDCAndSession({
serviceUrl: mockServiceUrl,
authRequest: mockAuthRequest,
sessionId: mockSessionId,
sessions: mockSessions,
sessionCookies: mockCookies,
});
// Should NOT redirect to the data: URI — must fall back to /signedin
expect(result).toHaveProperty("redirect");
if ("redirect" in result) {
expect(result.redirect).not.toContain("data:");
expect(result.redirect).toContain("/signedin");
}
});
});
+4 -1
View File
@@ -1,3 +1,4 @@
import { isSafeRedirectUri } from "@/lib/client-utils";
import { Cookie } from "@/lib/cookies";
import { isClassifiedError } from "@/lib/grpc/interceptors/error-classification";
import { sendLoginname, SendLoginnameCommand } from "@/lib/server/loginname";
@@ -77,8 +78,10 @@ export async function loginWithOIDCAndSession({
organization: selectedSession.factors?.user?.organizationId,
});
if (loginSettings?.defaultRedirectUri) {
if (loginSettings?.defaultRedirectUri && isSafeRedirectUri(loginSettings.defaultRedirectUri)) {
return { redirect: loginSettings.defaultRedirectUri };
} else if (loginSettings?.defaultRedirectUri) {
console.warn("loginWithOIDCAndSession: Unsafe defaultRedirectUri prevented:", loginSettings.defaultRedirectUri);
}
const signedinUrl = "/signedin";
+4 -1
View File
@@ -1,3 +1,4 @@
import { isSafeRedirectUri } from "@/lib/client-utils";
import { Cookie } from "@/lib/cookies";
import { isClassifiedError } from "@/lib/grpc/interceptors/error-classification";
import { sendLoginname, SendLoginnameCommand } from "@/lib/server/loginname";
@@ -96,8 +97,10 @@ export async function loginWithSAMLAndSession({
organization: selectedSession.factors?.user?.organizationId,
});
if (loginSettings?.defaultRedirectUri) {
if (loginSettings?.defaultRedirectUri && isSafeRedirectUri(loginSettings.defaultRedirectUri)) {
return { redirect: loginSettings.defaultRedirectUri };
} else if (loginSettings?.defaultRedirectUri) {
console.warn("loginWithSAMLAndSession: Unsafe defaultRedirectUri prevented:", loginSettings.defaultRedirectUri);
}
const signedinUrl = "/signedin";