-
Notifications
You must be signed in to change notification settings - Fork 0
Security
Felines was designed around a unique risk: the product maps locations of vulnerable animals. Security here isn't just data protection — it's physical protection of the cats.
Full audit report: AUDIT_REPORT.md
The most important security feature in the product — designed to protect cats, not just user data.
| Level | User | Precision | Offset |
|---|---|---|---|
| 1 | Anonymous | Neighborhood | ~600m |
| 2 | Authenticated | Street level | ~110m |
| 3 | Linked caretaker | Exact | Via RPC only |
Implementation:
- Database stores both exact and blurred coordinates
- Exact columns: RLS protected, anon key returns
42501 permission denied - Blurred columns: intentionally public
- Exact coordinates only via
get_colony_exact_locationRPC, which validates caretaker status server-side
Tested: SELECT latitude FROM colonies with anon key → 42501 permission denied ✅
RLS enabled on all 17 tables. Every policy was tested with direct API calls using the anon key.
Critical policies:
| Table | Anonymous | Authenticated | Caretaker |
|---|---|---|---|
| colonies | Read blurred only | Read blurred | Read exact via RPC |
| reports | No access | Read (sensitive data) | Read + update |
| knowledge_progress | No access | Own rows only | Own rows only |
| profiles | Read public fields | Own full row | Own full row |
| feedings | Read | Read + insert | Read + insert |
| flags | No access | Insert only | Insert only |
Problem: File uploads used unsanitized path prefixes.
../ sequences could write files outside the intended
storage folder.
Affected files:
components/EditColonyForm.tsxcomponents/ProfileContent.tsxcomponents/ShareStoryButton.tsxcomponents/TimelineEventForm.tsx
Fix: Centralized buildSafeStoragePath() in lib/storage.ts:
- Removes
../sequences - Removes leading slashes
- Allows only safe characters in prefix
- UUID-based filename (no user input in the path)
- Type validation (images only) + size limit (max 5MB)
Commit: security: fix path traversal vulnerability in file uploads — Aikido report
Problem: Nominatim API received lat/lon interpolated directly into the URL string without geographic range validation.
Fix:
-
lib/validateCoordinates.ts: validates lat -90/+90, lon -180/+180, rejects NaN/Infinity -
lib/geocode.ts: URLSearchParams instead of string interpolation,redirect: "error" - Response shape validated before returning to client
- Bonus: Fixed bug where geocoding always returned Portuguese regardless of user language
Commit: security: fix SSRF vulnerability in geocoding requests — Aikido report
Configured in next.config.ts:
| Header | Value | Protection |
|---|---|---|
| X-Frame-Options | DENY | Clickjacking |
| X-Content-Type-Options | nosniff | MIME sniffing |
| Referrer-Policy | strict-origin-when-cross-origin | URL leakage |
| Content-Security-Policy | (configured) | XSS, injection |
| Permissions-Policy | camera=(), microphone=(), geolocation=(self) | Browser permissions |
- Anonymous users: max 10 reports per IP per hour
- Authenticated users: max 30 reports per IP per hour
- Friendly message shown when limit is reached
- Automatic reset after 1 hour
- All fields validated client-side AND server-side
- Field size limits on all database columns (migration 0067)
- File uploads: type check + 5MB max before upload
- No
dangerouslySetInnerHTMLwith user content - Coordinate validation before any external API call
| Migration | Fix |
|---|---|
| 0044 | Critical RPCs reject anon key |
| 0045 | Secondary RPCs reject anon key |
| 0065 | Notification RPCs hardened against spam injection |
| 0066 | Streaks restricted to own user |
| 0067 | Field size limits added to all columns |
| 0068 | Self-thanking blocked |
| 0069 | Unique constraint on knowledge_progress |
| 0070 | Flag deduplication |
Several product decisions were made specifically to protect the physical safety of the cats — not just user data:
- No people reporting — prevents digital vigilantism
- 3-confirmation threshold — prevents one user from hiding serious reports
- Sensitive reports never deleted — permanent evidence trail
- Colony sharing disabled — direct links lower the barrier for malicious access
- UUID filenames — original user input never reaches storage paths
- Exact coordinates only via server-side RPC — never in any public API response
See SECURITY.md for the full responsible disclosure policy.