mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-07 15:32:21 +02:00
fix(security): harden browser security boundaries
Reject unsafe metric ledger keys, keep email HTML inspection inert, and construct gallery thumbnails structurally. Add adversarial browser regressions for the remaining CodeQL security boundaries.
This commit is contained in:
@@ -1392,10 +1392,13 @@ export function recordSessionMetricsCost(metrics, sessionId, selectedEndpointUrl
|
||||
const sid = sessionId || (
|
||||
window.sessionModule && window.sessionModule.getCurrentSessionId()
|
||||
);
|
||||
if (!sid || cost === null) return cost;
|
||||
if (typeof sid !== 'string' || !sid || cost === null) return cost;
|
||||
const runId = typeof metrics._costRecordId === 'string'
|
||||
? metrics._costRecordId.trim()
|
||||
: '';
|
||||
// Never resolve ledger entries through Object.prototype or its constructor.
|
||||
if (['__proto__', 'prototype', 'constructor'].includes(sid)
|
||||
|| ['__proto__', 'prototype', 'constructor'].includes(runId)) return cost;
|
||||
if ((metrics._costRecorded || metrics._costRecordPending) && !runId) return cost;
|
||||
// Recorded is only set once the write actually runs; pending covers the
|
||||
// window while the write waits on the cross-tab lock, so a replay in that
|
||||
|
||||
+14
-9
@@ -3416,9 +3416,9 @@ import { attachColorPicker } from './colorPicker.js?v=20260910eyedropper1';
|
||||
}).join('');
|
||||
|
||||
if (!changed && /<[^>]+>/.test(text) && typeof document !== 'undefined') {
|
||||
const probe = document.createElement('div');
|
||||
const probe = document.createElement('template');
|
||||
probe.innerHTML = text;
|
||||
const plain = (probe.innerText || probe.textContent || '').trim();
|
||||
const plain = (probe.content.textContent || '').trim();
|
||||
const plainClean = plain ? _sanitizeOutgoingEmailBody(plain) : plain;
|
||||
if (plainClean !== plain) return plainClean;
|
||||
}
|
||||
@@ -3530,9 +3530,9 @@ import { attachColorPicker } from './colorPicker.js?v=20260910eyedropper1';
|
||||
|
||||
function _emailHtmlToPlainText(html) {
|
||||
if (typeof document === 'undefined') return String(html || '');
|
||||
const d = document.createElement('div');
|
||||
const d = document.createElement('template');
|
||||
d.innerHTML = String(html || '');
|
||||
return d.innerText || d.textContent || '';
|
||||
return d.content.textContent || '';
|
||||
}
|
||||
|
||||
function _sanitizedRichTextHtml(rich) {
|
||||
@@ -5393,15 +5393,20 @@ import { attachColorPicker } from './colorPicker.js?v=20260910eyedropper1';
|
||||
row.dataset.id = item.id || '';
|
||||
row.dataset.kind = kind;
|
||||
if (kind === 'gallery') {
|
||||
const src = item.url ? `${API_BASE}${item.url}` : '';
|
||||
row.innerHTML = `
|
||||
<span class="email-odysseus-attach-dot" aria-hidden="true"></span>
|
||||
<span class="email-odysseus-attach-thumb">${src ? `<img src="${_escHtml(src)}" alt="">` : ''}</span>
|
||||
<span class="email-odysseus-attach-thumb"></span>
|
||||
<span class="email-odysseus-attach-main">
|
||||
<span class="email-odysseus-attach-title">${_escHtml(label)}</span>
|
||||
<span class="email-odysseus-attach-meta">${_escHtml(item.filename || 'image')}</span>
|
||||
</span>
|
||||
`;
|
||||
if (item.url) {
|
||||
const img = document.createElement('img');
|
||||
img.alt = '';
|
||||
img.src = `${API_BASE}${item.url}`;
|
||||
row.querySelector('.email-odysseus-attach-thumb').appendChild(img);
|
||||
}
|
||||
} else {
|
||||
row.innerHTML = `
|
||||
<span class="email-odysseus-attach-dot" aria-hidden="true"></span>
|
||||
@@ -6730,10 +6735,10 @@ import { attachColorPicker } from './colorPicker.js?v=20260910eyedropper1';
|
||||
.trim();
|
||||
};
|
||||
const splitCurrent = _splitEmailReplyQuote(currentBody);
|
||||
const ownBody = document.createElement('div');
|
||||
const ownBody = document.createElement('template');
|
||||
ownBody.innerHTML = String(splitCurrent.body || '');
|
||||
const ownText = (ownBody.textContent || '').trim();
|
||||
const isReplaceableDraft = (!ownText && !ownBody.querySelector('img,video,audio,iframe,table')) || /^(\[AI reply draft will appear here\]|Drafting AI reply)/i.test(ownText);
|
||||
const ownText = (ownBody.content.textContent || '').trim();
|
||||
const isReplaceableDraft = (!ownText && !ownBody.content.querySelector('img,video,audio,iframe,table')) || /^(\[AI reply draft will appear here\]|Drafting AI reply)/i.test(ownText);
|
||||
if (!isReplaceableDraft) {
|
||||
if (uiModule) uiModule.showToast('Reply already has text');
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user