replaced onclick handlers with event listeners to resolve possible xss vector from single quotes
This commit is contained in:
parent
7f94597706
commit
95b1a8507b
1 changed files with 52 additions and 7 deletions
|
|
@ -1834,9 +1834,22 @@ function _genreWhitelistRender(genres) {
|
|||
const searchVal = (document.getElementById('genre-whitelist-search')?.value || '').toLowerCase();
|
||||
const filtered = searchVal ? _genreWhitelistCache.filter(g => g.toLowerCase().includes(searchVal)) : _genreWhitelistCache;
|
||||
container.innerHTML = filtered.map(g =>
|
||||
`<span class="genre-chip">${escapeHtml(g)}<button class="genre-chip-x" onclick="_genreWhitelistRemove('${escapeHtml(g.replace(/'/g, "\\'"))}')">×</button></span>`
|
||||
`<span class="genre-chip">${escapeHtml(g)}<button class="genre-chip-x" data-genre="${escapeHtml(g)}">×</button></span>`
|
||||
).join('');
|
||||
if (countEl) countEl.textContent = `${_genreWhitelistCache.length} genres`;
|
||||
_initGenreChipClickHandler();
|
||||
}
|
||||
|
||||
function _initGenreChipClickHandler() {
|
||||
const container = document.getElementById('genre-whitelist-chips');
|
||||
if (!container) return;
|
||||
container.onclick = (e) => {
|
||||
const btn = e.target.closest('.genre-chip-x');
|
||||
if (btn) {
|
||||
e.preventDefault();
|
||||
_genreWhitelistRemove(btn.dataset.genre);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function _genreWhitelistRemove(genre) {
|
||||
|
|
@ -2787,19 +2800,32 @@ function renderApiKeys(keys) {
|
|||
container.innerHTML = keys.map(k => `
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; padding: 8px 10px; margin-bottom: 4px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.06); border-radius: 6px;">
|
||||
<div style="flex: 1; min-width: 0;">
|
||||
<div style="font-size: 13px; color: #e0e0e0; font-weight: 500;">${k.label || 'Unnamed'}</div>
|
||||
<div style="font-size: 13px; color: #e0e0e0; font-weight: 500;">${escapeHtml(k.label || 'Unnamed')}</div>
|
||||
<div style="font-size: 11px; color: #666; margin-top: 2px;">
|
||||
<code>${k.key_prefix || 'sk_...'}...</code>
|
||||
<code>${escapeHtml(k.key_prefix || 'sk_...')}...</code>
|
||||
· Created ${k.created_at ? new Date(k.created_at).toLocaleDateString() : 'unknown'}
|
||||
${k.last_used_at ? '· Last used ' + new Date(k.last_used_at).toLocaleDateString() : ''}
|
||||
</div>
|
||||
</div>
|
||||
<button onclick="revokeApiKey('${k.id}', '${(k.label || 'this key').replace(/'/g, "\\'")}')"
|
||||
<button class="revoke-api-key-btn" data-key-id="${escapeHtml(k.id)}" data-key-label="${escapeHtml(k.label || 'this key')}"
|
||||
style="padding: 4px 10px; background: rgba(255,82,82,0.1); border: 1px solid rgba(255,82,82,0.2); color: #ff5252; border-radius: 4px; cursor: pointer; font-size: 11px; white-space: nowrap;">
|
||||
Revoke
|
||||
</button>
|
||||
</div>
|
||||
`).join('');
|
||||
_initApiKeyClickHandler();
|
||||
}
|
||||
|
||||
function _initApiKeyClickHandler() {
|
||||
const container = document.getElementById('api-keys-list');
|
||||
if (!container) return;
|
||||
container.onclick = (e) => {
|
||||
const btn = e.target.closest('.revoke-api-key-btn');
|
||||
if (btn) {
|
||||
e.preventDefault();
|
||||
revokeApiKey(btn.dataset.keyId, btn.dataset.keyLabel);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function generateApiKey() {
|
||||
|
|
@ -3291,16 +3317,17 @@ async function loadHiFiInstances() {
|
|||
listEl.innerHTML = data.instances.map((inst, i) => {
|
||||
const enabledClass = inst.enabled ? '' : 'hifi-instance-disabled';
|
||||
const checkHtml = inst.enabled
|
||||
? `<span class="hifi-instance-toggle on" onclick="toggleHiFiInstance('${escapeHtml(inst.url)}')" title="Click to disable">✔</span>`
|
||||
: `<span class="hifi-instance-toggle off" onclick="toggleHiFiInstance('${escapeHtml(inst.url)}')" title="Click to enable">✘</span>`;
|
||||
? `<span class="hifi-instance-toggle on" data-url="${escapeHtml(inst.url)}" title="Click to disable">✔</span>`
|
||||
: `<span class="hifi-instance-toggle off" data-url="${escapeHtml(inst.url)}" title="Click to enable">✘</span>`;
|
||||
return `<div class="hifi-instance-item${inst.enabled ? '' : ' disabled'}" draggable="true" data-url="${escapeHtml(inst.url)}">
|
||||
<span class="hifi-instance-grip">☰</span>
|
||||
<span class="hifi-instance-url">${escapeHtml(inst.url)}</span>
|
||||
${checkHtml}
|
||||
<span class="hifi-instance-remove" onclick="removeHiFiInstance('${escapeHtml(inst.url)}')" title="Remove instance">✖</span>
|
||||
<span class="hifi-instance-remove" data-url="${escapeHtml(inst.url)}" title="Remove instance">✖</span>
|
||||
</div>`;
|
||||
}).join('');
|
||||
_initHiFiDragDrop();
|
||||
_initHiFiClickHandlers();
|
||||
} catch (e) {
|
||||
listEl.innerHTML = `<div style="color:#f44336;font-size:0.85em;">Error loading instances: ${escapeHtml(e.message)}</div>`;
|
||||
}
|
||||
|
|
@ -3349,6 +3376,24 @@ function _initHiFiDragDrop() {
|
|||
});
|
||||
}
|
||||
|
||||
function _initHiFiClickHandlers() {
|
||||
const listEl = document.getElementById('hifi-instances-list');
|
||||
if (!listEl) return;
|
||||
listEl.onclick = (e) => {
|
||||
const toggle = e.target.closest('.hifi-instance-toggle');
|
||||
if (toggle) {
|
||||
e.preventDefault();
|
||||
toggleHiFiInstance(toggle.dataset.url);
|
||||
return;
|
||||
}
|
||||
const remove = e.target.closest('.hifi-instance-remove');
|
||||
if (remove) {
|
||||
e.preventDefault();
|
||||
removeHiFiInstance(remove.dataset.url);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function _saveHiFiInstanceOrder(urls) {
|
||||
try {
|
||||
await fetch('/api/hifi/instances/reorder', {
|
||||
|
|
|
|||
Loading…
Reference in a new issue