added the elmeent on the nav and fixed the payments transactions
All checks were successful
Build frontend / build (push) Successful in 1m27s
All checks were successful
Build frontend / build (push) Successful in 1m27s
This commit is contained in:
488
security-audit-report.md
Normal file
488
security-audit-report.md
Normal file
@ -0,0 +1,488 @@
|
||||
# SweetHome — Full Security Audit & Pentest Report
|
||||
|
||||
**Project:** SweetHome Next.js Real Estate Application
|
||||
**Date:** June 16, 2026
|
||||
**Scope:** Client-side source code analysis (full white-box)
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
| Severity | Count | Key Issues |
|
||||
|----------|-------|------------|
|
||||
| 🔴 **CRITICAL** | 8 | JWT in localStorage, XSS via Leaflet, OTP in console, no middleware, passwords in URL params, IDOR on edit APIs |
|
||||
| 🟠 **HIGH** | 12 | Client-only role checks, HTTP endpoints, credential logging, mass assignment, error leakage, no CSP |
|
||||
| 🟡 **MEDIUM** | 15 | Hardcoded IPs, stale localStorage keys, weak validation, no security headers, FCM token exposure |
|
||||
| 🟢 **LOW** | 6 | Missing maxLength, base64 avatar in storage, weak email regex |
|
||||
|
||||
---
|
||||
|
||||
## 🔴 CRITICAL FINDINGS
|
||||
|
||||
### C1. JWT Stored in `localStorage` (No httpOnly Cookie)
|
||||
|
||||
**Files:** `app/services/AuthService.js:23-38`, `app/settings/page.js:335-339`, `app/payments/page.js:185-189`
|
||||
|
||||
```javascript
|
||||
const TOKEN_KEY = 'auth_token';
|
||||
const USER_KEY = 'cached_user';
|
||||
|
||||
const AuthService = Object.freeze({
|
||||
addToken(token) {
|
||||
localStorage.setItem(TOKEN_KEY, token); // <-- XSS-accessible
|
||||
},
|
||||
getToken() {
|
||||
return localStorage.getItem(TOKEN_KEY); // <-- XSS-accessible
|
||||
},
|
||||
deleteToken() {
|
||||
localStorage.removeItem(TOKEN_KEY);
|
||||
localStorage.removeItem(USER_KEY);
|
||||
},
|
||||
```
|
||||
|
||||
**Impact:** Any XSS vulnerability gives attackers permanent token theft. No httpOnly, Secure, or SameSite protection. Token persists across tabs with no client-side expiration enforcement.
|
||||
|
||||
**Multiple fallback keys** (fragmented storage):
|
||||
- `auth_token` (primary)
|
||||
- `token`, `accessToken`, `authToken` (fallbacks in settings, payments, reservations pages)
|
||||
- `cached_user` (user profile data)
|
||||
- `userProfile`, `userAvatar` (profile page - base64 images!)
|
||||
|
||||
---
|
||||
|
||||
### C2. Stored XSS via Leaflet Popup HTML Construction
|
||||
|
||||
**File:** `app/components/PropertyMapWithMarkers.js:51-65`
|
||||
|
||||
```javascript
|
||||
const popupContent = `
|
||||
<div dir="rtl" style="text-align: right; padding: 12px; max-width: 250px;">
|
||||
<h3 style="font-weight: bold; font-size: 16px; margin-bottom: 8px; color: #111;">
|
||||
${property.title || 'عقار'}
|
||||
</h3>
|
||||
<p style="font-size: 14px; color: #666; margin-bottom: 8px;">
|
||||
${property.address || property.location?.address || ''}
|
||||
</p>
|
||||
${property.images && property.images.length > 0
|
||||
? `<img src="${property.images[0]}" alt="${property.title}"
|
||||
style="width:100%;height:120px;object-fit:cover;border-radius:8px;"
|
||||
onerror="this.src='/property-placeholder.jpg'" />`
|
||||
: ''
|
||||
}
|
||||
</div>
|
||||
`;
|
||||
marker.bindPopup(popupContent);
|
||||
```
|
||||
|
||||
**Impact:** Property data (title, address, image URLs) from API is interpolated directly into HTML without sanitization. A malicious owner or compromised API can execute arbitrary JavaScript in every viewer's session. `onerror` handler on `<img>` provides an additional execution vector.
|
||||
|
||||
**Also affected:** `app/components/home/PropertyMap.js:227` — `property.priceUSD` interpolated into `L.divIcon({ html: ... })`.
|
||||
|
||||
---
|
||||
|
||||
### C3. OTP Verification Code Logged to Browser Console
|
||||
|
||||
**File:** `app/login/page.js:200-203`
|
||||
|
||||
```javascript
|
||||
console.log("[OTP] Verifying code:", otpCode); // <-- OTP in plaintext
|
||||
console.log("[OTP] Verify response status:", result.status);
|
||||
```
|
||||
|
||||
**Impact:** OTP codes are one-time passwords. Browser extensions, devtools monitors, or error-logging services can capture them. Full account takeover via credential + OTP.
|
||||
|
||||
---
|
||||
|
||||
### C4. No Server-Side Route Middleware
|
||||
|
||||
**No `middleware.js` file exists** in the project. Every protected route relies on **client-side only** checks:
|
||||
|
||||
```javascript
|
||||
// app/ClientLayout.js:74-86 — Role read from JWT, no server verification
|
||||
useEffect(() => {
|
||||
const authUser = AuthService.getUser();
|
||||
if (authUser) {
|
||||
setUser({ role: AuthService.isOwner() ? UserRole.OWNER : UserRole.CUSTOMER });
|
||||
}
|
||||
}, [pathname]);
|
||||
```
|
||||
|
||||
**Impact:** Routes like `/owner/*`, `/settings`, `/profile`, `/payments`, `/reservations` have zero server-side protection. If JS fails to load, pages render unauthenticated.
|
||||
|
||||
---
|
||||
|
||||
### C5. Passwords Sent as URL Query Parameters
|
||||
|
||||
**File:** `app/utils/api.js:1082-1088`
|
||||
|
||||
```javascript
|
||||
export async function changePassword(oldPassword, newPassword) {
|
||||
return apiFetch(
|
||||
`/User/ChangePassword?oldPassword=${encodeURIComponent(oldPassword)}&newPassword=${encodeURIComponent(newPassword)}`,
|
||||
{ method: "PUT" },
|
||||
);
|
||||
}
|
||||
|
||||
export async function deleteMyAccount(password) {
|
||||
return apiFetch(
|
||||
`/User/DeleteMyAccount?password=${encodeURIComponent(password)}`,
|
||||
{ method: "DELETE" },
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Impact:** Passwords in URL query parameters are logged by web servers, proxies, browser history, and the `apiFetch` `console.log("API Body:", ...)` at `api.js:445`. Also leaked via `Referer` header.
|
||||
|
||||
---
|
||||
|
||||
### C6. IDOR — No Ownership Verification on Property Edit/Status APIs
|
||||
|
||||
**File:** `app/utils/api.js:691-732`
|
||||
|
||||
```javascript
|
||||
editRentProperty(id, data) // PUT /RentProperties/EditRentProperty/${id}
|
||||
editSaleProperty(id, data) // PUT /SaleProperties/EditSaleProperty/${id}
|
||||
updateRentPropertyStatus(id, status) // PUT /RentProperties/UpdateStatus/${id}
|
||||
updateSalePropertyStatus(id, status) // PUT /SaleProperties/UpdateStatus/${id}
|
||||
```
|
||||
|
||||
**Impact:** Any authenticated user can edit or deactivate **any** property by guessing/iterating IDs. The `GetMyRentListings` fetch filters to "my" properties, but the edit API has no server-side ownership check.
|
||||
|
||||
---
|
||||
|
||||
### C7. IDOR — Reservation Confirmation Without Ownership Check
|
||||
|
||||
**File:** `app/owner/reservations/page.js:1309-1332`
|
||||
|
||||
```javascript
|
||||
const handleConfirm = async (r) => {
|
||||
setActionLoadingId(r.id);
|
||||
const res = await API(
|
||||
AuthService.getToken(),
|
||||
'PUT',
|
||||
`/Reservations/OwnerConfirmReservation/owner-confirm/${r.id}`
|
||||
);
|
||||
```
|
||||
|
||||
**Impact:** Any owner can confirm/reject **any** reservation by ID, regardless of whether they own the property. Combined with C6, an attacker could spam-confirm reservations across the platform.
|
||||
|
||||
---
|
||||
|
||||
### C8. IDOR — Profile APIs Expose Any User's Data
|
||||
|
||||
**File:** `app/utils/api.js:670-676`
|
||||
|
||||
```javascript
|
||||
getCustomerByUserId(userId) // GET /Customer/GetByUserId/${userId}
|
||||
getOwnerByUserId(userId) // GET /Owner/GetByUserId/${userId}
|
||||
```
|
||||
|
||||
**Impact:** If the backend doesn't verify the requesting user matches the target `userId`, this allows enumerating all user profiles (name, email, phone, WhatsApp, national number).
|
||||
|
||||
---
|
||||
|
||||
## 🟠 HIGH FINDINGS
|
||||
|
||||
### H1. Client-Side Only Role Checks (JWT Decoded Without Signature Verification)
|
||||
|
||||
**File:** `app/services/AuthService.js:66-75`
|
||||
|
||||
```javascript
|
||||
decodeToken() {
|
||||
const token = this.getToken();
|
||||
if (!token) return null;
|
||||
try {
|
||||
const payload = token.split('.')[1];
|
||||
return JSON.parse(atob(payload)); // <-- No signature verification, just base64
|
||||
} catch { return null; }
|
||||
},
|
||||
|
||||
isOwner() {
|
||||
return this.getRoles().includes('Owner');
|
||||
},
|
||||
```
|
||||
|
||||
**Impact:** A user can trivially craft/modify a JWT in localStorage to add the `Owner` role. While server-side validation should catch this, the client-side UI fully trusts the decoded token.
|
||||
|
||||
**All owner pages use this pattern:**
|
||||
```javascript
|
||||
if (!AuthService.isOwner()) { router.push('/'); return; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### H2. HTTP (Unencrypted) API Endpoints Used in Production
|
||||
|
||||
**File:** Multiple locations
|
||||
|
||||
| File | URL |
|
||||
|------|-----|
|
||||
| `app/utils/api.js:380` | `http://45.93.137.91/api` (REPORT_API_BASE) |
|
||||
| `app/settings/page.js:351` | `http://45.93.137.91/api/Reports/SendGeneralReport` |
|
||||
| `app/privacy/page.js:114` | `http://45.93.137.91/api` |
|
||||
| `app/payments/page.js:198` | `http://45.93.137.91/api/Customer/GetMyTransaction` |
|
||||
| `app/property/[id]/page.js:71` | `http://45.93.137.91${p.image}` |
|
||||
|
||||
**Impact:** Credentials, tokens, and personal data sent over HTTP are visible in plaintext to anyone on the network (MITM). The main API uses HTTPS (`*.nip.io`), but the Report API and several image fetches use raw HTTP.
|
||||
|
||||
---
|
||||
|
||||
### H3. User Credentials Logged to Console
|
||||
|
||||
**File:** `app/login/page.js:87-92`
|
||||
|
||||
```javascript
|
||||
console.log("[Login] Attempting login via", loginMethod, ":", formData.credential);
|
||||
```
|
||||
|
||||
**File:** `app/utils/api.js:443-445` — Full API request bodies logged:
|
||||
|
||||
```javascript
|
||||
console.log("API Request:", url);
|
||||
console.log("API Method:", options.method || "GET");
|
||||
console.log("API Body:", hasBody ? options.body : null); // <-- may contain passwords
|
||||
```
|
||||
|
||||
**Impact:** Login credentials, password change payloads, and any API body is visible in browser console. Any extension or error-logging service can capture this.
|
||||
|
||||
---
|
||||
|
||||
### H4. No CSP or Security Headers
|
||||
|
||||
**File:** `next.config.mjs`
|
||||
|
||||
```javascript
|
||||
const nextConfig = {
|
||||
reactCompiler: true,
|
||||
images: { remotePatterns: [ ... ] },
|
||||
};
|
||||
export default nextConfig;
|
||||
```
|
||||
|
||||
**Missing headers:**
|
||||
- `Content-Security-Policy` — would prevent XSS (C2)
|
||||
- `Strict-Transport-Security` (HSTS) — would enforce HTTPS
|
||||
- `X-Content-Type-Options: nosniff`
|
||||
- `X-Frame-Options: DENY` — clickjacking protection
|
||||
- `Referrer-Policy`
|
||||
|
||||
---
|
||||
|
||||
### H5. Mass Assignment via Spread Operator
|
||||
|
||||
**File:** `app/owner/properties/page.js:1592`
|
||||
|
||||
```javascript
|
||||
const updatedProperty = { ...property, ...formData };
|
||||
```
|
||||
|
||||
**Impact:** User-controlled `formData` is spread directly into the property object. Unexpected fields (`status`, `ownerId`, `role`) could overwrite protected properties if the backend doesn't sanitize.
|
||||
|
||||
---
|
||||
|
||||
### H6. Error Messages Leak Internal Details to Users
|
||||
|
||||
**12 affected files.** Pattern:
|
||||
|
||||
```javascript
|
||||
toast.error(err.message || 'فشل تحميل البيانات');
|
||||
```
|
||||
|
||||
**Files:** `account-verification/page.js`, `change-password/page.js`, `notifications/page.js`, `reports/page.js`, `payments/page.js`, `reservations/page.js`, `PropertyDetail.js`, `owner/*/page.js`
|
||||
|
||||
**Impact:** Raw `err.message` may contain API paths, stack traces, or database errors. Shows internal implementation details to end users.
|
||||
|
||||
---
|
||||
|
||||
### H7. Firebase & VAPID Keys Hardcoded Client-Side
|
||||
|
||||
**File:** `app/utils/firebase.js:5-11`
|
||||
|
||||
```javascript
|
||||
const firebaseConfig = {
|
||||
apiKey: "AIzaSyBZV7KBLRJSTApahfrO8lBesmIM3zNRSaY",
|
||||
authDomain: "sweet-home-b2766.firebaseapp.com",
|
||||
projectId: "sweet-home-b2766",
|
||||
// ...
|
||||
vapidKey: "BGZ4Fo8rRhoTdStLGlCySDZOnAX4ekCA0e3HDWXL5uEi2kOnXynYjbaDbY15002phUrFqxBpPPFHgfH2VhrmFDU",
|
||||
};
|
||||
```
|
||||
|
||||
**Duplicated in:** `public/firebase-messaging-sw.js:8-14`, `app/components/NotificationHandler.js:70`
|
||||
|
||||
**Impact:** While Firebase API keys are semi-public, the `projectId` and `storageBucket` exposure enables abuse if Firebase Security Rules are misconfigured. Push notifications can be sent without server authorization.
|
||||
|
||||
---
|
||||
|
||||
## 🟡 MEDIUM FINDINGS
|
||||
|
||||
### M1. Hardcoded Server IP in 10+ Files
|
||||
|
||||
The IP `45.93.137.91` is hardcoded across the codebase with no environment variable abstraction for many URLs.
|
||||
|
||||
### M2. No CSRF Protection
|
||||
|
||||
No CSRF tokens, no `SameSite` cookie attributes (no cookies used at all), no custom headers for state-changing operations.
|
||||
|
||||
### M3. No Rate Limiting or Brute-Force Protection
|
||||
|
||||
Login functions (`loginWithEmail`, `loginWithPhone`) pass credentials directly without client-side rate limiting or exponential backoff.
|
||||
|
||||
### M4. CI/CD Server URL Exposed in Source
|
||||
|
||||
**File:** `.gitea/workflows/deployer.yaml:14`
|
||||
```yaml
|
||||
github-server-url: http://45.93.137.91:3000
|
||||
```
|
||||
|
||||
### M5. FCM Push Token Logged to Console
|
||||
|
||||
**File:** `app/utils/firebase.js:45`
|
||||
```javascript
|
||||
console.log("[FCM] Token:", token); // persistent device identifier
|
||||
```
|
||||
|
||||
### M6. Owner Properties Cached in localStorage
|
||||
|
||||
**File:** `app/owner/properties/page.js:1450-1453`
|
||||
```javascript
|
||||
localStorage.setItem("ownerProperties", JSON.stringify(newProperties));
|
||||
```
|
||||
|
||||
### M7. Weak Email Validation Regex
|
||||
|
||||
**Pattern:** `/^[^\s@]+@[^\s@]+\.[^\s@]+$/` — allows ````, `|`, and special characters in local part.
|
||||
|
||||
### M8. Report API Sends Token Over HTTP
|
||||
|
||||
**File:** `app/settings/page.js:351` — `fetch('http://45.93.137.91/api/Reports/SendGeneralReport', ...)` with Bearer token header.
|
||||
|
||||
### M9. No `.env` Files in Repository
|
||||
|
||||
All environment variables fall back to hardcoded values (IPs, Firebase config). No `.env` or `.env.local` exists.
|
||||
|
||||
### M10. User Credential Reflected in OTP UI
|
||||
|
||||
**File:** `app/login/page.js:612` — `{formData.credential}` rendered in DOM (JSX-escaped, but could aid social engineering).
|
||||
|
||||
### M11. Client-Side Only File Upload Validation
|
||||
|
||||
`file.type.startsWith('image/')` — bypassable. Size limits (2MB/5MB) enforced client-side only.
|
||||
|
||||
### M12. All Reservations Loaded Then Filtered Client-Side
|
||||
|
||||
**File:** `app/reservations/page.js:871-899` — Fetches ALL rent properties via `getRentProperties()` for enrichment; if backend doesn't scope properly, sensitive data leaks.
|
||||
|
||||
### M13. Reservation Report Allows Reporting Any Reservation
|
||||
|
||||
**File:** `app/reservations/page.js:498-543` — No verification the reporter owns the reservation before filing a report.
|
||||
|
||||
### M14. Multiple Inconsistent localStorage Keys
|
||||
|
||||
At least 5 different keys store user data: `auth_token`, `token`, `accessToken`, `authToken`, `user`, `currentUser`, `authUser`, `profile`, `cached_user`.
|
||||
|
||||
### M15. i18next Configured to Cache in localStorage
|
||||
|
||||
**File:** `app/i18n/config.js:373-374` — Language detection data cached in localStorage.
|
||||
|
||||
---
|
||||
|
||||
## 🟢 LOW FINDINGS
|
||||
|
||||
| # | Finding | File(s) |
|
||||
|---|---------|---------|
|
||||
| L1 | Missing `maxLength` on text inputs | `reports/page.js`, `profile/page.js`, `login/page.js` |
|
||||
| L2 | Base64 avatar images stored in localStorage | `profile/page.js:163` |
|
||||
| L3 | Weak WhatsApp number sanitization | `PropertyDetail.js:1682` — only `[^0-9]` stripped |
|
||||
| L4 | Profuse console.log throughout codebase | ~30+ files with active console.log statements |
|
||||
| L5 | Business data cached in localStorage | `owner/bookings/page.js`, `owner/profits/page.js`, `owner/calendar/page.js` |
|
||||
| L6 | No JWT library in dependencies (manual `atob` decode) | `package.json` |
|
||||
|
||||
---
|
||||
|
||||
## Dependencies Analysis
|
||||
|
||||
**File:** `package.json`
|
||||
|
||||
| Dependency | Risk |
|
||||
|------------|------|
|
||||
| `next@16.1.6` | Very bleeding-edge; verify official release |
|
||||
| `firebase@^12.11.0` | FCM only; correctly scoped |
|
||||
| `xlsx@^0.18.5` | Used for admin export; no user data flow |
|
||||
| `jspdf@^4.2.1` | Client-side PDF generation; safe |
|
||||
| `html2canvas@^1.4.1` | Screenshot capture; safe |
|
||||
| No `jsonwebtoken`/`jose` | Manual JWT decode with `atob()` — no signature verification |
|
||||
| No `helmet`/`cors`/`csurf` | No security middleware |
|
||||
| No `axios`/`ky` | Raw `fetch` used throughout |
|
||||
|
||||
---
|
||||
|
||||
## Attack Scenarios
|
||||
|
||||
### Scenario 1: Full Account Takeover via XSS + OTP Logging
|
||||
1. Attacker stores a malicious property with `<script>` in the title
|
||||
2. Any user viewing the map (`PropertyMapWithMarkers.js`) executes the script
|
||||
3. Script steals JWT from `localStorage` → full account access
|
||||
4. If the victim is logging in, OTP is visible in console → complete takeover
|
||||
|
||||
### Scenario 2: Mass Property Manipulation via IDOR
|
||||
1. Attacker enumerates property IDs via `GetRentProperties` (public)
|
||||
2. Calls `editRentProperty(id, { price: 1 })` or `updateRentPropertyStatus(id, 5)` on any property
|
||||
3. No server-side ownership check — all properties editable
|
||||
|
||||
### Scenario 3: Credential Harvesting via MITM
|
||||
1. Attacker on same network intercepts HTTP traffic to `http://45.93.137.91/api`
|
||||
2. Captures Bearer tokens, passwords in URL query params, and personal data
|
||||
3. Credentials from `changePassword?oldPassword=...&newPassword=...` visible in plaintext
|
||||
|
||||
---
|
||||
|
||||
## Key Remediation Priority Matrix
|
||||
|
||||
| Priority | Fix | Effort | Impact |
|
||||
|----------|-----|--------|--------|
|
||||
| P0 | Move JWT to httpOnly Secure SameSite cookie | High | Eliminates XSS token theft |
|
||||
| P0 | Sanitize Leaflet popup HTML with DOMPurify | Low | Fixes stored XSS |
|
||||
| P0 | Remove all console.log statements (especially OTP) | Medium | Stops data leakage |
|
||||
| P0 | Add `middleware.js` for server-side route protection | Medium | Real auth enforcement |
|
||||
| P0 | Move passwords from URL params to POST body | Low | Stops credential leakage |
|
||||
| P1 | Add CSP and security headers in `next.config.mjs` | Low | Mitigates XSS, MITM, clickjacking |
|
||||
| P1 | Add ID validation on all API calls with path IDs | Medium | Defense-in-depth vs IDOR |
|
||||
| P1 | Replace HTTP endpoints with HTTPS | Medium | Stops MITM credential theft |
|
||||
| P1 | Add server-side ownership verification on edit APIs | High (backend) | Primary IDOR fix |
|
||||
| P1 | Remove `err.message` from toast.error calls | Low | Prevents info leakage |
|
||||
| P2 | Move all hardcoded IPs/secrets to env variables | Medium | Configurable, secure defaults |
|
||||
| P2 | Enforce single localStorage key for token | Low | Consistent, auditable |
|
||||
| P2 | Add rate limiting on login APIs | High (backend) | Brute-force protection |
|
||||
| P2 | Add input maxLength and server-side validation | Low | Defense-in-depth |
|
||||
|
||||
---
|
||||
|
||||
## Files Reviewed
|
||||
|
||||
| File | Lines | Key Security Findings |
|
||||
|------|-------|----------------------|
|
||||
| `app/utils/api.js` | 1088 | Token logging, passwords in URL, IDOR on edit APIs, no CSRF |
|
||||
| `app/services/AuthService.js` | 151 | localStorage JWT, no signature verification, role from decoded JWT |
|
||||
| `app/login/page.js` | 800+ | OTP in console, credential logging, credential in UI |
|
||||
| `app/property/[id]/PropertyDetail.js` | 1700+ | Error messages leak, WhatsApp link construction |
|
||||
| `app/components/PropertyMapWithMarkers.js` | 100+ | **Stored XSS** via Leaflet popup HTML |
|
||||
| `app/components/home/PropertyMap.js` | 250+ | HTML injection in DivIcon |
|
||||
| `app/owner/properties/page.js` | 1650+ | Mass assignment, localStorage caching, console.log |
|
||||
| `app/owner/reservations/page.js` | 1350+ | IDOR on confirm/reject, localStorage fallback keys |
|
||||
| `app/reservations/page.js` | 1000+ | Load all rent properties, report any reservation, error leakage |
|
||||
| `app/settings/page.js` | 500+ | Token over HTTP, multiple localStorage fallback keys |
|
||||
| `app/profile/page.js` | 250+ | localStorage caching, base64 avatar, console.log |
|
||||
| `next.config.mjs` | 12 | No security headers, no CSP |
|
||||
| `package.json` | — | No security libraries |
|
||||
| `app/utils/firebase.js` | 60 | Hardcoded Firebase config, FCM token logging |
|
||||
| `.gitea/workflows/deployer.yaml` | 30 | CI/CD internal URL exposed |
|
||||
| `app/payments/page.js` | 300+ | Token from multiple localStorage keys |
|
||||
| `app/owner/properties/add/page.js` | 700+ | Full payload logged to console |
|
||||
| `app/register/*/page.js` | 2000+ | Client-only file upload validation |
|
||||
| `app/change-password/page.js` | 60+ | Error leakage |
|
||||
| `app/account-verification/page.js` | 110+ | Error leakage |
|
||||
|
||||
---
|
||||
|
||||
*Report generated June 16, 2026. All findings based on static analysis of client-side source code.*
|
||||
Reference in New Issue
Block a user