Harden CSP by removing unsafe-inline for scripts

Refactored all inline event handlers (onclick, onchange, onsubmit) to use
addEventListener with data-action attributes and event delegation pattern.

Changes:
- views/*.php: Replaced inline handlers with data-action attributes
- views/admin/*.php: Same refactoring for all admin views
- assets/js/dashboard.js: Added event delegation for bulk/quick action modals
- assets/js/ticket.js: Added event delegation for dynamic elements
- assets/js/markdown.js: Refactored toolbar button handlers
- assets/js/keyboard-shortcuts.js: Refactored modal close button
- SecurityHeadersMiddleware.php: Enabled strict CSP with nonces

The CSP now uses script-src 'self' 'nonce-{nonce}' instead of 'unsafe-inline',
significantly improving XSS protection.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-30 13:15:55 -05:00
parent 37be81b3e2
commit c3f7593f3c
13 changed files with 564 additions and 158 deletions

View File

@@ -356,17 +356,36 @@ function createEditorToolbar(textareaId, containerId) {
const toolbar = document.createElement('div');
toolbar.className = 'editor-toolbar';
toolbar.innerHTML = `
<button type="button" onclick="toolbarBold('${textareaId}')" title="Bold (Ctrl+B)"><b>B</b></button>
<button type="button" onclick="toolbarItalic('${textareaId}')" title="Italic (Ctrl+I)"><i>I</i></button>
<button type="button" onclick="toolbarCode('${textareaId}')" title="Code">&lt;/&gt;</button>
<button type="button" data-toolbar-action="bold" data-textarea="${textareaId}" title="Bold (Ctrl+B)"><b>B</b></button>
<button type="button" data-toolbar-action="italic" data-textarea="${textareaId}" title="Italic (Ctrl+I)"><i>I</i></button>
<button type="button" data-toolbar-action="code" data-textarea="${textareaId}" title="Code">&lt;/&gt;</button>
<span class="toolbar-separator"></span>
<button type="button" onclick="toolbarHeading('${textareaId}')" title="Heading">H</button>
<button type="button" onclick="toolbarList('${textareaId}')" title="List">≡</button>
<button type="button" onclick="toolbarQuote('${textareaId}')" title="Quote">"</button>
<button type="button" data-toolbar-action="heading" data-textarea="${textareaId}" title="Heading">H</button>
<button type="button" data-toolbar-action="list" data-textarea="${textareaId}" title="List">≡</button>
<button type="button" data-toolbar-action="quote" data-textarea="${textareaId}" title="Quote">"</button>
<span class="toolbar-separator"></span>
<button type="button" onclick="toolbarLink('${textareaId}')" title="Link">🔗</button>
<button type="button" data-toolbar-action="link" data-textarea="${textareaId}" title="Link">🔗</button>
`;
// Add event delegation for toolbar buttons
toolbar.addEventListener('click', function(e) {
const btn = e.target.closest('[data-toolbar-action]');
if (!btn) return;
const action = btn.dataset.toolbarAction;
const targetId = btn.dataset.textarea;
switch (action) {
case 'bold': toolbarBold(targetId); break;
case 'italic': toolbarItalic(targetId); break;
case 'code': toolbarCode(targetId); break;
case 'heading': toolbarHeading(targetId); break;
case 'list': toolbarList(targetId); break;
case 'quote': toolbarQuote(targetId); break;
case 'link': toolbarLink(targetId); break;
}
});
container.insertBefore(toolbar, container.firstChild);
}