Security hardening from 2026-07-01 audit
- Exclude supabase/ from Docker build context (leaked service_role key file) - /api/files: exact per-user namespace match + reject path traversal; storage resolveKey rejects ".."/"." segments (fixes cross-user file read) - Add ownsProperty/Unit/Tenant checks to tenants, maintenance (landlord path), and documents (JSON branch, now field-whitelisted) create handlers - Escape user data in follow-up + payment-link emails (reuse escapeHtml) - Neutralize CSV formula injection in toCsv + export routes - Tighter sign-in rate limit (10/min); env-gated email verification + sender - Per-request nonce CSP; drop script-src 'unsafe-inline' (styles unchanged) - Add input length bounds; validate follow-ups POST body Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
857b9a7811
commit
969d5d4c8a
@@ -14,12 +14,18 @@ export async function GET(_: Request, { params }: { params: Promise<{ key: strin
|
||||
if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
|
||||
|
||||
const { key: segments } = await params
|
||||
const key = segments.map((s) => decodeURIComponent(s)).join("/")
|
||||
|
||||
if (!key.startsWith(`${user.id}/`)) {
|
||||
// Reject traversal / malformed segments (no double-decode — params are already decoded).
|
||||
const badSegment = segments.some(
|
||||
(s) => s === "" || s === "." || s === ".." || s.includes("/") || s.includes("\\")
|
||||
)
|
||||
// Ownership: the first path segment must be EXACTLY the caller's user id.
|
||||
if (badSegment || segments[0] !== user.id) {
|
||||
return NextResponse.json({ error: "Forbidden" }, { status: 403 })
|
||||
}
|
||||
|
||||
const key = segments.join("/")
|
||||
|
||||
try {
|
||||
const buffer = await readFile(key)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user