fix: XSS vulnerabilities and error handling in import window
- Escape creators, year, item_type fields in renderImportItemList and showItemPreview (was only escaping title and abstract) - loadImportState uses api() wrapper for proper error handling - generateSelectedCards uses api() wrapper with try/catch - Remove duplicate isImportWindowOpen in state object Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
aae8a6340a
commit
c6e1d20c68
@ -495,7 +495,6 @@
|
|||||||
currentCards: [],
|
currentCards: [],
|
||||||
collectionTree: [],
|
collectionTree: [],
|
||||||
isImportWindowOpen: false,
|
isImportWindowOpen: false,
|
||||||
isImportWindowOpen: false,
|
|
||||||
windowX: typeof windowSessionState.x === "number" ? windowSessionState.x : null,
|
windowX: typeof windowSessionState.x === "number" ? windowSessionState.x : null,
|
||||||
windowY: typeof windowSessionState.y === "number" ? windowSessionState.y : null,
|
windowY: typeof windowSessionState.y === "number" ? windowSessionState.y : null,
|
||||||
windowWidth: typeof windowSessionState.width === "number" ? windowSessionState.width : null,
|
windowWidth: typeof windowSessionState.width === "number" ? windowSessionState.width : null,
|
||||||
@ -1140,8 +1139,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadImportState() {
|
async function loadImportState() {
|
||||||
const resp = await fetch(`/api/projects/${state.currentProjectId}/import-state`);
|
const data = await api(`/api/projects/${state.currentProjectId}/import-state`);
|
||||||
const data = await resp.json();
|
|
||||||
state.importItems = data.items;
|
state.importItems = data.items;
|
||||||
renderImportItemList();
|
renderImportItemList();
|
||||||
updateImportCounts(data.pending_count, data.done_count);
|
updateImportCounts(data.pending_count, data.done_count);
|
||||||
@ -1159,7 +1157,7 @@
|
|||||||
<input type="checkbox" ${isDone ? 'disabled' : ''} data-key="${item.item_key}">
|
<input type="checkbox" ${isDone ? 'disabled' : ''} data-key="${item.item_key}">
|
||||||
<div>
|
<div>
|
||||||
<div class="item-title">${escapeHtml(item.title)}</div>
|
<div class="item-title">${escapeHtml(item.title)}</div>
|
||||||
<div class="item-meta">${item.creators.join(', ')} · ${item.year || 'n.d.'}</div>
|
<div class="item-meta">${escapeHtml(item.creators.join(', '))} · ${escapeHtml(item.year || 'n.d.')}</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
if (!isDone) {
|
if (!isDone) {
|
||||||
@ -1178,7 +1176,7 @@
|
|||||||
function showItemPreview(item) {
|
function showItemPreview(item) {
|
||||||
document.getElementById('item-preview').innerHTML = `
|
document.getElementById('item-preview').innerHTML = `
|
||||||
<div class="preview-title">${escapeHtml(item.title)}</div>
|
<div class="preview-title">${escapeHtml(item.title)}</div>
|
||||||
<div class="preview-meta">${item.creators.join(', ')} · ${item.year || 'n.d.'} · ${item.item_type}</div>
|
<div class="preview-meta">${escapeHtml(item.creators.join(', '))} · ${escapeHtml(item.year || 'n.d.')} · ${escapeHtml(item.item_type)}</div>
|
||||||
<div class="preview-abstract">${escapeHtml(item.abstract || 'No abstract available.')}</div>
|
<div class="preview-abstract">${escapeHtml(item.abstract || 'No abstract available.')}</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -1200,12 +1198,10 @@
|
|||||||
updateGenerateButton();
|
updateGenerateButton();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const resp = await fetch(`/api/projects/${state.currentProjectId}/cards/generate`, {
|
const data = await api(`/api/projects/${state.currentProjectId}/cards/generate`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ item_keys: keys }),
|
body: JSON.stringify({ item_keys: keys }),
|
||||||
});
|
});
|
||||||
const data = await resp.json();
|
|
||||||
|
|
||||||
data.items.forEach(updated => {
|
data.items.forEach(updated => {
|
||||||
const item = state.importItems.find(i => i.item_key === updated.item_key);
|
const item = state.importItems.find(i => i.item_key === updated.item_key);
|
||||||
@ -1220,6 +1216,8 @@
|
|||||||
done: state.importItems.filter(i => i.card_status === 'done').length,
|
done: state.importItems.filter(i => i.card_status === 'done').length,
|
||||||
};
|
};
|
||||||
updateImportCounts(counts.pending, counts.done);
|
updateImportCounts(counts.pending, counts.done);
|
||||||
|
} catch (error) {
|
||||||
|
setStatus(elements.windowCollectionStatus, error.message, true);
|
||||||
} finally {
|
} finally {
|
||||||
state.isGenerating = false;
|
state.isGenerating = false;
|
||||||
updateGenerateButton();
|
updateGenerateButton();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user