fix: XSS-Härtung im Trump-Tab (esc()-Helper, https-Allowlist für Truth-Links, noopener)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 09:00:55 +00:00
parent ed2fdf6c0a
commit 52cd31bf42

View File

@@ -123,6 +123,8 @@ const fmtTs = (ts) => ts == null ? '' : new Date(ts).toLocaleString('de-DE',
const cls = (n) => n > 0 ? 'pos' : n < 0 ? 'neg' : ''; const cls = (n) => n > 0 ? 'pos' : n < 0 ? 'neg' : '';
const sign = (n, d = 2) => (n > 0 ? '+' : '') + fmt(n, d); const sign = (n, d = 2) => (n > 0 ? '+' : '') + fmt(n, d);
const priceDec = (p) => p < 1 ? 5 : p < 10 ? 4 : p < 1000 ? 2 : 0; const priceDec = (p) => p < 1 ? 5 : p < 10 ? 4 : p < 1000 ? 2 : 0;
// HTML-Escaping für DB-Felder mit externem Ursprung (Truth-Feed/On-chain)
const esc = (s) => String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
function kpi(label, value, klass = '') { function kpi(label, value, klass = '') {
return `<div class="kpi"><div class="label">${label}</div><div class="value ${klass}">${value}</div></div>`; return `<div class="kpi"><div class="label">${label}</div><div class="value ${klass}">${value}</div></div>`;
@@ -525,24 +527,25 @@ async function refresh() {
document.getElementById('trump-positions').innerHTML = table( document.getElementById('trump-positions').innerHTML = table(
['Pair', 'Entry-Zeit', 'Entry-Preis', 'Qty', 'Exit fällig'], ['Pair', 'Entry-Zeit', 'Entry-Preis', 'Qty', 'Exit fällig'],
trumpPositions.map(p => `<tr><td><span class="tag long">${p.pair}</span></td><td>${fmtTs(p.entryTs)}</td><td>${fmt(p.entryPrice, priceDec(p.entryPrice))}</td><td>${fmt(p.qty, 4)}</td><td>${fmtTs(p.exitDueTs)}</td></tr>`), trumpPositions.map(p => `<tr><td><span class="tag long">${esc(p.pair)}</span></td><td>${fmtTs(p.entryTs)}</td><td>${fmt(p.entryPrice, priceDec(p.entryPrice))}</td><td>${fmt(p.qty, 4)}</td><td>${fmtTs(p.exitDueTs)}</td></tr>`),
'Keine offenen Positionen.'); 'Keine offenen Positionen.');
document.getElementById('trump-events').innerHTML = table( document.getElementById('trump-events').innerHTML = table(
['Zeit', 'Quelle', 'Coin', 'Instrument', 'Notional $', 'Ref'], ['Zeit', 'Quelle', 'Coin', 'Instrument', 'Notional $', 'Ref'],
trumpEvents.map(e => { trumpEvents.map(e => {
const ref = e.ref // href nur mit https-Schema (Truth-Refs kommen aus externem Feed)
? e.source === 'onchain' const safeHref = e.source === 'onchain'
? `<a href="https://etherscan.io/tx/${e.ref}" target="_blank" style="color:var(--accent)">${e.ref.slice(0, 12)}…</a>` ? `https://etherscan.io/tx/${esc(e.ref)}`
: `<a href="${e.ref}" target="_blank" style="color:var(--accent)">${e.ref.slice(0, 24)}${e.ref.length > 24 ? '…' : ''}</a>` : /^https:\/\//i.test(e.ref) ? esc(e.ref) : null;
: ''; const refLabel = esc(e.source === 'onchain' ? e.ref.slice(0, 12) + '…' : e.ref.slice(0, 24) + (e.ref.length > 24 ? '…' : ''));
return `<tr><td>${fmtTs(e.eventTs)}</td><td>${e.source ?? ''}</td><td>${e.token ?? ''}</td><td>${e.instrument ?? ''}</td><td>${e.notionalUsd != null ? fmt(e.notionalUsd) + ' $' : ''}</td><td style="text-align:left">${ref}</td></tr>`; const ref = safeHref ? `<a href="${safeHref}" target="_blank" rel="noopener" style="color:var(--accent)">${refLabel}</a>` : refLabel;
return `<tr><td>${fmtTs(e.eventTs)}</td><td>${esc(e.source ?? '')}</td><td>${esc(e.token ?? '')}</td><td>${esc(e.instrument ?? '')}</td><td>${e.notionalUsd != null ? fmt(e.notionalUsd) + ' $' : ''}</td><td style="text-align:left">${ref}</td></tr>`;
}), }),
'Keine Events vorhanden.'); 'Keine Events vorhanden.');
document.getElementById('trump-trades').innerHTML = table( document.getElementById('trump-trades').innerHTML = table(
['Entry', 'Exit', 'Entry-Preis', 'Exit-Preis', 'PnL $', 'Grund'], ['Entry', 'Exit', 'Entry-Preis', 'Exit-Preis', 'PnL $', 'Grund'],
trumpTradesAll.map(t => `<tr><td>${fmtTs(new Date(t.entryTs).getTime())}</td><td>${fmtTs(new Date(t.exitTs).getTime())}</td><td>${fmt(t.entryPrice, 4)}</td><td>${fmt(t.exitPrice, 4)}</td><td class="${cls(t.pnl)}">${sign(t.pnl)}</td><td>${t.exitReason}</td></tr>`), trumpTradesAll.map(t => `<tr><td>${fmtTs(new Date(t.entryTs).getTime())}</td><td>${fmtTs(new Date(t.exitTs).getTime())}</td><td>${fmt(t.entryPrice, 4)}</td><td>${fmt(t.exitPrice, 4)}</td><td class="${cls(t.pnl)}">${sign(t.pnl)}</td><td>${esc(t.exitReason)}</td></tr>`),
'Noch keine Trump-Trades.'); 'Noch keine Trump-Trades.');
const eng = pf.engine || {}; const eng = pf.engine || {};