56 lines
1.9 KiB
JavaScript
56 lines
1.9 KiB
JavaScript
export const metadata = {
|
|
id: 'middleware-broad-matcher',
|
|
title: 'Middleware matcher missing or too broad',
|
|
severity: 'high',
|
|
billingDimension: 'edge-requests',
|
|
trafficIndependent: true,
|
|
description:
|
|
'middleware.ts without a config.matcher (or matcher: ["/(.*)"]) runs on every request including _next/static, _next/image, favicon.ico, and image asset fetches. Edge-request cost scales accordingly.',
|
|
fix:
|
|
'Scope the matcher to actual application paths. Example: matcher: ["/((?!_next/static|_next/image|favicon.ico|.*\\\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)"]',
|
|
citations: [
|
|
'https://nextjs.org/docs/app/building-your-application/routing/middleware',
|
|
],
|
|
excludeGlobs: ['node_modules/**', '.next/**', 'dist/**', '__tests__/**'],
|
|
includeGlobs: ['middleware.{ts,js,mjs}', 'src/middleware.{ts,js,mjs}'],
|
|
};
|
|
|
|
export function scan({ files }) {
|
|
const out = [];
|
|
for (const { path, content } of files) {
|
|
if (!isApplicable(path)) continue;
|
|
|
|
const exportsMiddleware = /export\s+(default\s+)?(async\s+)?function\s+middleware/.test(content)
|
|
|| /export\s+const\s+middleware\s*=/.test(content);
|
|
if (!exportsMiddleware) continue;
|
|
|
|
const configBlock = content.match(/export\s+const\s+config\s*=\s*\{([\s\S]*?)\}/);
|
|
const matcherStr = configBlock && configBlock[1].match(/matcher\s*:\s*([^,}]+)/);
|
|
|
|
let problem = null;
|
|
if (!configBlock || !matcherStr) {
|
|
problem = 'no config.matcher (runs on every request)';
|
|
} else {
|
|
const m = matcherStr[1];
|
|
if (/['"`]\s*\/\s*['"`]/.test(m) || /['"`]\/\(\.\*\)['"`]/.test(m)) {
|
|
problem = 'matcher = "/" or "/(.*)" (still covers everything)';
|
|
}
|
|
}
|
|
|
|
if (problem) {
|
|
out.push({
|
|
pattern: metadata.id,
|
|
file: path,
|
|
line: 1,
|
|
evidence: problem,
|
|
trafficIndependent: metadata.trafficIndependent,
|
|
});
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function isApplicable(path) {
|
|
return /(^|\/)middleware\.(ts|js|mjs)$/.test(path);
|
|
}
|