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:
@@ -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 sign = (n, d = 2) => (n > 0 ? '+' : '') + fmt(n, d);
|
||||
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, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
||||
|
||||
function kpi(label, value, klass = '') {
|
||||
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(
|
||||
['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.');
|
||||
|
||||
document.getElementById('trump-events').innerHTML = table(
|
||||
['Zeit', 'Quelle', 'Coin', 'Instrument', 'Notional $', 'Ref'],
|
||||
trumpEvents.map(e => {
|
||||
const ref = e.ref
|
||||
? e.source === 'onchain'
|
||||
? `<a href="https://etherscan.io/tx/${e.ref}" target="_blank" style="color:var(--accent)">${e.ref.slice(0, 12)}…</a>`
|
||||
: `<a href="${e.ref}" target="_blank" style="color:var(--accent)">${e.ref.slice(0, 24)}${e.ref.length > 24 ? '…' : ''}</a>`
|
||||
: '–';
|
||||
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>`;
|
||||
// href nur mit https-Schema (Truth-Refs kommen aus externem Feed)
|
||||
const safeHref = e.source === 'onchain'
|
||||
? `https://etherscan.io/tx/${esc(e.ref)}`
|
||||
: /^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 ? '…' : ''));
|
||||
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.');
|
||||
|
||||
document.getElementById('trump-trades').innerHTML = table(
|
||||
['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.');
|
||||
|
||||
const eng = pf.engine || {};
|
||||
|
||||
Reference in New Issue
Block a user