Issue
After reviewing the code, I found that canonforces uses Firebase Firestore for storing:
- User profiles and coin balances
- Problem submissions and completion status
- Weekly POTD solve counts and streaks
- Problem data (descriptions, test cases, difficulty)
Key concerns observed:
-
Problem data in Firestore is world-writable - The QuestionBar component in src/pages/questions/[id].tsx reads problems from Firestore using getDoc(doc(db, 'questions', id)) but also uses increment(), arrayUnion(), and direct updateDoc() calls from the client side for:
- Coin balance changes
- Hint tracking
- Submission counts
-
No server-side validation of game state - Coins and streaks are modified entirely client-side:
setUserData with Firestore writes happen directly from the browser
- A malicious user can modify coin balance in real-time through browser DevTools
-
Hint cost defined client-side (HINT_COST = 10) - The cost is just a constant in the React component, not enforced by the database
Fix
The safest approach for a competitive programming platform with gamification:
-
Use Firebase Cloud Functions for all game-state mutations:
- User clicks "Get Hint" -> POST to Cloud Function -> Function verifies coins, deducts, returns hint
- POTD solved -> Function verifies submission with Codeforces API -> Updates user record
-
Set strict Firestore Security Rules:
match /users/{userId} {
allow read: if request.auth != null;
allow write: if false; // Never allow clients to write user data directly
}
match /questions/{questionId} {
allow read: if true;
allow write: if false; // Only admin or Cloud Functions can modify
}
Issue
After reviewing the code, I found that canonforces uses Firebase Firestore for storing:
Key concerns observed:
Problem data in Firestore is world-writable - The
QuestionBarcomponent insrc/pages/questions/[id].tsxreads problems from Firestore usinggetDoc(doc(db, 'questions', id))but also usesincrement(),arrayUnion(), and directupdateDoc()calls from the client side for:No server-side validation of game state - Coins and streaks are modified entirely client-side:
setUserDatawith Firestore writes happen directly from the browserHint cost defined client-side (
HINT_COST = 10) - The cost is just a constant in the React component, not enforced by the databaseFix
The safest approach for a competitive programming platform with gamification:
Use Firebase Cloud Functions for all game-state mutations:
Set strict Firestore Security Rules: