[FIX][UI]: Replace inline event handlers on Plugins page to survive innerHTML sanitizer#3288
[FIX][UI]: Replace inline event handlers on Plugins page to survive innerHTML sanitizer#3288crivetimihai merged 3 commits intomainfrom
Conversation
b2a8515 to
10bd8b5
Compare
77b4603 to
a3e742e
Compare
|
Thanks @gcgoncalves. Excellent root cause analysis — the innerHTML sanitizer stripping inline |
a3e742e to
0380b0f
Compare
|
Thanks for the PR @gcgoncalves. Here are the review findingsDetailed Criteria Assessment — Issue #3271
This PR removed both onclick and onkeydown from every badge element and replaced onclick with a delegated click listener on #plugins-panel. But it only added a delegated click listener — there's no corresponding delegated keydown listener. The role="button" tabindex="0" attributes are still there, so the badges are still focusable via Tab, but pressing Enter or Space on them does nothing. |
c4f13f6 to
9e7bc65
Compare
f0ec81a to
bf807c4
Compare
marekdano
left a comment
There was a problem hiding this comment.
Good approach to use data attributes in this case!
Manually tested, and it works as expected.
LGTM 🚀
Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
…iew polyfill - Remove unused imports (beforeEach) and destructured variables (card1, card2, hookFilter) - Add HTMLElement.prototype.scrollIntoView polyfill for JSDOM environment - Revert unrelated package-lock.json changes Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
…controls The delegated keydown listener on #plugins-panel was calling preventDefault() unconditionally for Enter and Space before checking whether the target was actually an actionable element (badge, button). This blocked typing spaces in the search box and pressing Enter on select dropdowns. Fix: dispatchPluginAction() now returns a boolean indicating whether it matched an action. The keydown handler only calls preventDefault() when an action was actually dispatched. Add regression tests: Space in #plugin-search and Enter on #plugin-mode-filter must not have defaultPrevented set. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
f7bc85d to
3ba975c
Compare
Maintainer ReviewRebased onto main (clean, no conflicts) and applied the following fixes: 1. Keydown regression fix (critical)The delegated Fix: 2. Test cleanup
3. Removed noise
Test results
|
crivetimihai
left a comment
There was a problem hiding this comment.
Approved. The event delegation approach is correct, consistent with existing patterns (token panel, tool-ops panel), and more CSP-friendly than inline handlers. Fixed the keydown regression where Space/Enter was swallowed in form controls. All 805 JS tests pass.
…nnerHTML sanitizer (#3288) * Add event listener for plugin filters Signed-off-by: Gabriel Costa <gabrielcg@proton.me> * fix(tests): clean up plugin filter tests — lint fixes and scrollIntoView polyfill - Remove unused imports (beforeEach) and destructured variables (card1, card2, hookFilter) - Add HTMLElement.prototype.scrollIntoView polyfill for JSDOM environment - Revert unrelated package-lock.json changes Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix(ui): prevent keydown handler from swallowing Space/Enter in form controls The delegated keydown listener on #plugins-panel was calling preventDefault() unconditionally for Enter and Space before checking whether the target was actually an actionable element (badge, button). This blocked typing spaces in the search box and pressing Enter on select dropdowns. Fix: dispatchPluginAction() now returns a boolean indicating whether it matched an action. The keydown handler only calls preventDefault() when an action was actually dispatched. Add regression tests: Space in #plugin-search and Enter on #plugin-mode-filter must not have defaultPrevented set. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Gabriel Costa <gabrielcg@proton.me> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
…nnerHTML sanitizer (#3288) * Add event listener for plugin filters Signed-off-by: Gabriel Costa <gabrielcg@proton.me> * fix(tests): clean up plugin filter tests — lint fixes and scrollIntoView polyfill - Remove unused imports (beforeEach) and destructured variables (card1, card2, hookFilter) - Add HTMLElement.prototype.scrollIntoView polyfill for JSDOM environment - Revert unrelated package-lock.json changes Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * fix(ui): prevent keydown handler from swallowing Space/Enter in form controls The delegated keydown listener on #plugins-panel was calling preventDefault() unconditionally for Enter and Space before checking whether the target was actually an actionable element (badge, button). This blocked typing spaces in the search box and pressing Enter on select dropdowns. Fix: dispatchPluginAction() now returns a boolean indicating whether it matched an action. The keydown handler only calls preventDefault() when an action was actually dispatched. Add regression tests: Space in #plugin-search and Enter on #plugin-mode-filter must not have defaultPrevented set. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Gabriel Costa <gabrielcg@proton.me> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> Co-authored-by: Mihai Criveti <crivetimihai@gmail.com> Signed-off-by: Yosief Eyob <yosiefogbazion@gmail.com>
🐛 Bug-fix PR
📌 Summary
Closes #3271
🔁 Reproduction Steps
🐞 Root Cause
admin.js globally patches Element.prototype.innerHTML via installInnerHtmlGuard() (admin.js:532) as a security measure. The patched setter calls sanitizeHtmlForInsertion() (admin.js:461–505)
which strips every attribute beginning with "on" (line 485–488):
When the plugins tab is first clicked (admin.js:8266–8310), the handler fetches plugins_partial.html and injects it with:
This silently strips every inline event handler in plugins_partial.html before any user interaction:
window.filterPluginsand the other related functions are defined byinitializePluginFunctions()(called right after), but there are no events bound to the DOM elements, so nothing calls them.💡 Fix Description
Use two container-level event listeners in initializePluginFunctions():