66 lines
2.2 KiB
TypeScript
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;
|
|
},
|
|
},
|
|
];
|