import type { DevToolbarRule } from "./types.ts"; import { createIssue } from "./helpers.ts"; export const formRules: DevToolbarRule[] = [ { id: "forms/basics", category: "forms", defaultSeverity: "warning", description: "Checks common form implementation problems.", run: ({ root }) => { const issues = []; for (const input of root.querySelectorAll< HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement >("input:not([type='hidden']),textarea,select")) { if (!input.name) issues.push( createIssue({ ruleId: "forms/name-missing", category: "forms", severity: "warning", title: "Form control has no name", message: "This value may not be included in native form submission.", element: input, recommendation: "Add a stable name attribute.", }), ); if ( input instanceof HTMLInputElement && ["email", "tel", "password", "text"].includes(input.type) && !input.autocomplete ) issues.push( createIssue({ ruleId: "forms/autocomplete", category: "forms", severity: "suggestion", title: "Autocomplete is not configured", message: "Browsers may not provide the most useful autofill behavior.", element: input, recommendation: "Add an appropriate autocomplete token.", confidence: "medium", }), ); } for (const form of root.querySelectorAll("form")) { if ( (form.method || "get").toLowerCase() === "get" && form.querySelector("input[type='password']") ) issues.push( createIssue({ ruleId: "forms/password-get", category: "security", severity: "error", title: "Password form uses GET", message: "Sensitive values can appear in URLs and logs.", element: form, recommendation: "Use POST for forms containing secrets.", }), ); } return issues; }, }, ];