62 lines
1.5 KiB
TypeScript
62 lines
1.5 KiB
TypeScript
function normalizeRequestPath(requestUrl: string) {
|
|
try {
|
|
return new URL(requestUrl, 'http://localhost').pathname;
|
|
} catch {
|
|
return requestUrl.split('?')[0] ?? requestUrl;
|
|
}
|
|
}
|
|
|
|
function hasHiddenPathSegment(pathname: string) {
|
|
return pathname
|
|
.split('/')
|
|
.some((segment, index) => index > 1 && segment.startsWith('.'));
|
|
}
|
|
|
|
function isIgnoredScannerProbePath(pathname: string) {
|
|
return pathname === '/api/docs' ||
|
|
pathname.startsWith('/api/docs/') ||
|
|
pathname === '/api/env' ||
|
|
pathname === '/api/config' ||
|
|
pathname === '/api/debug' ||
|
|
pathname.startsWith('/api/debug/');
|
|
}
|
|
|
|
function isOpaqueScannerToken(segment: string) {
|
|
if (segment.length < 24 || !/^[a-z0-9_-]+$/i.test(segment)) {
|
|
return false;
|
|
}
|
|
|
|
if (/^(.)\1+$/.test(segment)) {
|
|
return true;
|
|
}
|
|
|
|
return !/[/-]/.test(segment);
|
|
}
|
|
|
|
function isIgnoredOpaqueApiProbePath(pathname: string) {
|
|
const segments = pathname.split('/').filter(Boolean);
|
|
|
|
if (segments.length !== 2 || segments[0] !== 'api') {
|
|
return false;
|
|
}
|
|
|
|
return isOpaqueScannerToken(segments[1] ?? '');
|
|
}
|
|
|
|
export function shouldPersistNotFoundErrorLog(requestUrl: string) {
|
|
const pathname = normalizeRequestPath(String(requestUrl ?? ''));
|
|
if (!(pathname === '/api' || pathname.startsWith('/api/'))) {
|
|
return false;
|
|
}
|
|
|
|
if (hasHiddenPathSegment(pathname)) {
|
|
return false;
|
|
}
|
|
|
|
if (isIgnoredScannerProbePath(pathname)) {
|
|
return false;
|
|
}
|
|
|
|
return !isIgnoredOpaqueApiProbePath(pathname);
|
|
}
|