Fix security audit findings: auth scoping, OIDC hardening, CSP, file download
Deploy to LXC / deploy (push) Successful in 1m56s
Validate / validate (push) Successful in 33s

C3: Budget allocation now verifies project belongs to company
M4: Expense approve/reject scoped by company via project join
H2: OIDC cookies get secure flag on HTTPS
H3: OIDC auto-link only when email_verified by provider
H4: Content-Security-Policy + X-Content-Type-Options in hooks
M7: SSRF favicon redirect depth capped at 3
M2: File downloads use attachment disposition (not inline)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-17 14:18:28 +07:00
parent dbfd229ba8
commit b4eda2d553
8 changed files with 34 additions and 16 deletions
+1 -1
View File
@@ -117,7 +117,7 @@ export async function exchangeCode(
export async function getUserInfo(
accessToken: string
): Promise<{ sub: string; email: string; name?: string }> {
): Promise<{ sub: string; email: string; name?: string; email_verified?: boolean }> {
const config = await getOIDCConfig();
const res = await fetch(config.userinfoEndpoint, {
+3 -2
View File
@@ -74,7 +74,8 @@ async function resolvePublicIp(hostname: string): Promise<string> {
return ips[0];
}
async function safeFetch(targetUrl: URL): Promise<Response | null> {
async function safeFetch(targetUrl: URL, depth = 0): Promise<Response | null> {
if (depth > 3) return null;
if (targetUrl.protocol !== 'http:' && targetUrl.protocol !== 'https:') return null;
try {
await resolvePublicIp(targetUrl.hostname);
@@ -109,7 +110,7 @@ async function safeFetch(targetUrl: URL): Promise<Response | null> {
} catch {
return null;
}
return safeFetch(next);
return safeFetch(next, depth + 1);
}
return res;
} catch {