bf70c3d7d7
Security review caught: per-row hover actions (users.js) and async user picker (shop.js, gam.js) interpolated user-controlled name into JS string literals inside onclick. LS.esc() escapes & < > " but NOT backslash; the .replace(/'/g, '\'') fallback was broken. Attack: any authenticated user could set their name to a\'); alert(1); // via PATCH /api/auth/profile (stripTags doesn't strip \) — admin viewing the users/shop/gam picker would execute arbitrary JS. Fix: switch from JS-string interpolation to data-uid/data-name attributes, read via dataset in handler. esc() correctly escapes for HTML-attribute context; dataset returns the raw string with zero parse re-entry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>