Some checks failed
Deploy Website / build-and-deploy (push) Has been cancelled
81 lines
2.1 KiB
TypeScript
81 lines
2.1 KiB
TypeScript
const CONTROL_CHARS = /[\u0000-\u001f\u007f]/;
|
|
|
|
function sanitizeHeaderValue(value: string, maxLength: number) {
|
|
return value.replace(/[\u0000-\u001f\u007f]/g, '').trim().slice(0, maxLength) || 'unknown';
|
|
}
|
|
|
|
export function getClientAddress(headers: Headers) {
|
|
const forwarded = headers.get('x-forwarded-for');
|
|
if (forwarded) {
|
|
const addresses = forwarded
|
|
.split(',')
|
|
.map((value) => value.trim())
|
|
.filter(Boolean);
|
|
|
|
if (addresses.length > 0) {
|
|
// Use the nearest address. This avoids trusting a spoofed left-most value
|
|
// when Next or a reverse proxy appends the real peer address.
|
|
return sanitizeHeaderValue(addresses[addresses.length - 1], 128);
|
|
}
|
|
}
|
|
|
|
const realIp = headers.get('x-real-ip');
|
|
if (realIp) return sanitizeHeaderValue(realIp, 128);
|
|
|
|
return 'unknown';
|
|
}
|
|
|
|
export function getUserAgent(headers: Headers) {
|
|
return sanitizeHeaderValue(headers.get('user-agent') || 'unknown', 256);
|
|
}
|
|
|
|
export function normalizeVisitPath(value: unknown) {
|
|
if (typeof value !== 'string') return null;
|
|
|
|
const input = value.trim();
|
|
if (
|
|
input.length === 0 ||
|
|
input.length > 2048 ||
|
|
!input.startsWith('/') ||
|
|
input.startsWith('//') ||
|
|
CONTROL_CHARS.test(input)
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
const parsed = new URL(input, 'https://site.local');
|
|
if (parsed.origin !== 'https://site.local') return null;
|
|
return `${parsed.pathname}${parsed.search}`;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function isSameOriginRequest(request: Request) {
|
|
const host = request.headers.get('host');
|
|
if (!host) return false;
|
|
const requestHost = host.toLowerCase();
|
|
|
|
const origin = request.headers.get('origin');
|
|
if (origin) {
|
|
try {
|
|
return new URL(origin).host.toLowerCase() === requestHost;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const referer = request.headers.get('referer');
|
|
if (referer) {
|
|
try {
|
|
return new URL(referer).host.toLowerCase() === requestHost;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const fetchSite = request.headers.get('sec-fetch-site');
|
|
return fetchSite === 'same-origin' || fetchSite === 'none';
|
|
}
|