Skip to content

Commit fde66ce

Browse files
committed
fix: Restrict CORS, add rate limiting, password validation, and IDOR check
- Narrow CORS regex to only match studyquest Vercel deployments - Add rate limiting to login (10/min) and signup (5/min) endpoints - Enforce password strength via Field constraints and existing validator - Add user ownership check on quiz result endpoint to prevent IDOR
1 parent a146681 commit fde66ce

3 files changed

Lines changed: 35 additions & 10 deletions

File tree

backend/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
app.add_middleware(
4242
CORSMiddleware,
4343
allow_origins=ALLOWED_ORIGINS,
44-
allow_origin_regex=r"https://.*\.vercel\.app", # Allow all Vercel preview deployments
44+
allow_origin_regex=r"https://study-?quest.*\.vercel\.app", # Allow only StudyQuest preview deployments
4545
allow_credentials=True,
4646
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
4747
allow_headers=["*"],

backend/routes/auth.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1-
from fastapi import APIRouter, HTTPException, status, Depends
2-
from pydantic import BaseModel, EmailStr
1+
from fastapi import APIRouter, HTTPException, status, Depends, Request
2+
from pydantic import BaseModel, EmailStr, Field
33
from config.supabase_client import get_supabase
44
from utils.auth import verify_user
5+
from utils.validation import validate_password_strength
6+
from slowapi import Limiter
7+
from slowapi.util import get_remote_address
8+
9+
# Initialize rate limiter
10+
limiter = Limiter(key_func=get_remote_address)
511

612
router = APIRouter(
713
prefix="/auth",
@@ -11,8 +17,8 @@
1117

1218
class SignUpRequest(BaseModel):
1319
email: EmailStr
14-
password: str
15-
20+
password: str = Field(..., min_length=8, max_length=128)
21+
1622
class Config:
1723
json_schema_extra = {
1824
"example": {
@@ -48,16 +54,25 @@ class UserResponse(BaseModel):
4854

4955

5056
@router.post("/signup", response_model=AuthResponse, status_code=status.HTTP_201_CREATED)
51-
async def signup(request: SignUpRequest):
57+
@limiter.limit("5/minute")
58+
async def signup(request: SignUpRequest, req: Request):
5259
"""
5360
Register a new user with email and password.
5461
5562
Creates a new user account in Supabase Auth and automatically creates
5663
a corresponding record in the users table via database trigger.
5764
"""
5865
try:
66+
# Validate password strength
67+
is_valid, message = validate_password_strength(request.password)
68+
if not is_valid:
69+
raise HTTPException(
70+
status_code=status.HTTP_400_BAD_REQUEST,
71+
detail=message
72+
)
73+
5974
supabase = get_supabase()
60-
75+
6176
# Sign up user with Supabase Auth
6277
response = supabase.auth.sign_up({
6378
"email": request.email,
@@ -80,6 +95,8 @@ async def signup(request: SignUpRequest):
8095
}
8196
}
8297

98+
except HTTPException:
99+
raise
83100
except Exception as e:
84101
# Handle specific Supabase errors
85102
error_message = str(e)
@@ -95,7 +112,8 @@ async def signup(request: SignUpRequest):
95112

96113

97114
@router.post("/login", response_model=AuthResponse)
98-
async def login(request: LoginRequest):
115+
@limiter.limit("10/minute")
116+
async def login(request: LoginRequest, req: Request):
99117
"""
100118
Authenticate user with email and password.
101119

backend/routes/study.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -928,13 +928,20 @@ async def get_quiz_result_by_id(
928928
supabase = get_supabase_client()
929929

930930
result = await get_quiz_result(supabase, quiz_id)
931-
931+
932932
if not result:
933933
raise HTTPException(
934934
status_code=404,
935935
detail="Quiz result not found"
936936
)
937-
937+
938+
# Verify the quiz result belongs to the requesting user
939+
if result.get("user_id") != current_user:
940+
raise HTTPException(
941+
status_code=403,
942+
detail="You do not have permission to view this quiz result"
943+
)
944+
938945
return result
939946

940947
except HTTPException:

0 commit comments

Comments
 (0)