diff --git a/public/index.html b/public/index.html index 88d1179..2b9ba31 100644 --- a/public/index.html +++ b/public/index.html @@ -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, '''); function kpi(label, value, klass = '') { return `
${label}
${value}
`; @@ -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 => `${p.pair}${fmtTs(p.entryTs)}${fmt(p.entryPrice, priceDec(p.entryPrice))}${fmt(p.qty, 4)}${fmtTs(p.exitDueTs)}`), + trumpPositions.map(p => `${esc(p.pair)}${fmtTs(p.entryTs)}${fmt(p.entryPrice, priceDec(p.entryPrice))}${fmt(p.qty, 4)}${fmtTs(p.exitDueTs)}`), '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' - ? `${e.ref.slice(0, 12)}…` - : `${e.ref.slice(0, 24)}${e.ref.length > 24 ? '…' : ''}` - : '–'; - return `${fmtTs(e.eventTs)}${e.source ?? '–'}${e.token ?? '–'}${e.instrument ?? '–'}${e.notionalUsd != null ? fmt(e.notionalUsd) + ' $' : '–'}${ref}`; + // 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 ? `${refLabel}` : refLabel; + return `${fmtTs(e.eventTs)}${esc(e.source ?? '–')}${esc(e.token ?? '–')}${esc(e.instrument ?? '–')}${e.notionalUsd != null ? fmt(e.notionalUsd) + ' $' : '–'}${ref}`; }), 'Keine Events vorhanden.'); document.getElementById('trump-trades').innerHTML = table( ['Entry', 'Exit', 'Entry-Preis', 'Exit-Preis', 'PnL $', 'Grund'], - trumpTradesAll.map(t => `${fmtTs(new Date(t.entryTs).getTime())}${fmtTs(new Date(t.exitTs).getTime())}${fmt(t.entryPrice, 4)}${fmt(t.exitPrice, 4)}${sign(t.pnl)}${t.exitReason}`), + trumpTradesAll.map(t => `${fmtTs(new Date(t.entryTs).getTime())}${fmtTs(new Date(t.exitTs).getTime())}${fmt(t.entryPrice, 4)}${fmt(t.exitPrice, 4)}${sign(t.pnl)}${esc(t.exitReason)}`), 'Noch keine Trump-Trades.'); const eng = pf.engine || {};