fix(ui): jsAttrEsc for inline onclick handlers — apostrophe in product_name broke SKU mapping modal
HTML parser decodes ' back to ' inside onclick="..." before the JS parser
runs, so esc() left inline handlers vulnerable: product names containing an
apostrophe terminated the JS string literal ("missing ) after argument list").
New jsAttrEsc() escapes for JS-string-inside-HTML-attribute (\\, ', \n first;
then &, ", <, >). Applied to all inline onclick sites that interpolate
user-controlled sku/product_name/codmat: shared.js detail modal (lines
879/939), missing_skus.html (4 sites), mappings.js (3 sites).
Cache-bust: shared.js v49→50, mappings.js v17→18.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -169,7 +169,7 @@
|
||||
|
||||
<script>window.ROOT_PATH = "{{ rp }}";</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="{{ rp }}/static/js/shared.js?v=49"></script>
|
||||
<script src="{{ rp }}/static/js/shared.js?v=50"></script>
|
||||
<script>
|
||||
// Dark mode toggle
|
||||
function toggleDarkMode() {
|
||||
|
||||
@@ -159,5 +159,5 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{{ request.scope.get('root_path', '') }}/static/js/mappings.js?v=17"></script>
|
||||
<script src="{{ request.scope.get('root_path', '') }}/static/js/mappings.js?v=18"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -187,7 +187,7 @@ function renderMissingSkusTable(skus, data) {
|
||||
|
||||
tbody.innerHTML = skus.map(s => {
|
||||
const trAttrs = !s.resolved
|
||||
? ` style="cursor:pointer" onclick="openMapModal('${esc(s.sku)}', '${esc(s.product_name || '')}')"`
|
||||
? ` style="cursor:pointer" onclick="openMapModal('${jsAttrEsc(s.sku)}', '${jsAttrEsc(s.product_name || '')}')"`
|
||||
: '';
|
||||
return `<tr${trAttrs}>
|
||||
<td>${s.resolved ? '<span class="dot dot-green"></span>' : '<span class="dot dot-yellow"></span>'}</td>
|
||||
@@ -195,7 +195,7 @@ function renderMissingSkusTable(skus, data) {
|
||||
<td class="truncate" style="max-width:300px">${esc(s.product_name || '-')}</td>
|
||||
<td>
|
||||
${!s.resolved
|
||||
? `<a href="#" class="btn-map-icon" onclick="event.stopPropagation(); openMapModal('${esc(s.sku)}', '${esc(s.product_name || '')}'); return false;" title="Mapeaza">
|
||||
? `<a href="#" class="btn-map-icon" onclick="event.stopPropagation(); openMapModal('${jsAttrEsc(s.sku)}', '${jsAttrEsc(s.product_name || '')}'); return false;" title="Mapeaza">
|
||||
<i class="bi bi-link-45deg"></i>
|
||||
</a>`
|
||||
: `<small class="text-muted">${s.resolved_at ? new Date(s.resolved_at).toLocaleDateString('ro-RO') : ''}</small>`}
|
||||
@@ -206,10 +206,10 @@ function renderMissingSkusTable(skus, data) {
|
||||
if (mobileList) {
|
||||
mobileList.innerHTML = skus.map(s => {
|
||||
const actionHtml = !s.resolved
|
||||
? `<a href="#" class="btn-map-icon" onclick="event.stopPropagation(); openMapModal('${esc(s.sku)}', '${esc(s.product_name || '')}'); return false;"><i class="bi bi-link-45deg"></i></a>`
|
||||
? `<a href="#" class="btn-map-icon" onclick="event.stopPropagation(); openMapModal('${jsAttrEsc(s.sku)}', '${jsAttrEsc(s.product_name || '')}'); return false;"><i class="bi bi-link-45deg"></i></a>`
|
||||
: `<small class="text-muted">${s.resolved_at ? new Date(s.resolved_at).toLocaleDateString('ro-RO') : ''}</small>`;
|
||||
const flatRowAttrs = !s.resolved
|
||||
? ` onclick="openMapModal('${esc(s.sku)}', '${esc(s.product_name || '')}')" style="cursor:pointer"`
|
||||
? ` onclick="openMapModal('${jsAttrEsc(s.sku)}', '${jsAttrEsc(s.product_name || '')}')" style="cursor:pointer"`
|
||||
: '';
|
||||
return `<div class="flat-row"${flatRowAttrs}>
|
||||
${s.resolved ? '<span class="dot dot-green"></span>' : '<span class="dot dot-yellow"></span>'}
|
||||
|
||||
Reference in New Issue
Block a user