mirror of
https://github.com/formbricks/formbricks.git
synced 2026-03-19 00:23:35 -05:00
Compare commits
1 Commits
chore/impr
...
codex/pass
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d08c93dd6c |
@@ -2,6 +2,7 @@
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import { SubmitHandler, useForm } from "react-hook-form";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -33,6 +34,7 @@ export const ResetPasswordForm = () => {
|
||||
const { t } = useTranslation();
|
||||
const searchParams = useSearchParams();
|
||||
const router = useRouter();
|
||||
const [token, setToken] = useState("");
|
||||
|
||||
const form = useForm<TPasswordResetForm>({
|
||||
defaultValues: {
|
||||
@@ -42,12 +44,24 @@ export const ResetPasswordForm = () => {
|
||||
resolver: zodResolver(ZPasswordResetForm),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const resetToken = searchParams?.get("token");
|
||||
if (!resetToken) {
|
||||
return;
|
||||
}
|
||||
|
||||
setToken((currentToken) => currentToken || resetToken);
|
||||
|
||||
// Remove the token from the address bar after the page has loaded.
|
||||
const sanitizedUrl = `${window.location.pathname}${window.location.hash}`;
|
||||
window.history.replaceState(window.history.state, "", sanitizedUrl);
|
||||
}, [searchParams]);
|
||||
|
||||
const handleSubmit: SubmitHandler<TPasswordResetForm> = async (data) => {
|
||||
if (data.password !== data.confirmPassword) {
|
||||
toast.error(t("auth.forgot-password.reset.passwords_do_not_match"));
|
||||
return;
|
||||
}
|
||||
const token = searchParams?.get("token");
|
||||
if (!token) {
|
||||
toast.error(t("auth.forgot-password.reset.no_token_provided"));
|
||||
return;
|
||||
@@ -94,7 +108,7 @@ export const ResetPasswordForm = () => {
|
||||
<div>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={!form.formState.isValid}
|
||||
disabled={!form.formState.isValid || !token}
|
||||
className="w-full justify-center"
|
||||
loading={form.formState.isSubmitting}>
|
||||
{t("auth.forgot-password.reset_password")}
|
||||
|
||||
@@ -234,6 +234,15 @@ const nextConfig = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
source: "/auth/forgot-password/reset",
|
||||
headers: [
|
||||
{
|
||||
key: "Referrer-Policy",
|
||||
value: "no-referrer",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
source: "/js/(.*)",
|
||||
headers: [
|
||||
|
||||
@@ -8,27 +8,20 @@ test.describe("Authentication Security Tests - Vulnerability Prevention", () =>
|
||||
let csrfToken: string;
|
||||
let testUser: { email: string; password: string };
|
||||
|
||||
test.beforeEach(async ({ request, users }) => {
|
||||
// Get CSRF token for authentication requests
|
||||
const csrfResponse = await request.get("/api/auth/csrf");
|
||||
const csrfData = await csrfResponse.json();
|
||||
csrfToken = csrfData.csrfToken;
|
||||
test("should disable referrers on the password reset page", async ({ request }) => {
|
||||
const response = await request.get("/auth/forgot-password/reset?token=test-token");
|
||||
|
||||
// Create a test user for "existing user" scenarios with unique email
|
||||
const uniqueId = Date.now() + Math.random();
|
||||
const userName = "Security Test User";
|
||||
const userEmail = `security-test-${uniqueId}@example.com`;
|
||||
await users.create({
|
||||
name: userName,
|
||||
email: userEmail,
|
||||
});
|
||||
testUser = {
|
||||
email: userEmail,
|
||||
password: userName, // The fixture uses the name as password
|
||||
};
|
||||
expect(response.status()).toBe(200);
|
||||
expect(response.headers()["referrer-policy"]).toBe("no-referrer");
|
||||
});
|
||||
|
||||
test.describe("DoS Protection - Password Length Limits", () => {
|
||||
test.beforeEach(async ({ request }) => {
|
||||
const csrfResponse = await request.get("/api/auth/csrf");
|
||||
const csrfData = await csrfResponse.json();
|
||||
csrfToken = csrfData.csrfToken;
|
||||
});
|
||||
|
||||
test("should handle extremely long passwords without crashing", async ({ request }) => {
|
||||
const email = "nonexistent-dos-test@example.com"; // Use non-existent email for DoS test
|
||||
const extremelyLongPassword = "A".repeat(50000); // 50,000 characters
|
||||
@@ -126,6 +119,24 @@ test.describe("Authentication Security Tests - Vulnerability Prevention", () =>
|
||||
});
|
||||
|
||||
test.describe("Timing Attack Prevention - User Enumeration Protection", () => {
|
||||
test.beforeEach(async ({ request, users }) => {
|
||||
const csrfResponse = await request.get("/api/auth/csrf");
|
||||
const csrfData = await csrfResponse.json();
|
||||
csrfToken = csrfData.csrfToken;
|
||||
|
||||
const uniqueId = Date.now() + Math.random();
|
||||
const userName = "Security Test User";
|
||||
const userEmail = `security-test-${uniqueId}@example.com`;
|
||||
await users.create({
|
||||
name: userName,
|
||||
email: userEmail,
|
||||
});
|
||||
testUser = {
|
||||
email: userEmail,
|
||||
password: userName, // The fixture uses the name as password
|
||||
};
|
||||
});
|
||||
|
||||
test("should not reveal user existence through response timing differences", async ({ request }) => {
|
||||
// Helper functions for statistical analysis
|
||||
const calculateMedian = (values: number[]): number => {
|
||||
@@ -359,6 +370,12 @@ test.describe("Authentication Security Tests - Vulnerability Prevention", () =>
|
||||
});
|
||||
|
||||
test.describe("Security Headers and Response Safety", () => {
|
||||
test.beforeEach(async ({ request }) => {
|
||||
const csrfResponse = await request.get("/api/auth/csrf");
|
||||
const csrfData = await csrfResponse.json();
|
||||
csrfToken = csrfData.csrfToken;
|
||||
});
|
||||
|
||||
test("should include security headers in responses", async ({ request }) => {
|
||||
const response = await request.post("/api/auth/callback/credentials", {
|
||||
data: {
|
||||
|
||||
Reference in New Issue
Block a user