Skip to content

Commit 3c5d378

Browse files
committed
fix: add HTML escaping and null safety to homescreen client
1 parent 7f91537 commit 3c5d378

1 file changed

Lines changed: 16 additions & 7 deletions

File tree

src/webview/client/homescreen.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,19 @@ function el<T extends HTMLElement>(id: string): T {
5151
}
5252

5353
function show(id: string): void {
54-
el(id).classList.remove("hidden");
54+
el(id)?.classList.remove("hidden");
5555
}
5656

5757
function hide(id: string): void {
58-
el(id).classList.add("hidden");
58+
el(id)?.classList.add("hidden");
59+
}
60+
61+
function escapeHtml(text: string): string {
62+
return text
63+
.replace(/&/g, "&amp;")
64+
.replace(/</g, "&lt;")
65+
.replace(/>/g, "&gt;")
66+
.replace(/"/g, "&quot;");
5967
}
6068

6169
// ── State rendering ───────────────────────────────────────────────────────────
@@ -98,8 +106,8 @@ function renderSkillRows(
98106
const statusClass = isInstalled ? "hs-ai-item-status--ok" : "hs-ai-item-status--none";
99107
const statusText = isInstalled ? "installed" : "—";
100108
return `<label class="hs-ai-item">
101-
<input type="checkbox" class="hs-ai-cb" data-skill="${s.dirName}" ${isInstalled ? "" : "checked"}>
102-
<span class="hs-ai-item-name" title="${s.description}">${s.name}</span>
109+
<input type="checkbox" class="hs-ai-cb" data-skill="${escapeHtml(s.dirName)}" ${isInstalled ? "" : "checked"}>
110+
<span class="hs-ai-item-name" title="${escapeHtml(s.description)}">${escapeHtml(s.name)}</span>
103111
<span class="hs-ai-item-status ${statusClass}">${statusText}</span>
104112
</label>`;
105113
})
@@ -122,8 +130,8 @@ function renderMcpRows(
122130
const statusClass = isConfigured ? "hs-ai-item-status--ok" : "hs-ai-item-status--none";
123131
const statusText = isConfigured ? "configured" : "—";
124132
return `<label class="hs-ai-item">
125-
<input type="checkbox" class="hs-ai-cb" data-mcp="${s.key}" ${isConfigured ? "" : "checked"}>
126-
<span class="hs-ai-item-name" title="${s.description}">${s.label}</span>
133+
<input type="checkbox" class="hs-ai-cb" data-mcp="${escapeHtml(s.key)}" ${isConfigured ? "" : "checked"}>
134+
<span class="hs-ai-item-name" title="${escapeHtml(s.description)}">${escapeHtml(s.label)}</span>
127135
<span class="hs-ai-item-status ${statusClass}">${statusText}</span>
128136
</label>`;
129137
})
@@ -205,7 +213,8 @@ function handleApply(): void {
205213

206214
function handleAiToolsData(msg: AiToolsDataMessage): void {
207215
if (msg.error) {
208-
el("hs-ai-error-msg").textContent = msg.error;
216+
const errEl = el("hs-ai-error-msg");
217+
if (errEl) { errEl.textContent = msg.error; }
209218
showPanelState("error");
210219
return;
211220
}

0 commit comments

Comments
 (0)