fix: security, correctness, and test hardening from review
- routes.py: reject glob wildcards (* ? [ ]) in skill name param to prevent rglob wildcard injection when serving linked files - panels.js: replace inline onclick+esc() with data-* attributes and addEventListener for skill tag removal and linked-file clicks; esc() is HTML-safe but not JS-safe -- apostrophes in names caused JS syntax errors and _cronSelectedSkills array corruption - ui.js: fix _fmtTokens(null/undefined) returning 'null'/'undefined' by guarding with (!n||n<0) -> '0'; add data-role attribute to msg-row elements so usage badge correctly targets the last assistant row instead of the last row regardless of speaker - tests: rename test_sprint24.py -> test_sprint23.py (wrong sprint #); add 3 new tests: path traversal rejection, wildcard name rejection, cron create with skills; strengthen existing tests to assert field presence explicitly (was using .get(field, 0)==0 which never caught a missing field)
This commit is contained in:
10
static/ui.js
10
static/ui.js
@@ -82,7 +82,7 @@ let _scrollPinned=true;
|
||||
_scrollPinned=nearBottom;
|
||||
});
|
||||
})();
|
||||
function _fmtTokens(n){if(n>=1e6)return(n/1e6).toFixed(1)+'M';if(n>=1e3)return(n/1e3).toFixed(1)+'k';return String(n);}
|
||||
function _fmtTokens(n){if(!n||n<0)return'0';if(n>=1e6)return(n/1e6).toFixed(1)+'M';if(n>=1e3)return(n/1e3).toFixed(1)+'k';return String(n);}
|
||||
|
||||
function scrollIfPinned(){
|
||||
if(!_scrollPinned) return;
|
||||
@@ -426,7 +426,7 @@ function renderMessages(){
|
||||
inner.appendChild(thinkRow);
|
||||
}
|
||||
const row=document.createElement('div');row.className='msg-row';
|
||||
row.dataset.msgIdx=rawIdx;
|
||||
row.dataset.msgIdx=rawIdx;row.dataset.role=m.role||'assistant';
|
||||
let filesHtml='';
|
||||
if(m.attachments&&m.attachments.length)
|
||||
filesHtml=`<div class="msg-files">${m.attachments.map(f=>`<div class="msg-file-badge">📎 ${esc(f)}</div>`).join('')}</div>`;
|
||||
@@ -488,9 +488,11 @@ function renderMessages(){
|
||||
else inner.appendChild(frag);
|
||||
}
|
||||
}
|
||||
// Render per-turn usage badge on the last assistant message (if usage data exists)
|
||||
// Render usage badge on the last assistant message row (if usage data exists)
|
||||
if(S.session&&(S.session.input_tokens||S.session.output_tokens)){
|
||||
const lastAssist=inner.querySelector('.msg-row:last-child');
|
||||
const rows=inner.querySelectorAll('.msg-row');
|
||||
let lastAssist=null;
|
||||
for(let i=rows.length-1;i>=0;i--){if(rows[i].dataset.role==='assistant'){lastAssist=rows[i];break;}}
|
||||
if(lastAssist&&!lastAssist.querySelector('.msg-usage')){
|
||||
const usage=document.createElement('div');
|
||||
usage.className='msg-usage';
|
||||
|
||||
Reference in New Issue
Block a user