T09 · Insecure Skill Coding Practices
- Location
src/ui/dashboard.html:715- Finding
Persistent Cross-Site Scripting in Project Navigation Can Compromise Gateway Credentials
- Content
View full analysis
``).join('') : '${badge(projectStatus(project))}${escapeHtml(project.name)}${percent(project.completion_score)}% completeNo projects yet.'; ``` ### Technical Analysis Project IDs originate from authenticated API input and are persisted in SQLite. The dashboard later inserts each ID into an inline JavaScript event handler assigned through `innerHTML`. `encodeURIComponent()` is URL encoding, not JavaScript-string escaping. In particular, it does not encode apostrophes. A crafted project ID can therefore terminate the single-quoted argument passed to `navigate()` and introduce additional JavaScript. An attacker can use an alphanumeric Base64 payload with an expression such as `eval(atob(...))`, avoiding many characters that `encodeURIComponent()` would otherwise encode. The issue is more ...[truncated 1836 chars]- Remediation
View remediation
{ navigate(`#project/${encodeURIComponent(project.id)}`); }); ``` 2. Construct untrusted content using `textContent`, DOM methods, or a template system with contextual escaping rather than `innerHTML`. 3. Apply separate encoders for HTML text, HTML attributes, URLs, and JavaScript strings; HTML escaping alone is not sufficient for executable contexts. 4. Stop storing a gateway bearer token in `localStorage`. Prefer an `HttpOnly`, `Secure`, and `SameSite=Strict` cookie or a short-lived, narrowly scoped UI session. 5. Add a restrictive Content Security Policy that disallows inline scripts and event handlers, for example with nonce-based scripts and no `unsafe-inline`. 6. Regenerate `src/ui/dashboard.generated.ts` after correcting `dashboard.html`. 7. Add security tests using project IDs containing apostrophes, parentheses, HTML metacharacters, and encoded JavaScript payloads. ]]>
