{"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.0","skillstoreRevision":5},"audit":{"id":"3b03fcbb-fd3d-487b-8cd9-a54f79ed3230","skill_id":"5b81ea1f-b005-4d91-ab7a-8f1aded25e63","version":5,"content_hash":"v3:dc0c62c16c34eb69a8f792103d7db6062c843aea:37aded03c8d930b718701d22a5eae4d35f1357079adf83e384f0b48eddf6e676:d35ef6bba448e39b7fe2dee8dca8a4626da9b0d9da102d53e039568d3185ac41:736b696c6c732f6a61736f6e686d792f706572736f6e616c2d66696e616e63652d76697375616c697a6572:2ea7347b5819be885f1cdd3d76848f80","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 alerts resolve to documented local reporting, explicit metadata lookup, or lexical false positives. Reviewed code uses escaped embedded data, text-node rendering, a tokenized loopback server, and restricted, user-requested exports. No evidence found of credential collection, unauthorized disclosure, prompt injection, or malicious obfuscation.","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":1224,"audit_model":"codex","audited_at":"2026-10-23T12:03:28.952+00:00","created_at":"2026-10-23T13:06:29.730784+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-d","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-2766-chars-likely-","file":"SKILL.md","pattern":"[HEURISTIC] Extremely long line (2766 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 form a JavaScript template literal for an SVG stroke attribute. No shell or Ruby interpreter is invoked.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:assets/app.js:361:ruby-shell-backtick-execution","reason":"The template literal constructs a CSS translate value for card animation. It does not execute an external command.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:assets/app.js:369:ruby-shell-backtick-execution","reason":"Backticks interpolate pointer coordinates into a CSS translate3d value during dragging. This is browser styling, not shell execution.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:assets/app.js:392:ruby-shell-backtick-execution","reason":"JavaScript template literals position the segmented control indicator with translateX. No command execution API is involved.","verdict":"false_positive","confidence":0.99},{"id":"network:assets/app.js:432:hardcoded-url","reason":"The GitHub URL appears only in a license attribution comment. It does not initiate a request or transmit report data.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:assets/app.js:218:node-js-fs-operations","reason":"The line creates SVG gradients, paths, and circles using DOM helpers. The variable defs is not Node.js fs or a filesystem operation.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:assets/app.js:345:hidden-file-access","reason":"The code reads a named browser localStorage entry containing validated layout identifiers. The regular expression validates identifiers, not hidden filesystem paths.","verdict":"false_positive","confidence":0.99},{"id":"obfuscation:assets/app.js:233:array-join-empty-string","reason":"The join assembles numeric SVG path coordinates for chart rendering. It neither decodes a payload nor dynamically executes code.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:27:certificate-key-files","reason":"The property p.key contains a calendar period identifier used for labels. It is not a certificate filename or cryptographic key.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:29:certificate-key-files","reason":"The code converts the period identifier p.key into a UTC date. No key file is read.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:30:certificate-key-files","reason":"The expression uses p.key to calculate a natural calendar period end. This is date arithmetic, not credential access.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:64:certificate-key-files","reason":"The event.key property checks the Escape keyboard key to hide a tooltip. No certificate or private key is accessed.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:87:certificate-key-files","reason":"The Escape keyboard handler closes transaction search. The property event.key is unrelated to certificate files.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:132:certificate-key-files","reason":"The item.key property identifies a chart segment in an animation frame. The line only updates SVG attributes and in-memory chart state.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:134:certificate-key-files","reason":"Legend entries retain their ordinary item.key identifiers while interpolating displayed amounts. No cryptographic material or file is involved.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:150:certificate-key-files","reason":"The map indexes previous chart entries by x.key for animation continuity. These are data identifiers, not sensitive key files.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:152:certificate-key-files","reason":"The code creates an SVG and maps chart categories by x.key. No filesystem or credential operation occurs.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:153:certificate-key-files","reason":"The keys collection merges old and current chart category identifiers. It does not contain cryptographic keys or reference key files.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:156:certificate-key-files","reason":"The line configures segment tooltips, accessibility labels, and Enter or Space keyboard activation. Its key references concern keyboard input only.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:162:certificate-key-files","reason":"Legend values are indexed by chart category keys and rendered as text. No certificate or secret material is read.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:199:certificate-key-files","reason":"The expression p[s.key] selects a numeric chart series value. The property is not a file path or cryptographic key.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:201:certificate-key-files","reason":"Series keys select numeric observations when calculating the chart scale. No credential access occurs.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:206:certificate-key-files","reason":"The currentPeriod.key property forms an in-memory animation signature. It is a calendar identifier, not a certificate key.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:211:certificate-key-files","reason":"The series key selects incoming or outgoing transfer rows for chart details. No sensitive file is accessed.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:212:certificate-key-files","reason":"The selected.key property supplies a date label for transaction details. The operation only creates browser controls and displays existing report data.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:213:certificate-key-files","reason":"Period and series keys select chart labels and amounts for text-node rendering. They are not cryptographic keys.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:232:certificate-key-files","reason":"The key property contains a date string used as an SVG axis label. No certificate file operation is present.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:234:certificate-key-files","reason":"Series keys retrieve plotted observations to position crosshair dots. The line performs SVG coordinate updates, not secret access.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:242:certificate-key-files","reason":"The code builds tooltip text from a date key and numeric series values. No key file or credential is referenced.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:249:certificate-key-files","reason":"The event.key comparisons implement chart keyboard navigation and transaction activation. These are keyboard names, not certificate keys.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:280:certificate-key-files","reason":"The point.key date labels a bar, while point[key] supplies its numeric height. Both belong to chart data, not sensitive files.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:281:certificate-key-files","reason":"Date and series keys populate a bar accessibility label and disabled state. No cryptographic material is involved.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:282:certificate-key-files","reason":"The computed property point[key] provides a bar amount for its label and tooltip. No certificate or filesystem access occurs.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:285:certificate-key-files","reason":"The key property is sliced to produce a short date label below a chart bar. It is not a private key.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:290:certificate-key-files","reason":"Monthly summary buttons use p.key as their calendar period identifier. The function constructs a table without reading files.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:310:certificate-key-files","reason":"The key argument selects a covered date and updates the active period. No cryptographic key handling is present.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:324:certificate-key-files","reason":"The keyboard handler reads a date from dataset.date and uses event.key for calendar navigation. It does not access key files.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:333:certificate-key-files","reason":"The event.key check handles Escape to close the calendar popover. No certificate or credential operation occurs.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:376:certificate-key-files","reason":"The Escape keyboard key cancels an active card drag. This is user interaction handling, not private key access.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:378:certificate-key-files","reason":"Keyboard modifier and arrow-key checks implement accessible card reordering. No certificate filename or cryptographic operation appears.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:406:certificate-key-files","reason":"The code selects the active calendar period using period.key and computes its summary. These are report identifiers, not sensitive keys.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:410:certificate-key-files","reason":"Calendar keys drive period selection and previous or next navigation buttons. No credential file access occurs.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:412:certificate-key-files","reason":"The x.key property identifies an expense group for translated chart labels. It is ordinary report data, not a certificate key.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:assets/app.js:414:certificate-key-files","reason":"The category.key property groups merchant charts and produces an opaque layout identifier. No private key or certificate is accessed.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/app.js:357:system-reconnaissance","reason":"The function looks up card identifiers in a JavaScript Map and appends DOM elements. It does not inspect operating system identities.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/app.js:392:system-reconnaissance","reason":"The code measures a selected button and positions an animated indicator. It gathers layout dimensions, not system reconnaissance data.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/app.js:412:system-reconnaissance","reason":"This line groups spending records and constructs primary chart rings. No system command or host enumeration occurs.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/app.js:413:system-reconnaissance","reason":"The loop renders fixed, AI, and housing expense charts. Its group identifiers are financial categories, not operating system groups.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/app.js:414:system-reconnaissance","reason":"The line creates merchant breakdown charts from supplied transactions. It does not enumerate system users, files, or services.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/app.js:419:system-reconnaissance","reason":"The code filters income records and renders income charts and details. It contains no host reconnaissance behavior.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/app.js:421:system-reconnaissance","reason":"Transfer records supply chart rings and repayment metrics. All inspected values come from the report dataset, not the operating 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. Its escaped decimal point is not a hidden file reference.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:assets/engine.js:16:hidden-file-access","reason":"The regular expression validates ISO date strings. No filesystem API or hidden path is used.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:assets/engine.js:29:hidden-file-access","reason":"The regular expression checks a three-letter uppercase currency code. This is input validation without file access.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:31:system-reconnaissance","reason":"Intl.NumberFormat resolves currency formatting rules to validate decimal precision. It neither executes commands nor probes host identities.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:39:system-reconnaissance","reason":"The condition checks that each transaction is an object. It is report validation, not system inspection.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:40:system-reconnaissance","reason":"The id variable receives a validated transaction identifier. It does not invoke the operating system id command.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:41:system-reconnaissance","reason":"The code rejects missing or duplicate transaction identifiers using a Set. No user identity lookup occurs.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:47:system-reconnaissance","reason":"The condition validates a transaction kind against supported financial record types. It performs no host reconnaissance.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:52:system-reconnaissance","reason":"The line validates income and transfer classification values against fixed lists. It does not access system configuration or identities.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:53:system-reconnaissance","reason":"The groups array contains expense classifications such as daily and housing. Checking membership does not inspect operating system groups.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:64:system-reconnaissance","reason":"The condition validates a purchase identifier and its refund mapping. It accesses only in-memory transaction data.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:67:system-reconnaissance","reason":"The loop iterates refund transaction identifiers from supplied input. It does not enumerate host users or execute identity commands.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:69:system-reconnaissance","reason":"This check rejects invalid or reused refund transaction identifiers. Its id variable is not an operating system command.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:78:system-reconnaissance","reason":"The line validates transfer settlement endpoints and a positive allocation amount. It does not query the host.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:89:system-reconnaissance","reason":"The function checks whether a requested calendar scale is supported. This is report period validation, not reconnaissance.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/engine.js:110:system-reconnaissance","reason":"The nested loop accumulates settlement amounts by transaction identifier. It reads only supplied financial records.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/report.css:1:system-reconnaissance","reason":"The line contains CSS theme, typography, grid, and component styling. System font names and CSS identifiers do not execute reconnaissance commands.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/report.css:11:system-reconnaissance","reason":"These declarations style tooltip positioning, dimensions, and text. CSS layout keywords are not host inspection commands.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/report.css:12:system-reconnaissance","reason":"The line defines bar chart dimensions and scrolling behavior. It has no executable system reconnaissance logic.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/report.css:16:system-reconnaissance","reason":"The selectors and declarations style transaction controls and table headers. They do not query operating system users or groups.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/report.css:25:system-reconnaissance","reason":"The line styles timeline charts, axes, and accessible hidden text. No shell command or host enumeration is present.","verdict":"false_positive","confidence":0.99},{"id":"blocker:assets/report.css:29:system-reconnaissance","reason":"These CSS rules style the local calendar popover and date cells. They contain no system inspection behavior.","verdict":"false_positive","confidence":0.99},{"id":"env_access:references/workflow.md:38:configuration-library","reason":"The prose explains handling unresolved transactions and unconfirmed allocations. 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 receives no financial input and does not install downloaded code.","verdict":"false_positive","confidence":0.98},{"id":"network:scripts/check_update.py:22:python-http-libraries","reason":"The request targets the fixed GitHub release endpoint with static Accept and User-Agent headers. No report records, credentials, or private paths are attached.","verdict":"false_positive","confidence":0.98},{"id":"network:scripts/check_update.py:24:python-http-libraries","reason":"The optional HTTPS lookup reads bounded release metadata with a timeout and parses JSON without execution. It does not upload financial data.","verdict":"false_positive","confidence":0.98},{"id":"network:scripts/check_update.py:11:hardcoded-url","reason":"The fixed HTTPS URL retrieves public release metadata from GitHub only when the standalone checker is run. No sensitive query parameters are included.","verdict":"false_positive","confidence":0.98},{"id":"network:scripts/check_update.py:29:hardcoded-url","reason":"The GitHub release URL is printed as metadata after validating the version tag. This line neither opens the link nor downloads an update.","verdict":"false_positive","confidence":0.99},{"id":"blocker:scripts/demo_data.py:40:system-reconnaissance","reason":"The line appends a fictional partial repayment to demo records. It performs no system query or command execution.","verdict":"false_positive","confidence":0.99},{"id":"network:scripts/report.py:97:hardcoded-url","reason":"The returned URL serves the requested report on loopback with a random token. It is not an external transmission destination.","verdict":"false_positive","confidence":0.99},{"id":"network:scripts/report.py:82:hardcoded-ip-address","reason":"The loopback address constructs the expected Host header for request validation. Combined with the token check, it restricts report access rather than exfiltrating data.","verdict":"false_positive","confidence":0.99},{"id":"network:scripts/report.py:96:hardcoded-ip-address","reason":"The HTTP server binds only to 127.0.0.1 on an assigned port. The handler requires the exact Host and random token and sends no-store headers.","verdict":"false_positive","confidence":0.99},{"id":"network:scripts/report.py:97:hardcoded-ip-address","reason":"The address identifies the local report server and includes an unpredictable access token. No remote endpoint receives the financial data.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:scripts/report.py:69:python-file-write-append","reason":"Writing occurs only for an explicit export, with exclusive creation and mode 0600. The function rejects destinations inside Git repositories or the installed skill.","verdict":"false_positive","confidence":0.99},{"id":"obfuscation:scripts/report.py:42:unicode-escape-sequence","reason":"Unicode escapes protect the HTML script boundary when embedding JSON. This prevents markup injection rather than concealing executable payloads.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:12:ruby-shell-backtick-execution","reason":"Markdown backticks mark supported language codes in ordinary workflow instructions. They do not invoke shell or Ruby execution.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:16:ruby-shell-backtick-execution","reason":"Backticks identify bundled assets and the local renderer in interface guidance. The paragraph contains no shell interpolation or arbitrary execution request.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:25:ruby-shell-backtick-execution","reason":"Backticks format the optional checker filename. The instruction explicitly prohibits automatic update checks while processing financial records.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:29:ruby-shell-backtick-execution","reason":"Backticks mark the merchantDisplayNames input field. The paragraph describes presentation metadata, not command execution.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:47:ruby-shell-backtick-execution","reason":"Backticks format the installation directory placeholder in runtime requirements. No executable shell substitution is present.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:49:ruby-shell-backtick-execution","reason":"The backticks delimit a Markdown code fence with Python renderer examples. These task-related commands read local input without shell backtick substitution.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:52:ruby-shell-backtick-execution","reason":"The match spans a closing Markdown code fence and subsequent input guidance. It is not shell backtick execution.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:54:ruby-shell-backtick-execution","reason":"Backticks format the stdin option and demo flag. The instructions require authorized reads and supported process APIs without financial records in command arguments.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:56:ruby-shell-backtick-execution","reason":"Markdown backticks format the local URL, followed by an export code fence. The text restricts serving to loopback and warns against publishing the token.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:60:ruby-shell-backtick-execution","reason":"The fenced example invokes the bundled renderer for a user-requested local HTML export. The backticks are Markdown syntax, not executable substitution.","verdict":"false_positive","confidence":0.99},{"id":"network:SKILL.md:24:python-http-libraries","reason":"The paragraph describes renderer network restrictions and host retention boundaries. It contains no HTTP library import or request.","verdict":"false_positive","confidence":0.99},{"id":"network:SKILL.md:7:hardcoded-url","reason":"The GitHub URL is repository metadata identifying the upstream project. It is not a data upload endpoint or an execution instruction.","verdict":"false_positive","confidence":0.99},{"id":"network:SKILL.md:56:hardcoded-url","reason":"The example URL documents the tokenized local report server. The same paragraph prohibits external exposure and publication.","verdict":"false_positive","confidence":0.99},{"id":"network:SKILL.md:56:hardcoded-ip-address","reason":"127.0.0.1 is explicitly documented as the report delivery boundary. It does not identify an external recipient.","verdict":"false_positive","confidence":0.99},{"id":"blocker:SKILL.md:16:system-reconnaissance","reason":"The paragraph specifies report controls, animations, and layout behavior. Its interface terminology does not instruct host inspection or privilege discovery.","verdict":"false_positive","confidence":0.99},{"id":"obfuscation:assets/app.js:7:heuristic-very-high-entropy-string-5-60-bits-lik","reason":"The flagged string is readable Simplified Chinese explaining report calculations. It is localization text, not an encoded or encrypted 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 an English translation dictionary with descriptive property names and plain interface text. No payload decoding or dynamic execution appears.","verdict":"false_positive","confidence":0.99},{"id":"obfuscation:assets/app.js:1:heuristic-multiple-bracket-chains-33-jsfuck-obfu","reason":"The file contains ordinary nested arrays, maps, callbacks, and property indexing in readable chart code. The aggregate bracket heuristic does not establish JSFuck execution.","verdict":"false_positive","confidence":0.98},{"id":"obfuscation:assets/report.css:1:heuristic-extremely-long-line-9757-chars-likely-","reason":"The long line concatenates readable CSS declarations for the report interface. It contains no encoded payload or remote resource loading.","verdict":"false_positive","confidence":0.99},{"id":"obfuscation:assets/report.css:25:heuristic-extremely-long-line-2080-chars-likely-","reason":"This line contains compact timeline chart styles with descriptive selectors. Its length reflects formatting, not concealed executable behavior.","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 styles and states. No decoding, external imports, or hidden executable payload is present.","verdict":"false_positive","confidence":0.99},{"id":"obfuscation:SKILL.md:16:heuristic-extremely-long-line-2766-chars-likely-","reason":"The paragraph is lengthy but readable guidance for preserving the bundled report interface. No encoded instructions or audit manipulation attempt appears.","verdict":"false_positive","confidence":0.99}],"semantic_findings":[],"subject_marketplace_commit_sha":"dc0c62c16c34eb69a8f792103d7db6062c843aea","subject_content_hash":"37aded03c8d930b718701d22a5eae4d35f1357079adf83e384f0b48eddf6e676","subject_tree_hash":"d35ef6bba448e39b7fe2dee8dca8a4626da9b0d9da102d53e039568d3185ac41","subject_plugin_path":"skills/jasonhmy/personal-finance-visualizer","audit_payload_hash":"2ea7347b5819be885f1cdd3d76848f80","confirmed_risk_level":"safe","scanner_version":"3.0.0","policy_version":"skillstore-security-audit-policy-v1","subject":{"marketplaceCommitSha":"dc0c62c16c34eb69a8f792103d7db6062c843aea","contentHash":"37aded03c8d930b718701d22a5eae4d35f1357079adf83e384f0b48eddf6e676","treeHash":"d35ef6bba448e39b7fe2dee8dca8a4626da9b0d9da102d53e039568d3185ac41","pluginPath":"skills/jasonhmy/personal-finance-visualizer","auditPayloadHash":"2ea7347b5819be885f1cdd3d76848f80"},"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/5/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}}