fix(routing): enforce slashless public URLs

This commit is contained in:
DeCentN2Madness committed 2026-10-25 13:13:46 -07:00
1 parent 7a6cab29db
commit f41af1966e
5 files changed
+87 -5

No files matched your search

+14
View File
@@ -24,6 +24,18 @@ function formActions(html) {
match[1].match(/\baction=["']([^"']*)["']/i)?.[1] ?? null);
}
function rootRelativeUrls(html) {
const urls = [];
for (const match of html.matchAll(/\b(?:href|action)=["']([^"']+)["']/gi)) urls.push(match[1]);
return urls;
}
function hasTerminalSlash(url) {
if (!url.startsWith('/') || url.startsWith('//')) return false;
const pathname = url.split(/[?#]/, 1)[0];
return pathname !== '/' && pathname.endsWith('/');
}
function unsafeSignals(html) {
const signals = [];
if (/<iframe\b[^>]*\bsrc=["'][^"']*jotform/i.test(html)) signals.push('active Jotform iframe');
@@ -52,10 +64,12 @@ export async function verifyArtifact(root, mode = 'staging') {
const title = html.match(/<title>([^<]*)<\/title>/i)?.[1]?.trim() ?? '';
const actions = formActions(html);
const nonSearchActions = actions.filter((action) => action && action !== '/search');
const trailingSlashUrls = rootRelativeUrls(html).filter(hasTerminalSlash);
const signals = unsafeSignals(html);
if (!title) errors.push(`${route}: missing title`);
if (!canonical) errors.push(`${route}: missing canonical`);
if (!robots) errors.push(`${route}: missing robots directive`);
if (trailingSlashUrls.length) errors.push(`${route}: root-relative URL(s) end in a slash: ${trailingSlashUrls.join(', ')}`);
if (mode === 'staging' && robots !== 'noindex,nofollow') errors.push(`${route}: staging robots is ${robots ?? 'missing'}`);
if (canonical) {
const prior = canonicalMap.get(canonical);