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:
@@ -77,7 +77,7 @@ $nonce = SecurityHeadersMiddleware::getNonce();
|
||||
<div class="ascii-frame-inner">
|
||||
<div class="detail-group">
|
||||
<label for="templateSelect">Use Template (Optional)</label>
|
||||
<select id="templateSelect" class="editable" onchange="loadTemplate()">
|
||||
<select id="templateSelect" class="editable" data-action="load-template">
|
||||
<option value="">-- No Template --</option>
|
||||
<?php if (isset($templates) && !empty($templates)): ?>
|
||||
<?php foreach ($templates as $template): ?>
|
||||
@@ -172,7 +172,7 @@ $nonce = SecurityHeadersMiddleware::getNonce();
|
||||
<div class="ascii-frame-inner">
|
||||
<div class="detail-group">
|
||||
<label for="visibility">Ticket Visibility</label>
|
||||
<select id="visibility" name="visibility" class="editable" onchange="toggleVisibilityGroups()">
|
||||
<select id="visibility" name="visibility" class="editable" data-action="toggle-visibility-groups">
|
||||
<option value="public" selected>Public - All authenticated users</option>
|
||||
<option value="internal">Internal - Specific groups only</option>
|
||||
<option value="confidential">Confidential - Creator, assignee, admins only</option>
|
||||
@@ -230,7 +230,7 @@ $nonce = SecurityHeadersMiddleware::getNonce();
|
||||
<div class="ascii-frame-inner">
|
||||
<div class="ticket-footer">
|
||||
<button type="submit" class="btn primary">Create Ticket</button>
|
||||
<button type="button" onclick="window.location.href='<?php echo $GLOBALS['config']['BASE_URL']; ?>/'" class="btn back-btn">Cancel</button>
|
||||
<button type="button" data-action="navigate" data-url="<?php echo $GLOBALS['config']['BASE_URL']; ?>/" class="btn back-btn">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -303,10 +303,32 @@ $nonce = SecurityHeadersMiddleware::getNonce();
|
||||
groupsContainer.style.display = 'block';
|
||||
} else {
|
||||
groupsContainer.style.display = 'none';
|
||||
// Uncheck all group checkboxes when hiding
|
||||
document.querySelectorAll('.visibility-group-checkbox').forEach(cb => cb.checked = false);
|
||||
}
|
||||
}
|
||||
|
||||
// Event delegation for data-action handlers
|
||||
document.addEventListener('click', function(event) {
|
||||
const target = event.target.closest('[data-action]');
|
||||
if (!target) return;
|
||||
|
||||
const action = target.dataset.action;
|
||||
if (action === 'navigate') {
|
||||
window.location.href = target.dataset.url;
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('change', function(event) {
|
||||
const target = event.target.closest('[data-action]');
|
||||
if (!target) return;
|
||||
|
||||
const action = target.dataset.action;
|
||||
if (action === 'load-template') {
|
||||
loadTemplate();
|
||||
} else if (action === 'toggle-visibility-groups') {
|
||||
toggleVisibilityGroups();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user