Fix security audit findings: auth scoping, OIDC hardening, CSP, file download
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:
@@ -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, {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user