Files
WRNexusJS/packages/dev-toolbar/src/rules/forms.ts
T
2026-07-21 12:30:46 +05:30

66 lines
2.2 KiB
TypeScript

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<HTMLFormElement>("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;
},
},
];