@@ -11,33 +11,124 @@ service cloud.firestore {
1111 return isAuthenticated () && request .auth.uid == userId;
1212 }
1313
14+ // Strict string validation with length limits
1415 function isValidString (field , maxLength ) {
15- return field is string && field.size () <= maxLength;
16+ return field is string && field.size () > 0 && field .size () <= maxLength;
1617 }
1718
19+ function isOptionalString (field , maxLength ) {
20+ return field == null || (field is string && field .size () <= maxLength );
21+ }
22+
23+ // Strict timestamp validation (only actual timestamps, not strings)
1824 function isValidTimestamp (field ) {
19- return field is timestamp || field is string ;
25+ return field is timestamp;
26+ }
27+
28+ function isOptionalTimestamp (field ) {
29+ return field == null || field is timestamp;
30+ }
31+
32+ // Boolean validation
33+ function isValidBool (field ) {
34+ return field is bool;
35+ }
36+
37+ // Number validation with optional range
38+ function isValidNumber (field , min , max ) {
39+ return field is number && field >= min && field <= max;
40+ }
41+
42+ function isOptionalNumber (field , min , max ) {
43+ return field == null || (field is number && field >= min && field <= max );
44+ }
45+
46+ // Document size limit (1MB max, but we enforce 100KB for safety)
47+ function isReasonableSize () {
48+ return request .resource .size () < 100000 ;
49+ }
50+
51+ // Rate limiting helper - checks if enough time has passed since last update
52+ // Note: This requires lastUpdated field in documents
53+ function hasRateLimitPassed (minIntervalMs ) {
54+ return ! (' lastUpdated' in resource .data ) ||
55+ request .time .toMillis () - resource.data.lastUpdated .toMillis () >= minIntervalMs;
56+ }
57+
58+ // Validate todo item structure
59+ function isValidTodo (todo ) {
60+ return todo.keys ().hasAll ([' id' , ' text' , ' completed' ]) &&
61+ isValidString (todo .id , 50 ) &&
62+ isValidString (todo .text , 500 ) &&
63+ isValidBool (todo .completed );
64+ }
65+
66+ // Validate attendance record
67+ function isValidAttendanceRecord (record ) {
68+ return record.keys ().hasAll ([' date' , ' status' ]) &&
69+ isValidString (record .date , 10 ) &&
70+ record.status in [' office' , ' wfh' , ' leave' , ' holiday' ];
71+ }
72+
73+ // Validate note structure
74+ function isValidNote (note ) {
75+ return note.keys ().hasAll ([' id' , ' title' , ' content' ]) &&
76+ isValidString (note .id , 50 ) &&
77+ isOptionalString (note .title , 200 ) &&
78+ isOptionalString (note .content , 10000 );
79+ }
80+
81+ // Validate expense structure
82+ function isValidExpense (expense ) {
83+ return expense.keys ().hasAll ([' id' , ' amount' , ' category' ]) &&
84+ isValidString (expense .id , 50 ) &&
85+ isValidNumber (expense .amount , 0 , 100000000 ) &&
86+ isValidString (expense .category , 50 );
87+ }
88+
89+ // Validate leave request
90+ function isValidLeave (leave ) {
91+ return leave.keys ().hasAll ([' id' , ' type' , ' startDate' , ' endDate' , ' status' ]) &&
92+ isValidString (leave .id , 50 ) &&
93+ leave.type in [' casual' , ' sick' , ' earned' , ' unpaid' , ' compOff' , ' other' ] &&
94+ isValidString (leave .startDate , 10 ) &&
95+ isValidString (leave .endDate , 10 ) &&
96+ leave.status in [' pending' , ' approved' , ' rejected' ];
2097 }
2198
2299 // Users collection - each user can only access their own data
23100 match / users/ {userId } {
24101 // Allow read/write only to the owner
25- allow read , write : if isOwner (userId );
102+ allow read : if isOwner (userId );
103+ allow write : if isOwner (userId ) && isReasonableSize ();
26104
27105 // User data subcollection (todos, attendance, notes, etc.)
28106 match / data/ {document } {
29107 allow read : if isOwner (userId );
30108
31- // Validate writes based on document type
32- allow write : if isOwner (userId ) && validateDocument (document );
109+ // Validate writes based on document type with strict validation
110+ allow write : if isOwner (userId ) &&
111+ isReasonableSize () &&
112+ validateDocument (document );
33113 }
34114 }
35115
36- // Document validation function
116+ // Document validation function - validates document type names
37117 function validateDocument (docType ) {
38- return docType in [' todos' , ' attendance' , ' notes' , ' officeConfig' ,
39- ' gamification' , ' leaves' , ' trips' , ' expenses' ,
40- ' salary' , ' links' , ' meetings' , ' pomodoro' ];
118+ return docType in [
119+ ' todos' ,
120+ ' attendance' ,
121+ ' notes' ,
122+ ' officeConfig' ,
123+ ' gamification' ,
124+ ' leaves' ,
125+ ' trips' ,
126+ ' expenses' ,
127+ ' salaryStructure' ,
128+ ' quickLinks' ,
129+ ' meetings' ,
130+ ' pomodoroStats'
131+ ];
41132 }
42133
43134 // Deny all other access by default
0 commit comments