diff --git a/src/Web/autopilot-monitor-web/app/settings/components/HardwareWhitelistSection.tsx b/src/Web/autopilot-monitor-web/app/settings/components/HardwareWhitelistSection.tsx index 91c49a14..3ef7c0a4 100644 --- a/src/Web/autopilot-monitor-web/app/settings/components/HardwareWhitelistSection.tsx +++ b/src/Web/autopilot-monitor-web/app/settings/components/HardwareWhitelistSection.tsx @@ -3,7 +3,7 @@ import { useState } from "react"; import SaveResetBar from "./SaveResetBar"; import ReadOnlyFieldset from "./ReadOnlyFieldset"; -import { joinList, parseList, toWhitelistEntry } from "../lib/hardwareWhitelist"; +import { joinList, parseList } from "../lib/hardwareWhitelist"; interface HardwareWhitelistSectionProps { manufacturerWhitelist: string; @@ -31,15 +31,17 @@ function WhitelistEditor({ setItems: (items: string[]) => void; }) { const [newItem, setNewItem] = useState(""); - // One typed value = one pattern; a ',' would otherwise split into several entries. - const trimmed = toWhitelistEntry(newItem); - const isDuplicate = - trimmed !== "" && - items.some((a) => a.toLowerCase() === trimmed.toLowerCase()); + // Admin-typed input is a LIST: "Dell*, *" adds two patterns (the placeholder invites + // exactly that). Only the insights buttons neutralize ',' — their values are untrusted. + const existing = new Set(items.map((a) => a.toLowerCase())); + const typed = parseList(newItem); + const fresh = typed.filter((t, i) => + !existing.has(t.toLowerCase()) && typed.findIndex((u) => u.toLowerCase() === t.toLowerCase()) === i); + const isDuplicate = typed.length > 0 && fresh.length === 0; const addItem = () => { - if (!trimmed || isDuplicate) return; - setItems([...items, trimmed]); + if (fresh.length === 0) return; + setItems([...items, ...fresh]); setNewItem(""); }; @@ -104,7 +106,7 @@ function WhitelistEditor({ />