{"data":{"skill":{"slug":"jasonhmy-personal-finance-visualizer","name":"personal-finance-visualizer","icon":"📦","repo":"https://github.com/jasonhmy/personal-finance-visualizer/tree/1417365626a574afc4f2f611aba413f41e902c68/skills/personal-finance-visualizer","status":"approved","author":"jasonhmy","authorVersion":"1.4.1","skillstoreRevision":6},"audit":{"id":"e83d995e-f601-4291-814b-457072cda45f","skill_id":"5b81ea1f-b005-4d91-ab7a-8f1aded25e63","version":6,"content_hash":"v3:91eb8ebfd4726a061a961f2f0be972321cbd06b1:fcc820170d7b71482c0e989565390410d4a72c64aada81b7793adcb8986ce0bc:15ceb8e2e4f506f426179bd6ef8862e9ef1fa1dd5c1fab0be1c33f6e7998cc66:736b696c6c732f6a61736f6e686d792f706572736f6e616c2d66696e616e63652d76697375616c697a6572:2d1641f3a8261b92148c3c9029ab3529","risk_level":"safe","is_blocked":false,"safe_to_publish":true,"analysis_status":"ok","agent_auto_install_policy":"allowed","manual_install_policy":"allowed","summary":"All 108 static findings are false positives after contextual review of the flagged code and documentation. The renderer uses loopback delivery, escaped embedded data, restrictive browser policies, and explicit private exports; the separate update checker requests public GitHub metadata. No evidence found of credential collection, unauthorized financial-data transmission, malicious obfuscation, or prompt injection.","remediation":[],"risk_factor_evidence":[{"factor":"external_commands","evidence":[{"file":"assets/app.js","line_end":132,"line_start":132},{"file":"assets/app.js","line_end":361,"line_start":361},{"file":"assets/app.js","line_end":369,"line_start":369},{"file":"assets/app.js","line_end":392,"line_start":392},{"file":"SKILL.md","line_end":12,"line_start":12},{"file":"SKILL.md","line_end":16,"line_start":16},{"file":"SKILL.md","line_end":25,"line_start":25},{"file":"SKILL.md","line_end":29,"line_start":29},{"file":"SKILL.md","line_end":47,"line_start":47},{"file":"SKILL.md","line_end":52,"line_start":49},{"file":"SKILL.md","line_end":54,"line_start":52},{"file":"SKILL.md","line_end":54,"line_start":54},{"file":"SKILL.md","line_end":60,"line_start":56},{"file":"SKILL.md","line_end":62,"line_start":60}]},{"factor":"network","evidence":[{"file":"assets/app.js","line_end":432,"line_start":432},{"file":"scripts/check_update.py","line_end":6,"line_start":6},{"file":"scripts/check_update.py","line_end":22,"line_start":22},{"file":"scripts/check_update.py","line_end":24,"line_start":24},{"file":"scripts/check_update.py","line_end":11,"line_start":11},{"file":"scripts/check_update.py","line_end":29,"line_start":29},{"file":"scripts/report.py","line_end":97,"line_start":97},{"file":"scripts/report.py","line_end":82,"line_start":82},{"file":"scripts/report.py","line_end":96,"line_start":96},{"file":"scripts/report.py","line_end":97,"line_start":97},{"file":"SKILL.md","line_end":24,"line_start":24},{"file":"SKILL.md","line_end":7,"line_start":7},{"file":"SKILL.md","line_end":56,"line_start":56},{"file":"SKILL.md","line_end":56,"line_start":56}]},{"factor":"filesystem","evidence":[{"file":"assets/app.js","line_end":218,"line_start":218},{"file":"assets/app.js","line_end":345,"line_start":345},{"file":"assets/engine.js","line_end":9,"line_start":9},{"file":"assets/engine.js","line_end":16,"line_start":16},{"file":"assets/engine.js","line_end":29,"line_start":29},{"file":"scripts/report.py","line_end":69,"line_start":69}]},{"factor":"env_access","evidence":[{"file":"references/workflow.md","line_end":38,"line_start":38}]}],"critical_findings":[],"high_findings":[],"medium_findings":[],"low_findings":[],"dangerous_patterns":[],"files_scanned":12,"total_lines":1233,"audit_model":"codex","audited_at":"2026-10-23T23:00:53.779+00:00","created_at":"2026-10-24T00:02:34.138898+00:00","static_findings":[{"id":"external_commands:assets/app.js:132:ruby-shell-backtick-execution","file":"assets/app.js","pattern":"Ruby/shell backtick execution","snippet":"job.entries.forEach((item,index)=>{const value=values[index],sweep=sumValues?value/sumValues*Math.PI","category":"external_commands","line_end":132,"severity":"medium","line_start":132},{"id":"external_commands:assets/app.js:361:ruby-shell-backtick-execution","file":"assets/app.js","pattern":"Ruby/shell backtick execution","snippet":"function animateMove(before){if(reducedMotion())return;for(const [card,old]of before){moveAnimations","category":"external_commands","line_end":361,"severity":"medium","line_start":361},{"id":"external_commands:assets/app.js:369:ruby-shell-backtick-execution","file":"assets/app.js","pattern":"Ruby/shell backtick execution","snippet":"function tick(now){const d=dragging;if(!d?.active)return;d.card.style.transform=`translate3d(${(d.x-","category":"external_commands","line_end":369,"severity":"medium","line_start":369},{"id":"external_commands:assets/app.js:392:ruby-shell-backtick-execution","file":"assets/app.js","pattern":"Ruby/shell backtick execution","snippet":"const place=(animate=false)=>{const left=active.offsetLeft,width=active.offsetWidth;pill.style.trans","category":"external_commands","line_end":392,"severity":"medium","line_start":392},{"id":"network:assets/app.js:432:hardcoded-url","file":"assets/app.js","pattern":"Hardcoded URL","snippet":"Area-chart and calendar interaction references: https://github.com/Surajmaurya1/easyui","category":"network","line_end":432,"severity":"low","line_start":432},{"id":"filesystem:assets/app.js:218:node-js-fs-operations","file":"assets/app.js","pattern":"Node.js fs operations","snippet":"series.forEach((s,i)=>{const gradient=svg('linearGradient',{id:uid+'-'+i,x1:0,y1:0,x2:0,y2:1});gradi","category":"filesystem","line_end":218,"severity":"medium","line_start":218},{"id":"filesystem:assets/app.js:345:hidden-file-access","file":"assets/app.js","pattern":"Hidden file access","snippet":"try{const saved=JSON.parse(localStorage.getItem(layoutStorageKey));if(saved?.version===1)for(const v","category":"filesystem","line_end":345,"severity":"medium","line_start":345},{"id":"obfuscation:assets/app.js:233:array-join-empty-string","file":"assets/app.js","pattern":"Array join empty string","snippet":"series.forEach((_,s)=>{const path=positions.map((x,i)=>(i?'L':'M')+xAt(x).toFixed(2)+' '+yAt(frame.v","category":"obfuscation","line_end":233,"severity":"low","line_start":233},{"id":"sensitive:assets/app.js:27:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"let label=grain==='year'?p.key.slice(0,4)+(language==='zh'?'年':''):grain==='month'?new Intl.DateTime","category":"sensitive","line_end":27,"severity":"high","line_start":27},{"id":"sensitive:assets/app.js:29:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"const d=new Date(p.key+'T00:00:00Z');","category":"sensitive","line_end":29,"severity":"high","line_start":29},{"id":"sensitive:assets/app.js:30:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"const naturalEnd=grain==='year'?p.key.slice(0,4)+'-12-31':new Date(Date.UTC(d.getUTCFullYear(),d.get","category":"sensitive","line_end":30,"severity":"high","line_start":30},{"id":"sensitive:assets/app.js:64:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"document.addEventListener('keydown',event=>{if(event.key==='Escape')hideChartTip();});","category":"sensitive","line_end":64,"severity":"high","line_start":64},{"id":"sensitive:assets/app.js:87:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"search.addEventListener('keydown',event=>{if(event.key==='Escape'){event.preventDefault();event.stop","category":"sensitive","line_end":87,"severity":"high","line_start":87},{"id":"sensitive:assets/app.js:132:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"job.entries.forEach((item,index)=>{const value=values[index],sweep=sumValues?value/sumValues*Math.PI","category":"sensitive","line_end":132,"severity":"high","line_start":132},{"id":"sensitive:assets/app.js:134:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"frame.legend=(job.legend||[]).map(item=>{const value=progress===1?item.to:item.from+(item.to-item.fr","category":"sensitive","line_end":134,"severity":"high","line_start":134},{"id":"sensitive:assets/app.js:150:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"const positive=list.filter(x=>x.value>0),positiveTotal=sum(positive,'value'),entries=[],oldByKey=new","category":"sensitive","line_end":150,"severity":"high","line_start":150},{"id":"sensitive:assets/app.js:152:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"const drawing=svg('svg',{viewBox:'0 0 300 300','aria-label':title,role:'img'}),newByKey=new Map(posi","category":"sensitive","line_end":152,"severity":"high","line_start":152},{"id":"sensitive:assets/app.js:153:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"const keys=[...new Set([...(previous?.entries||[]).map(x=>x.key),...positive.map(x=>x.key)])];","category":"sensitive","line_end":153,"severity":"high","line_start":153},{"id":"sensitive:assets/app.js:156:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"if(item){const percentage=percentFormat.format(item.value/positiveTotal),shareText=(language==='zh'?","category":"sensitive","line_end":156,"severity":"high","line_start":156},{"id":"sensitive:assets/app.js:162:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"card.append(plot);const legend=e('div','ring-legend'),legendValues=[],oldLegend=new Map((previous?.l","category":"sensitive","line_end":162,"severity":"high","line_start":162},{"id":"sensitive:assets/app.js:199:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"const targets=series.map(s=>positions.map(x=>sample(points.map(p=>p[s.key]),x)));","category":"sensitive","line_end":199,"severity":"high","line_start":199},{"id":"sensitive:assets/app.js:201:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"const rawStep=Math.max(100,...points.flatMap(p=>series.map(s=>p[s.key])))*1.08/4;","category":"sensitive","line_end":201,"severity":"high","line_start":201},{"id":"sensitive:assets/app.js:206:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"const signature=mode+':'+grain+':'+currentPeriod.key,unchanged=previous?.signature===signature;","category":"sensitive","line_end":206,"severity":"high","line_start":206},{"id":"sensitive:assets/app.js:211:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"const selectedRows=(point,s)=>mode==='transfers'?point.rows.filter(r=>s.key==='a'?r.transferCents<0:","category":"sensitive","line_end":211,"severity":"high","line_start":211},{"id":"sensitive:assets/app.js:212:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"const readoutButtons=series.map((s,i)=>{const b=btn('','area-value',()=>{const selected=active===nul","category":"sensitive","line_end":212,"severity":"high","line_start":212},{"id":"sensitive:assets/app.js:213:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"const updateReadout=()=>{readoutDate.textContent=active===null?t('periodTotal'):points[active].key;s","category":"sensitive","line_end":213,"severity":"high","line_start":213},{"id":"sensitive:assets/app.js:232:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"indices.forEach((index,j)=>{const text=svg('text',{x:xAt(points.length>1?index/(points.length-1):.5)","category":"sensitive","line_end":232,"severity":"high","line_start":232},{"id":"sensitive:assets/app.js:234:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"if(active!==null){const x=xAt(points.length>1?active/(points.length-1):.5);crossLine.setAttribute('x","category":"sensitive","line_end":234,"severity":"high","line_start":234},{"id":"sensitive:assets/app.js:242:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"const lines=[p.key,...series.map(s=>s.label+' · '+money(p[s.key]))];","category":"sensitive","line_end":242,"severity":"high","line_start":242},{"id":"sensitive:assets/app.js:249:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"drawing.addEventListener('keydown',event=>{if(['ArrowLeft','ArrowRight','ArrowUp','ArrowDown','Home'","category":"sensitive","line_end":249,"severity":"high","line_start":249},{"id":"sensitive:assets/app.js:280:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"const bar=btn('','bar',()=>openRows(point.key+' · '+series,selected,mode));bar.style.height=(point[k","category":"sensitive","line_end":280,"severity":"high","line_start":280},{"id":"sensitive:assets/app.js:281:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"bar.setAttribute('aria-label',point.key+' · '+series+' · '+money(point[key]));bar.disabled=!point[ke","category":"sensitive","line_end":281,"severity":"high","line_start":281},{"id":"sensitive:assets/app.js:282:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"if(point[key]){const label=e('span','bar-value',number(point[key]));label.setAttribute('aria-hidden'","category":"sensitive","line_end":282,"severity":"high","line_start":282},{"id":"sensitive:assets/app.js:285:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"group.append(e('span','bar-date',point.key.slice(5)));bars.append(group);","category":"sensitive","line_end":285,"severity":"high","line_start":285},{"id":"sensitive:assets/app.js:290:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"function monthSummary(){const wrap=e('div','table-area'),table=e('table','data-table'),head=e('tr');","category":"sensitive","line_end":290,"severity":"high","line_start":290},{"id":"sensitive:assets/app.js:310:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"function choose(key){const selected=periods.find(p=>p.start<=key&&p.end>=key);if(!selected)return;cl","category":"sensitive","line_end":310,"severity":"high","line_start":310},{"id":"sensitive:assets/app.js:324:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"grid.addEventListener('keydown',event=>{const key=event.target.dataset.date;if(!key)return;let targe","category":"sensitive","line_end":324,"severity":"high","line_start":324},{"id":"sensitive:assets/app.js:333:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"panel.addEventListener('keydown',event=>{if(event.key==='Escape'){event.preventDefault();event.stopP","category":"sensitive","line_end":333,"severity":"high","line_start":333},{"id":"sensitive:assets/app.js:376:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"function cancel(event){if(event.key==='Escape'&&dragging){event.preventDefault();finish(false);}}","category":"sensitive","line_end":376,"severity":"high","line_start":376},{"id":"sensitive:assets/app.js:378:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"handle.addEventListener('keydown',event=>{if(event.altKey||event.ctrlKey||event.metaKey||dragging)re","category":"sensitive","line_end":378,"severity":"high","line_start":378},{"id":"sensitive:assets/app.js:406:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"const focused=document.activeElement?.getAttribute('aria-label');const periods=Finance.periods(data,","category":"sensitive","line_end":406,"severity":"high","line_start":406},{"id":"sensitive:assets/app.js:410:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"const controls=e('div','period-header');controls.append(e('h2','period-title',periodLabel(currentPer","category":"sensitive","line_end":410,"severity":"high","line_start":410},{"id":"sensitive:assets/app.js:412:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"const total=bucket(spending,r=>r.group,'personalCents',groupColors).map(x=>({...x,label:t(x.key)}));","category":"sensitive","line_end":412,"severity":"high","line_start":412},{"id":"sensitive:assets/app.js:414:certificate-key-files","file":"assets/app.js","pattern":"Certificate/key files","snippet":"const merchants=e('div','ring-grid three merchant-grid');const categories=bucket(spending.filter(r=>","category":"sensitive","line_end":414,"severity":"high","line_start":414},{"id":"blocker:assets/app.js:357:system-reconnaissance","file":"assets/app.js","pattern":"System reconnaissance","snippet":"const arrange=ids=>{for(const id of ids)if(byId.has(id))board.append(byId.get(id));};","category":"blocker","line_end":357,"severity":"low","line_start":357},{"id":"blocker:assets/app.js:392:system-reconnaissance","file":"assets/app.js","pattern":"System reconnaissance","snippet":"const place=(animate=false)=>{const left=active.offsetLeft,width=active.offsetWidth;pill.style.trans","category":"blocker","line_end":392,"severity":"low","line_start":392},{"id":"blocker:assets/app.js:412:system-reconnaissance","file":"assets/app.js","pattern":"System reconnaissance","snippet":"const total=bucket(spending,r=>r.group,'personalCents',groupColors).map(x=>({...x,label:t(x.key)}));","category":"blocker","line_end":412,"severity":"low","line_start":412},{"id":"blocker:assets/app.js:413:system-reconnaissance","file":"assets/app.js","pattern":"System reconnaissance","snippet":"const secondary=e('div','ring-grid three');for(const g of ['fixed','ai','housing'])secondary.append(","category":"blocker","line_end":413,"severity":"low","line_start":413},{"id":"blocker:assets/app.js:414:system-reconnaissance","file":"assets/app.js","pattern":"System reconnaissance","snippet":"const merchants=e('div','ring-grid three merchant-grid');const categories=bucket(spending.filter(r=>","category":"blocker","line_end":414,"severity":"low","line_start":414},{"id":"blocker:assets/app.js:419:system-reconnaissance","file":"assets/app.js","pattern":"System reconnaissance","snippet":"const income=rows.filter(r=>r.kind==='income');const slices=bucket(income,'category','amountCents',c","category":"blocker","line_end":419,"severity":"low","line_start":419},{"id":"blocker:assets/app.js:421:system-reconnaissance","file":"assets/app.js","pattern":"System reconnaissance","snippet":"const transfers=rows.filter(r=>r.kind.startsWith('transfer_'));const outgoing=transfers.filter(r=>r.","category":"blocker","line_end":421,"severity":"low","line_start":421},{"id":"filesystem:assets/engine.js:9:hidden-file-access","file":"assets/engine.js","pattern":"Hidden file access","snippet":"if (typeof value !== 'string' || !/^-?\\d+(\\.\\d{1,2})?$/.test(value)) fail('Amounts must be decimal s","category":"filesystem","line_end":9,"severity":"medium","line_start":9},{"id":"filesystem:assets/engine.js:16:hidden-file-access","file":"assets/engine.js","pattern":"Hidden file access","snippet":"if (typeof value !== 'string' || !/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) fail('Use ISO calendar dates.')","category":"filesystem","line_end":16,"severity":"medium","line_start":16},{"id":"filesystem:assets/engine.js:29:hidden-file-access","file":"assets/engine.js","pattern":"Hidden file access","snippet":"if (!/^[A-Z]{3}$/.test(input.currency || '')) fail('Use one uppercase three-letter currency code.');","category":"filesystem","line_end":29,"severity":"medium","line_start":29},{"id":"blocker:assets/engine.js:31:system-reconnaissance","file":"assets/engine.js","pattern":"System reconnaissance","snippet":"try { currency = new Intl.NumberFormat('en', {style: 'currency', currency: input.currency}).resolved","category":"blocker","line_end":31,"severity":"low","line_start":31},{"id":"blocker:assets/engine.js:39:system-reconnaissance","file":"assets/engine.js","pattern":"System reconnaissance","snippet":"if (!t || typeof t !== 'object') fail('Invalid transaction.');","category":"blocker","line_end":39,"severity":"low","line_start":39},{"id":"blocker:assets/engine.js:40:system-reconnaissance","file":"assets/engine.js","pattern":"System reconnaissance","snippet":"const id = text(t.id);","category":"blocker","line_end":40,"severity":"low","line_start":40},{"id":"blocker:assets/engine.js:41:system-reconnaissance","file":"assets/engine.js","pattern":"System reconnaissance","snippet":"if (!id || ids.has(id)) fail('Transaction IDs must be present and unique.');","category":"blocker","line_end":41,"severity":"low","line_start":41},{"id":"blocker:assets/engine.js:47:system-reconnaissance","file":"assets/engine.js","pattern":"System reconnaissance","snippet":"if (!kinds.includes(t.kind)) fail('Invalid transaction kind.');","category":"blocker","line_end":47,"severity":"low","line_start":47},{"id":"blocker:assets/engine.js:52:system-reconnaissance","file":"assets/engine.js","pattern":"System reconnaissance","snippet":"if (!['salary','support','other'].includes(incomeType) || !['loan','reimbursement','other'].includes","category":"blocker","line_end":52,"severity":"low","line_start":52},{"id":"blocker:assets/engine.js:53:system-reconnaissance","file":"assets/engine.js","pattern":"System reconnaissance","snippet":"if (!groups.includes(group)) fail('Invalid expense group.');","category":"blocker","line_end":53,"severity":"low","line_start":53},{"id":"blocker:assets/engine.js:64:system-reconnaissance","file":"assets/engine.js","pattern":"System reconnaissance","snippet":"if (!purchase || purchase.kind !== 'expense' || used.has(purchase.id) || !Array.isArray(r.refundIds)","category":"blocker","line_end":64,"severity":"low","line_start":64},{"id":"blocker:assets/engine.js:67:system-reconnaissance","file":"assets/engine.js","pattern":"System reconnaissance","snippet":"for (const id of r.refundIds) {","category":"blocker","line_end":67,"severity":"low","line_start":67},{"id":"blocker:assets/engine.js:69:system-reconnaissance","file":"assets/engine.js","pattern":"System reconnaissance","snippet":"if (!refund || refund.kind !== 'refund' || used.has(id)) fail('Invalid or repeated refund record.');","category":"blocker","line_end":69,"severity":"low","line_start":69},{"id":"blocker:assets/engine.js:78:system-reconnaissance","file":"assets/engine.js","pattern":"System reconnaissance","snippet":"if (!out || out.kind !== 'transfer_out' || !incoming || incoming.kind !== 'transfer_in' || amount <=","category":"blocker","line_end":78,"severity":"low","line_start":78},{"id":"blocker:assets/engine.js:89:system-reconnaissance","file":"assets/engine.js","pattern":"System reconnaissance","snippet":"if (!['day','week','month','year'].includes(grain)) fail('Invalid period scale.');","category":"blocker","line_end":89,"severity":"low","line_start":89},{"id":"blocker:assets/engine.js:110:system-reconnaissance","file":"assets/engine.js","pattern":"System reconnaissance","snippet":"for (const s of data.settlements) for (const id of [s.outId, s.inId]) settled.set(id,(settled.get(id","category":"blocker","line_end":110,"severity":"low","line_start":110},{"id":"blocker:assets/report.css:1:system-reconnaissance","file":"assets/report.css","pattern":"System reconnaissance","snippet":":root{color-scheme:dark;font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,\"Segoe ","category":"blocker","line_end":1,"severity":"low","line_start":1},{"id":"blocker:assets/report.css:11:system-reconnaissance","file":"assets/report.css","pattern":"System reconnaissance","snippet":".chart-tooltip{position:fixed;z-index:1000;display:grid;gap:5px;max-width:min(280px,calc(100vw - 16p","category":"blocker","line_end":11,"severity":"low","line_start":11},{"id":"blocker:assets/report.css:12:system-reconnaissance","file":"assets/report.css","pattern":"System reconnaissance","snippet":".bar-chart{height:330px;padding:0}.y-axis{padding-top:32px;padding-bottom:36px}.bar-scroll{flex:1;mi","category":"blocker","line_end":12,"severity":"low","line_start":12},{"id":"blocker:assets/report.css:16:system-reconnaissance","file":"assets/report.css","pattern":"System reconnaissance","snippet":".transaction-explorer{display:flex;flex-direction:column;gap:14px;min-width:0}.detail-toolbar{displa","category":"blocker","line_end":16,"severity":"low","line_start":16},{"id":"blocker:assets/report.css:25:system-reconnaissance","file":"assets/report.css","pattern":"System reconnaissance","snippet":".timeline-card{border:0;background:color-mix(in srgb,var(--bg),var(--text) 2%);box-shadow:none}.time","category":"blocker","line_end":25,"severity":"low","line_start":25},{"id":"blocker:assets/report.css:29:system-reconnaissance","file":"assets/report.css","pattern":"System reconnaissance","snippet":".period-picker{position:relative;min-width:0}.date-trigger{display:flex;align-items:center;gap:9px;b","category":"blocker","line_end":29,"severity":"low","line_start":29},{"id":"env_access:references/workflow.md:38:configuration-library","file":"references/workflow.md","pattern":"Configuration library","snippet":"5. If the user cannot identify an item, explicitly chooses to leave it pending, or asks to see a pro","category":"env_access","line_end":38,"severity":"low","line_start":38},{"id":"network:scripts/check_update.py:6:python-http-libraries","file":"scripts/check_update.py","pattern":"Python HTTP libraries","snippet":"import urllib.request","category":"network","line_end":6,"severity":"low","line_start":6},{"id":"network:scripts/check_update.py:22:python-http-libraries","file":"scripts/check_update.py","pattern":"Python HTTP libraries","snippet":"request = urllib.request.Request(URL, headers={\"Accept\":\"application/vnd.github+json\", \"User-Agent\":","category":"network","line_end":22,"severity":"low","line_start":22},{"id":"network:scripts/check_update.py:24:python-http-libraries","file":"scripts/check_update.py","pattern":"Python HTTP libraries","snippet":"with urllib.request.urlopen(request, timeout=10) as response:","category":"network","line_end":24,"severity":"low","line_start":24},{"id":"network:scripts/check_update.py:11:hardcoded-url","file":"scripts/check_update.py","pattern":"Hardcoded URL","snippet":"URL = f\"https://api.github.com/repos/{REPOSITORY}/releases/latest\"","category":"network","line_end":11,"severity":"low","line_start":11},{"id":"network:scripts/check_update.py:29:hardcoded-url","file":"scripts/check_update.py","pattern":"Hardcoded URL","snippet":"\"release\":f\"https://github.com/{REPOSITORY}/releases/tag/{tag}\"}))","category":"network","line_end":29,"severity":"low","line_start":29},{"id":"blocker:scripts/demo_data.py:40:system-reconnaissance","file":"scripts/demo_data.py","pattern":"System reconnaissance","snippet":"paid = add(\"2024-03-09\", \"Confirmed demo partial repayment\", 50, \"transfer_in\", \"other\", \"Loan repay","category":"blocker","line_end":40,"severity":"low","line_start":40},{"id":"network:scripts/report.py:97:hardcoded-url","file":"scripts/report.py","pattern":"Hardcoded URL","snippet":"return server, f\"http://127.0.0.1:{server.server_port}/{token}\"","category":"network","line_end":97,"severity":"low","line_start":97},{"id":"network:scripts/report.py:82:hardcoded-ip-address","file":"scripts/report.py","pattern":"Hardcoded IP address","snippet":"host = f\"127.0.0.1:{self.server.server_port}\"","category":"network","line_end":82,"severity":"medium","line_start":82},{"id":"network:scripts/report.py:96:hardcoded-ip-address","file":"scripts/report.py","pattern":"Hardcoded IP address","snippet":"server = ThreadingHTTPServer((\"127.0.0.1\", 0), Handler)","category":"network","line_end":96,"severity":"medium","line_start":96},{"id":"network:scripts/report.py:97:hardcoded-ip-address","file":"scripts/report.py","pattern":"Hardcoded IP address","snippet":"return server, f\"http://127.0.0.1:{server.server_port}/{token}\"","category":"network","line_end":97,"severity":"medium","line_start":97},{"id":"filesystem:scripts/report.py:69:python-file-write-append","file":"scripts/report.py","pattern":"Python file write/append","snippet":"with os.fdopen(fd, \"wb\") as output:","category":"filesystem","line_end":69,"severity":"medium","line_start":69},{"id":"obfuscation:scripts/report.py:42:unicode-escape-sequence","file":"scripts/report.py","pattern":"Unicode escape sequence","snippet":"payload = json.dumps(data, ensure_ascii=True, allow_nan=False).replace(\"<\", \"\\\\u003c\").replace(\">\", ","category":"obfuscation","line_end":42,"severity":"low","line_start":42},{"id":"external_commands:SKILL.md:12:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"Prepare a reconciled dataset and show it in a local report. Use the user's language for the response","category":"external_commands","line_end":12,"severity":"medium","line_start":12},{"id":"external_commands:SKILL.md:16:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"Render the bundled `assets/report.html`, `report.css`, `app.js` and `engine.js` through `scripts/rep","category":"external_commands","line_end":16,"severity":"medium","line_start":16},{"id":"external_commands:SKILL.md:25:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- Never automatically check for updates while processing financial records. `scripts/check_update.py","category":"external_commands","line_end":25,"severity":"medium","line_start":25},{"id":"external_commands:SKILL.md:29:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"For long merchant names, optionally provide `merchantDisplayNames` as described in the schema: short","category":"external_commands","line_end":29,"severity":"medium","line_start":29},{"id":"external_commands:SKILL.md:47:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"Requires Python 3.10+ and a modern browser. Resolve `<skill-dir>` to this installed skill directory;","category":"external_commands","line_end":47,"severity":"medium","line_start":47},{"id":"external_commands:SKILL.md:49:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```bash","category":"external_commands","line_end":52,"severity":"medium","line_start":49},{"id":"external_commands:SKILL.md:52:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":54,"severity":"medium","line_start":52},{"id":"external_commands:SKILL.md:54:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"Existing files can be read directly within the user's authorization. For newly normalized data, pref","category":"external_commands","line_end":54,"severity":"medium","line_start":54},{"id":"external_commands:SKILL.md:56:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"The default command prints a tokenized `http://127.0.0.1:...` URL and keeps the report in memory whi","category":"external_commands","line_end":60,"severity":"medium","line_start":56},{"id":"external_commands:SKILL.md:60:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```bash","category":"external_commands","line_end":62,"severity":"medium","line_start":60},{"id":"network:SKILL.md:24:python-http-libraries","file":"SKILL.md","pattern":"Python HTTP libraries","snippet":"- The renderer has no telemetry, remote assets or external requests. The agent host may retain promp","category":"network","line_end":24,"severity":"low","line_start":24},{"id":"network:SKILL.md:7:hardcoded-url","file":"SKILL.md","pattern":"Hardcoded URL","snippet":"repository: \"https://github.com/JasonHMY/personal-finance-visualizer\"","category":"network","line_end":7,"severity":"low","line_start":7},{"id":"network:SKILL.md:56:hardcoded-url","file":"SKILL.md","pattern":"Hardcoded URL","snippet":"The default command prints a tokenized `http://127.0.0.1:...` URL and keeps the report in memory whi","category":"network","line_end":56,"severity":"low","line_start":56},{"id":"network:SKILL.md:56:hardcoded-ip-address","file":"SKILL.md","pattern":"Hardcoded IP address","snippet":"The default command prints a tokenized `http://127.0.0.1:...` URL and keeps the report in memory whi","category":"network","line_end":56,"severity":"medium","line_start":56},{"id":"blocker:SKILL.md:16:system-reconnaissance","file":"SKILL.md","pattern":"System reconnaissance","snippet":"Render the bundled `assets/report.html`, `report.css`, `app.js` and `engine.js` through `scripts/rep","category":"blocker","line_end":16,"severity":"low","line_start":16},{"id":"obfuscation:assets/app.js:7:heuristic-very-high-entropy-string-5-60-bits-lik","file":"assets/app.js","pattern":"[HEURISTIC] Very high entropy string (5.60 bits) - likely encoded/encrypted payload","snippet":"消费按已确认的个人比例计算，排除已匹配全额退款的购买。资金总览按银行入账日统计，排除内部调拨，并应用已确认的转账抵消。未知转入不计收入。金额均使用报表币种。","category":"obfuscation","line_end":7,"severity":"high","line_start":7},{"id":"obfuscation:assets/app.js:6:heuristic-extremely-long-line-3340-chars-likely-","file":"assets/app.js","pattern":"[HEURISTIC] Extremely long line (3340 chars) - likely obfuscated","snippet":"    en: {title:'Personal finance',subtitle:'A closer look at your money',demo:'Fictional demo data',","category":"obfuscation","line_end":6,"severity":"high","line_start":6},{"id":"obfuscation:assets/app.js:1:heuristic-multiple-bracket-chains-33-jsfuck-obfu","file":"assets/app.js","pattern":"[HEURISTIC] Multiple bracket chains (33) - JSFuck/obfuscation pattern","snippet":"{}){, ]})), ]))]","category":"obfuscation","line_end":1,"severity":"high","line_start":1},{"id":"obfuscation:assets/report.css:1:heuristic-extremely-long-line-9757-chars-likely-","file":"assets/report.css","pattern":"[HEURISTIC] Extremely long line (9757 chars) - likely obfuscated","snippet":":root{color-scheme:dark;font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,\"Segoe ","category":"obfuscation","line_end":1,"severity":"high","line_start":1},{"id":"obfuscation:assets/report.css:25:heuristic-extremely-long-line-2080-chars-likely-","file":"assets/report.css","pattern":"[HEURISTIC] Extremely long line (2080 chars) - likely obfuscated","snippet":".timeline-card{border:0;background:color-mix(in srgb,var(--bg),var(--text) 2%);box-shadow:none}.time","category":"obfuscation","line_end":25,"severity":"high","line_start":25},{"id":"obfuscation:assets/report.css:29:heuristic-extremely-long-line-2909-chars-likely-","file":"assets/report.css","pattern":"[HEURISTIC] Extremely long line (2909 chars) - likely obfuscated","snippet":".period-picker{position:relative;min-width:0}.date-trigger{display:flex;align-items:center;gap:9px;b","category":"obfuscation","line_end":29,"severity":"high","line_start":29},{"id":"obfuscation:SKILL.md:16:heuristic-extremely-long-line-2974-chars-likely-","file":"SKILL.md","pattern":"[HEURISTIC] Extremely long line (2974 chars) - likely obfuscated","snippet":"Render the bundled `assets/report.html`, `report.css`, `app.js` and `engine.js` through `scripts/rep","category":"obfuscation","line_end":16,"severity":"high","line_start":16}],"finding_verdicts":[{"id":"external_commands:assets/app.js:132:ruby-shell-backtick-execution","reason":"Backticks create a JavaScript template literal for SVG stroke lengths during ring animation. No shell or Ruby execution occurs.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:assets/app.js:361:ruby-shell-backtick-execution","reason":"The template literal formats a CSS translate transform from card coordinates. It does not invoke an operating-system command.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:assets/app.js:369:ruby-shell-backtick-execution","reason":"The backticks format a translate3d CSS value for pointer-driven card movement. This is browser animation, not shell execution.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:assets/app.js:392:ruby-shell-backtick-execution","reason":"Template literals position the segmented-control indicator using translateX. No command interpreter is called.","verdict":"false_positive","confidence":0.99},{"id":"network:assets/app.js:432:hardcoded-url","reason":"The GitHub URL appears inside a license attribution comment. It is not requested by the application.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:assets/app.js:218:node-js-fs-operations","reason":"The code creates SVG gradients and appends nodes to the defs element. It contains no Node.js filesystem operation.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:assets/app.js:345:hidden-file-access","reason":"The code reads a browser localStorage layout key and validates opaque card identifiers. It does not access hidden files or load financial records.","verdict":"false_positive","confidence":0.99},{"id":"obfuscation:assets/app.js:233:array-join-empty-string","reason":"Array join assembles numeric SVG path commands for the area chart. No encoded payload is decoded or executed.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:27:certificate-key-files","reason":"p.key is a calendar-period identifier used to format dates. No certificate or private-key file is referenced.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:29:certificate-key-files","reason":"p.key supplies an ISO date to the Date constructor. This property is unrelated to cryptographic key material.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:30:certificate-key-files","reason":"The code computes a natural period end from a date key. It does not read credentials or certificate files.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:64:certificate-key-files","reason":"event.key checks whether Escape was pressed to hide a chart tooltip. It is a keyboard event property, not a secret.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:87:certificate-key-files","reason":"event.key handles Escape in the transaction search field. No certificate or credential access occurs.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:132:certificate-key-files","reason":"item.key identifies a ring segment in an animation frame. It contains chart identity, not cryptographic material.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:134:certificate-key-files","reason":"Legend item keys preserve chart identities while amounts animate. There is no certificate-file access.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:150:certificate-key-files","reason":"A Map indexes prior ring entries by their chart key. These are application identifiers, not private keys.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:152:certificate-key-files","reason":"newByKey maps positive chart entries to SVG ring segments. No sensitive file or certificate operation exists here.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:153:certificate-key-files","reason":"The keys array combines old and new ring-entry identifiers for animation. It does not contain cryptographic keys.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:156:certificate-key-files","reason":"event.key enables Enter and Space activation of an accessible chart segment. No secret or certificate is accessed.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:162:certificate-key-files","reason":"Map keys associate legend entries with their previous displayed values. This is chart state management, not credential handling.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:199:certificate-key-files","reason":"s.key selects a numeric chart series from each observation. No key files or secrets are involved.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:201:certificate-key-files","reason":"Series keys select values when calculating chart-axis bounds. The code performs numeric rendering calculations only.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:206:certificate-key-files","reason":"currentPeriod.key forms an animation-state signature with view and period scale. It is a date identifier, not sensitive key material.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:211:certificate-key-files","reason":"s.key distinguishes outgoing and incoming transfer chart series. It does not identify any certificate or key file.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:212:certificate-key-files","reason":"A selected observation key labels the transaction drill-down dialog. No credential read occurs.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:213:certificate-key-files","reason":"Observation and series keys select displayed dates and numeric amounts. They are ordinary chart properties.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:232:certificate-key-files","reason":"A point key is sliced to produce a date-axis label. This is not certificate handling.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:234:certificate-key-files","reason":"Series keys select values for chart crosshair dots. No sensitive file operation appears.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:242:certificate-key-files","reason":"The tooltip combines an observation date key with formatted series amounts. No secret or private-key file is referenced.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:249:certificate-key-files","reason":"event.key handles keyboard chart navigation and drill-down. It is unrelated to cryptographic keys.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:280:certificate-key-files","reason":"point.key labels a bar-chart transaction dialog, and point[key] supplies its numeric height. No certificate file is accessed.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:281:certificate-key-files","reason":"Point and series keys populate an accessible bar label and disabled state. They are chart-data selectors.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:282:certificate-key-files","reason":"point[key] is a numeric bar value used in a label and tooltip. There is no cryptographic operation.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:285:certificate-key-files","reason":"The point date key is shortened for a bar-axis label. No secret-file access occurs.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:290:certificate-key-files","reason":"Monthly period keys label buttons and select a report month. They are calendar identifiers, not certificate keys.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:310:certificate-key-files","reason":"Selecting a calendar period assigns selected.key to report state. This identifier contains a date, not credentials.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:324:certificate-key-files","reason":"event.key implements calendar navigation through arrow, Home, End, and page keys. It does not access key files.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:333:certificate-key-files","reason":"event.key checks Escape to close the calendar popover. This is a standard keyboard interaction.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:376:certificate-key-files","reason":"event.key checks Escape to cancel card dragging. No credential or certificate access occurs.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:378:certificate-key-files","reason":"Keyboard event keys reorder report cards within the browser. They do not refer to stored cryptographic keys.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:406:certificate-key-files","reason":"Period keys select the current report interval before computing its summary. They are ordinary calendar data.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:410:certificate-key-files","reason":"The key variable iterates the four report view names to create buttons. No secret material is used.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:412:certificate-key-files","reason":"Period keys implement previous, next, and scale-selection controls. The code only changes the displayed interval.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:414:certificate-key-files","reason":"Category keys select labels for spending rings. There is no certificate or private-key file handling.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/app.js:357:system-reconnaissance","reason":"The current function enumerates browser card elements to record their layout order. It does not inspect the operating system.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/app.js:392:system-reconnaissance","reason":"Element offset measurements position an animated control indicator. These DOM geometry reads are not system reconnaissance.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/app.js:412:system-reconnaissance","reason":"The line builds calendar-period controls and updates report state. No system command or host enumeration appears.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/app.js:413:system-reconnaissance","reason":"This conditional selects the spending report branch. It is ordinary interface logic, not host discovery.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/app.js:414:system-reconnaissance","reason":"The code groups spending rows into chart rings. It does not collect machine or operating-system information.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/app.js:419:system-reconnaissance","reason":"The code builds inflow, outflow, and net-change metrics from financial summaries. No reconnaissance operation occurs.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/app.js:421:system-reconnaissance","reason":"Income rows are grouped into rings and transaction tables. The line does not probe the host system.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:assets/engine.js:9:hidden-file-access","reason":"The regular expression validates signed decimal amount strings. It does not reference hidden files.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:assets/engine.js:16:hidden-file-access","reason":"The regular expression checks ISO calendar-date syntax. No filesystem operation occurs.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:assets/engine.js:29:hidden-file-access","reason":"The regular expression validates a three-letter currency code. This is input validation, not hidden-file access.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:31:system-reconnaissance","reason":"Intl.NumberFormat checks currency formatting support and decimal precision. It does not enumerate host information.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:39:system-reconnaissance","reason":"The conditional validates that a transaction is an object. No system discovery behavior exists.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:40:system-reconnaissance","reason":"The code validates transaction identifier text. It does not obtain a machine or user identifier.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:41:system-reconnaissance","reason":"The condition rejects missing or duplicate transaction IDs. This protects dataset integrity rather than probing the host.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:47:system-reconnaissance","reason":"The code checks transaction kinds against an allowed list. It performs no host inspection.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:52:system-reconnaissance","reason":"Allowed income and transfer classifications are validated here. No reconnaissance API is used.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:53:system-reconnaissance","reason":"The line rejects unknown expense groups. This is a financial-data validation rule, not system discovery.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:64:system-reconnaissance","reason":"Refund mappings are checked for valid purchases, duplicate use, and refund arrays. The operation only inspects supplied report data.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:67:system-reconnaissance","reason":"The loop iterates transaction IDs in a supplied refund mapping. No host information is collected.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:69:system-reconnaissance","reason":"The condition validates refund transaction identity and prevents repeated allocation. It is not a system probe.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:78:system-reconnaissance","reason":"Transfer settlement endpoints and positive allocation amounts are validated. The code examines only supplied financial records.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:89:system-reconnaissance","reason":"The condition restricts the report period scale to day, week, month, or year. No reconnaissance occurs.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:110:system-reconnaissance","reason":"The loop totals confirmed settlement allocations by transaction ID. It does not enumerate system resources.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/report.css:1:system-reconnaissance","reason":"The line defines browser styles, including system font fallbacks. It contains no executable host-discovery command.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/report.css:11:system-reconnaissance","reason":"These CSS declarations control tooltip position, typography, and visibility. They do not inspect the operating system.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/report.css:12:system-reconnaissance","reason":"The stylesheet defines bar-chart sizing and scrolling behavior. No reconnaissance capability is present.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/report.css:16:system-reconnaissance","reason":"The declarations style transaction filters, search controls, and sorting headers. They do not execute system commands.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/report.css:25:system-reconnaissance","reason":"The CSS defines local area-chart presentation and accessibility styles. No host discovery or data collection occurs.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/report.css:29:system-reconnaissance","reason":"The line styles a calendar popover and its date controls. It does not inspect host resources.","verdict":"false_positive","confidence":0.99},{"id":"env_access:references/workflow.md:38:configuration-library","reason":"This prose explains how to disclose unresolved transaction classifications. It neither imports a configuration library nor reads environment variables.","verdict":"false_positive","confidence":0.99},{"id":"network:scripts/check_update.py:6:python-http-libraries","reason":"urllib.request supports a separate, explicitly requested public-release lookup. The script does not import financial inputs or execute downloaded code.","verdict":"false_positive","confidence":0.98},{"id":"network:scripts/check_update.py:22:python-http-libraries","reason":"The request targets GitHub release metadata with fixed Accept and User-Agent headers. No financial data or credentials are attached.","verdict":"false_positive","confidence":0.98},{"id":"network:scripts/check_update.py:24:python-http-libraries","reason":"The explicit update checker fetches public release metadata with a timeout and bounded response read. It does not transmit report contents.","verdict":"false_positive","confidence":0.98},{"id":"network:scripts/check_update.py:11:hardcoded-url","reason":"The HTTPS URL identifies the author's public GitHub release endpoint. It serves the documented, separate version check rather than data exfiltration.","verdict":"false_positive","confidence":0.98},{"id":"network:scripts/check_update.py:29:hardcoded-url","reason":"The code prints a GitHub release link after validating the release tag. It does not visit that link or install anything.","verdict":"false_positive","confidence":0.99},{"id":"blocker:scripts/demo_data.py:40:system-reconnaissance","reason":"The line creates an invented partial-repayment transaction in memory. It does not collect host information.","verdict":"false_positive","confidence":0.99},{"id":"network:scripts/report.py:97:hardcoded-url","reason":"The returned URL serves an in-memory report on loopback with an unpredictable path token. It does not target an external recipient.","verdict":"false_positive","confidence":0.99},{"id":"network:scripts/report.py:82:hardcoded-ip-address","reason":"127.0.0.1 defines the expected Host header for a local report request. The following check rejects other hosts and incorrect tokens.","verdict":"false_positive","confidence":0.99},{"id":"network:scripts/report.py:96:hardcoded-ip-address","reason":"The server binds only to IPv4 loopback on an automatically assigned port. It does not expose the report on external interfaces.","verdict":"false_positive","confidence":0.99},{"id":"network:scripts/report.py:97:hardcoded-ip-address","reason":"The address is loopback, used to deliver the authorized report locally. The URL includes a random access token.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:scripts/report.py:69:python-file-write-append","reason":"Writing occurs only for an explicit export, using exclusive creation and owner-only permissions. Paths inside the skill or Git repositories are rejected.","verdict":"false_positive","confidence":0.98},{"id":"obfuscation:scripts/report.py:42:unicode-escape-sequence","reason":"Unicode escapes protect the embedded JSON from HTML script-boundary injection. They are a security control, not concealed executable content.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:12:ruby-shell-backtick-execution","reason":"Backticks mark supported language identifiers in Markdown. The sentence describes report preparation without invoking a shell.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:16:ruby-shell-backtick-execution","reason":"Inline backticks identify bundled report assets and the renderer. The surrounding prose specifies interface behavior, not hidden command execution.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:25:ruby-shell-backtick-execution","reason":"Backticks identify the optional update script in documentation. The instruction explicitly prohibits automatic checks during financial processing.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:29:ruby-shell-backtick-execution","reason":"The backticked merchantDisplayNames identifier describes optional display metadata. No command is executed.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:47:ruby-shell-backtick-execution","reason":"Backticks mark an installation-directory placeholder. This is runtime setup guidance, not Ruby or shell substitution.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:49:ruby-shell-backtick-execution","reason":"The Markdown code fence introduces Python report commands with quoted script paths. They perform the documented local rendering task.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:52:ruby-shell-backtick-execution","reason":"The backticks close a Markdown example block. They are not executable shell substitutions.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:54:ruby-shell-backtick-execution","reason":"Backticks identify input and demo options in prose. The text recommends stdin instead of exposing financial records in arguments or temporary files.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:56:ruby-shell-backtick-execution","reason":"Backticks format the loopback report URL, followed by a Markdown export example. No undocumented shell command is present.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:60:ruby-shell-backtick-execution","reason":"The code fence documents the renderer's explicit local-export command. It is authorized functionality, not shell backtick execution.","verdict":"false_positive","confidence":0.99},{"id":"network:SKILL.md:24:python-http-libraries","reason":"The prose describes renderer network restrictions and host retention limitations. It contains no Python HTTP-library invocation.","verdict":"false_positive","confidence":0.99},{"id":"network:SKILL.md:7:hardcoded-url","reason":"The URL is author-declared repository metadata. It does not initiate a network request.","verdict":"false_positive","confidence":0.99},{"id":"network:SKILL.md:56:hardcoded-url","reason":"The documented URL is a tokenized loopback report address. The same instruction prohibits external exposure or publication.","verdict":"false_positive","confidence":0.99},{"id":"network:SKILL.md:56:hardcoded-ip-address","reason":"127.0.0.1 documents local delivery, matching the renderer's loopback-only bind. It is not an external collection endpoint.","verdict":"false_positive","confidence":0.99},{"id":"blocker:SKILL.md:16:system-reconnaissance","reason":"The paragraph specifies chart layout, responsiveness, and keyboard interactions. It contains no host reconnaissance instruction.","verdict":"false_positive","confidence":0.99},{"id":"obfuscation:assets/app.js:7:heuristic-very-high-entropy-string-5-60-bits-lik","reason":"The string contains readable Simplified Chinese interface translations and accounting explanations. It is not an encrypted or encoded payload.","verdict":"false_positive","confidence":0.99},{"id":"obfuscation:assets/app.js:6:heuristic-extremely-long-line-3340-chars-likely-","reason":"The long line is a readable English localization dictionary. It contains interface labels, not obfuscated executable logic.","verdict":"false_positive","confidence":0.99},{"id":"obfuscation:assets/app.js:1:heuristic-multiple-bracket-chains-33-jsfuck-obfu","reason":"The file uses readable named functions, ordinary arrays, and callback expressions. Its bracket sequences do not construct or execute a JSFuck payload.","verdict":"false_positive","confidence":0.98},{"id":"obfuscation:assets/report.css:1:heuristic-extremely-long-line-9757-chars-likely-","reason":"The long line contains compact, readable CSS rules for the report interface. No encoded payload or executable script is present.","verdict":"false_positive","confidence":0.99},{"id":"obfuscation:assets/report.css:25:heuristic-extremely-long-line-2080-chars-likely-","reason":"The line contains area-chart styles, color variables, and accessibility rules. Its length reflects compact CSS formatting, not malicious concealment.","verdict":"false_positive","confidence":0.99},{"id":"obfuscation:assets/report.css:29:heuristic-extremely-long-line-2909-chars-likely-","reason":"The long line defines readable calendar-popover CSS selectors and declarations. It does not contain an encoded or executable payload.","verdict":"false_positive","confidence":0.99},{"id":"obfuscation:SKILL.md:16:heuristic-extremely-long-line-2974-chars-likely-","reason":"This is a long, readable paragraph of report-interface requirements. It neither conceals code nor attempts to override the security audit.","verdict":"false_positive","confidence":0.99}],"semantic_findings":[],"subject_marketplace_commit_sha":"91eb8ebfd4726a061a961f2f0be972321cbd06b1","subject_content_hash":"fcc820170d7b71482c0e989565390410d4a72c64aada81b7793adcb8986ce0bc","subject_tree_hash":"15ceb8e2e4f506f426179bd6ef8862e9ef1fa1dd5c1fab0be1c33f6e7998cc66","subject_plugin_path":"skills/jasonhmy/personal-finance-visualizer","audit_payload_hash":"2d1641f3a8261b92148c3c9029ab3529","confirmed_risk_level":"safe","scanner_version":"3.0.0","policy_version":"skillstore-security-audit-policy-v1","subject":{"marketplaceCommitSha":"91eb8ebfd4726a061a961f2f0be972321cbd06b1","contentHash":"fcc820170d7b71482c0e989565390410d4a72c64aada81b7793adcb8986ce0bc","treeHash":"15ceb8e2e4f506f426179bd6ef8862e9ef1fa1dd5c1fab0be1c33f6e7998cc66","pluginPath":"skills/jasonhmy/personal-finance-visualizer","auditPayloadHash":"2d1641f3a8261b92148c3c9029ab3529"},"scannerVersion":"3.0.0","policyVersion":"skillstore-security-audit-policy-v1"},"auditTranslation":null,"localization":{"requestedLocale":"en","contentLocale":"en","availableLocales":["en"],"fallbackToEnglish":false},"attestation":{"availability":"issued","url":"/api/skills/jasonhmy-personal-finance-visualizer/audits/6/attestation","status":"superseded"},"trust":{"publicState":"public","auditState":"complete","auditCurrentness":null,"confirmedRiskLevel":"safe","confirmedFindingCount":0,"capabilityReviewCount":0,"needsReviewCount":0,"falsePositiveCount":0,"agentAutoInstallPolicy":"allowed","manualInstallPolicy":"allowed","artifactSignatureState":"available","attestationState":"superseded","verificationState":"not_verified"},"isLatest":false}}