diff --git a/AUDIT_FINDINGS.md b/AUDIT_FINDINGS.md index 252ec48..65fff0f 100644 --- a/AUDIT_FINDINGS.md +++ b/AUDIT_FINDINGS.md @@ -249,6 +249,19 @@ |---|----------|------|---------|-------------|-----------------|--------| | AD-1 | **MEDIUM** | `server/src/routes/admin.ts` | 302-310 | Self-update endpoint runs `git pull` then `npm run build`. While admin-only and `npm install` uses `--ignore-scripts`, `npm run build` executes whatever is in the pulled package.json. A compromised upstream could execute arbitrary code. | Document as accepted risk for self-hosted self-update feature. Users should pin to specific versions. | DOCUMENTED | +### 1.9 Client-Side XSS + +| # | Severity | File | Line(s) | Description | Recommended Fix | Status | +|---|----------|------|---------|-------------|-----------------|--------| +| X-1 | **CRITICAL** | `client/src/components/Admin/GitHubPanel.tsx` | 66, 106 | `dangerouslySetInnerHTML` with `inlineFormat()` renders GitHub release notes as HTML without escaping. Malicious HTML in release notes could execute scripts. | Escape HTML entities before applying markdown-style formatting. Validate link URLs. | FIXED | +| X-2 | **LOW** | `client/src/components/Map/MapView.tsx` | divIcon | Uses `escAttr()` for HTML sanitization in divIcon strings. Properly mitigated. | OK | OK | + +### 1.10 Route Calculator Bug + +| # | Severity | File | Line(s) | Description | Recommended Fix | Status | +|---|----------|------|---------|-------------|-----------------|--------| +| RC-1 | **HIGH** | `client/src/components/Map/RouteCalculator.ts` | 16 | OSRM URL hardcodes `'driving'` profile, ignoring the `profile` parameter. Walking/cycling routes always return driving results. | Use the `profile` parameter in URL construction. | FIXED | + ### Additional Findings (from exhaustive scan) - **MEDIUM** — `server/src/index.ts:121-136`: Upload files (`/uploads/:type/:filename`) served without authentication. UUIDs are unguessable but this is security-through-obscurity. **REQUIRES MANUAL REVIEW** — adding auth would break shared trip image URLs. diff --git a/client/src/components/Admin/GitHubPanel.tsx b/client/src/components/Admin/GitHubPanel.tsx index 8db9d6f..2dc6dfa 100644 --- a/client/src/components/Admin/GitHubPanel.tsx +++ b/client/src/components/Admin/GitHubPanel.tsx @@ -72,11 +72,15 @@ export default function GitHubPanel() { } } + const escapeHtml = (str) => str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"') const inlineFormat = (text) => { - return text + return escapeHtml(text) .replace(/\*\*(.+?)\*\*/g, '$1') .replace(/`(.+?)`/g, '$1') - .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1') + .replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, label, url) => { + const safeUrl = url.startsWith('http://') || url.startsWith('https://') ? url : '#' + return `${label}` + }) } for (const line of lines) { diff --git a/client/src/components/Map/RouteCalculator.ts b/client/src/components/Map/RouteCalculator.ts index 6f79a97..8300aab 100644 --- a/client/src/components/Map/RouteCalculator.ts +++ b/client/src/components/Map/RouteCalculator.ts @@ -13,7 +13,7 @@ export async function calculateRoute( } const coords = waypoints.map((p) => `${p.lng},${p.lat}`).join(';') - const url = `${OSRM_BASE}/driving/${coords}?overview=full&geometries=geojson&steps=false` + const url = `${OSRM_BASE}/${profile}/${coords}?overview=full&geometries=geojson&steps=false` const response = await fetch(url, { signal }) if (!response.ok) {