import type { DevToolbarRule } from "./types.ts"; import { createIssue } from "./helpers.ts"; export const linkRules: DevToolbarRule[] = [ { id: "links/basics", category: "links", defaultSeverity: "warning", description: "Checks unsafe and incomplete links.", run: ({ root, url }) => { const issues = []; for (const anchor of root.querySelectorAll("a")) { const raw = anchor.getAttribute("href"); if (!raw || raw === "#") issues.push( createIssue({ ruleId: "links/empty", category: "links", severity: "warning", title: "Link has no useful destination", message: `The href is ${raw ? '"#"' : "missing"}.`, element: anchor, recommendation: "Provide a real URL or use a button for an action.", }), ); if (raw?.trim().toLowerCase().startsWith("javascript:")) issues.push( createIssue({ ruleId: "links/javascript-url", category: "security", severity: "error", title: "JavaScript URL used", message: "javascript: links are unsafe and inaccessible.", element: anchor, recommendation: "Use a button and a normal event handler.", }), ); if ( anchor.target === "_blank" && !anchor.rel.split(/\s+/).some((value) => value === "noopener" || value === "noreferrer") ) issues.push( createIssue({ ruleId: "links/blank-rel", category: "security", severity: "warning", title: "New-tab link lacks rel protection", message: "The opened page may retain access to window.opener.", element: anchor, recommendation: 'Add rel="noopener noreferrer".', }), ); if (raw?.startsWith("http://") && url.protocol === "https:") issues.push( createIssue({ ruleId: "links/mixed-content", category: "security", severity: "error", title: "Insecure link on HTTPS page", message: "This link uses HTTP from an HTTPS page.", element: anchor, recommendation: "Use an HTTPS destination.", }), ); } return issues; }, }, ];