diff --git a/server/src/ledgrab/static/js/core/icon-select.ts b/server/src/ledgrab/static/js/core/icon-select.ts index 2419b5c..6b67b20 100644 --- a/server/src/ledgrab/static/js/core/icon-select.ts +++ b/server/src/ledgrab/static/js/core/icon-select.ts @@ -44,6 +44,43 @@ function escAttr(text: string | undefined | null): string { .replace(/'/g, '''); } +/** + * Defensive sink for `IconSelectItem.icon`. + * + * `item.icon` is interpolated raw into the cell innerHTML (see `_buildGrid` + * and `_syncTrigger`). Every current caller (audited via the IconSelect + * caller scan) feeds the field with one of: + * * a constant `ICON_*` literal, + * * the output of a lookup table (`getValueSourceIcon`, `getColorStripIcon`, + * `getDeviceTypeIcon`, …) — table values are project-owned constants, + * * `renderDeviceIcon(d.icon)` — `d.icon` is a stored id mapped to a + * project-owned SVG via `getDeviceIconDef`; unknown ids return `''`. + * None of those paths embed user input. + * + * The sanitiser here is **defence in depth** for a future caller who + * forgets that contract: reject icon strings containing `