T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/agents_canvas_snapshot.py:112
- Finding
- Sensitive session metadata is persisted in a shareable HTML artifact<![CDATA[ ## Vulnerability Details **File Location**: `scripts/agents_canvas_snapshot.py:112-155, 264-277`; generated data visible in `assets/agents_canvas.html:63-84` **Vulnerability Type**: Sensitive operational metadata exposure **Risk Level**: Medium ### Vulnerable Code ```python for session in sessions: key = html.escape(session.get("key", session.get("sessionId", "unknown"))) age_ms = session.get("ageMs") status = session.get("status") or classify_status(age_ms) status_counts.setdefault(status, 0) status_counts[status] += 1 updated_ms = session.get("updatedAt") lag_seconds = None last_update_local = "-" last_update_ago = "-" if updated_ms: lag_seconds = max(0.0, now_epoch - (updated_ms / 1000.0)) last_update_local = datetime.fromtimestamp(updated_ms / 1000.0).strftime("%Y-%m-%d %H:%M:%S") last_update_ago = f"{human_duration(lag_seconds)} ago" lag_total += lag_seconds lag_count += 1 created_ms = session.get("createdAt") runtime_display = "-" if created_ms: runtime_seconds = max(0.0, now_epoch - (created_ms / 1000.0)) runtime_display = human_duration(runtime_seconds) longest_runtime = max(longest_runtime, runtime_seconds) tokens = session.get("totalTokens") tokens_total += tokens or 0 tokens_display = format_tokens(tokens) cost_display = format_cost(tokens, args.cost_per_1k) model = html.escape(session.get("model", "-")) kind = html.escape(session.get("kind", "-")) status_badge = f"<span class='status {STATUS_CLASSES.get(status, 'status-run')}'>{status}</span>" cost_cell = f"<td>{html.escape(cost_display)}</td>" if args.cost_per_1k > 0 else "" rows_html.append( "<tr>" f"<td>{key}</td>" f"<td>{status_badge}</td>" f"<td>{runtime_display}</td>" f"<td>{last_update_ago}</td>" f"<td>{last_update_local}</td>" f"<td>{tokens_display}</td>" f"{cost_ ...[truncated 3263 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Make the default dashboard aggregate-only and require an explicit flag such as `--include-identifiers` before including raw session keys. 2. Replace session and channel identifiers with truncated, salted hashes suitable only for distinguishing rows within the dashboard. 3. Write generated snapshots to a user-selected data directory outside the Skill source tree instead of `assets/` by default. 4. Replace the populated bundled HTML file with an empty template or synthetic sample data. 5. Add an explicit warning before generating remotely shareable output and document that static hosting must use authentication and transport encryption. 6. Provide field-level controls for timestamps, models, token usage, costs, and channel identifiers. 7. Set restrictive file permissions where supported and document snapshot retention and secure deletion. 8. Correct the documentation so that it distinguishes in-memory CLI monitoring from persistent HTML generation. ]]>
