fix(vscode): validate webview files input and extract FileAttachment type

Address PR review remarks: sanitize runtime-untyped files from webview
with Array.isArray + field validation, and extract shared FileAttachment
type to reduce inline type repetition.
This commit is contained in:
Igor Šćekić
2026-02-13 15:36:31 +01:00
parent 49fa2f7647
commit 01dc6965b0
3 changed files with 20 additions and 19 deletions
+11 -6
View File
@@ -719,7 +719,7 @@ export class KiloProvider implements vscode.WebviewViewProvider {
providerID?: string,
modelID?: string,
agent?: string,
files?: Array<{ mime: string; url: string }>,
files?: unknown,
): Promise<void> {
if (!this.httpClient) {
this.postMessage({
@@ -748,6 +748,13 @@ export class KiloProvider implements vscode.WebviewViewProvider {
throw new Error("No session available")
}
// Sanitize files input from webview (runtime-untyped)
const sanitized = Array.isArray(files)
? files.filter(
(f): f is { mime: string; url: string } => f && typeof f.mime === "string" && typeof f.url === "string",
)
: []
// Build parts array with file context and user text
const parts: Array<{ type: "text"; text: string } | { type: "file"; mime: string; url: string }> = []
@@ -755,17 +762,15 @@ export class KiloProvider implements vscode.WebviewViewProvider {
const editor = vscode.window.activeTextEditor
if (editor && editor.document.uri.scheme === "file") {
const url = editor.document.uri.toString()
const already = files?.some((f) => f.url === url)
const already = sanitized.some((f) => f.url === url)
if (!already) {
parts.push({ type: "file", mime: "text/plain", url })
}
}
// Add any explicitly attached files from the webview
if (files) {
for (const f of files) {
parts.push({ type: "file", mime: f.mime, url: f.url })
}
for (const f of sanitized) {
parts.push({ type: "file", mime: f.mime, url: f.url })
}
parts.push({ type: "text", text })
@@ -33,6 +33,7 @@ import type {
ContextUsage,
AgentInfo,
ExtensionMessage,
FileAttachment,
} from "../types/messages"
// Store structure for messages and parts
@@ -87,12 +88,7 @@ interface SessionContextValue {
selectAgent: (name: string) => void
// Actions
sendMessage: (
text: string,
providerID?: string,
modelID?: string,
files?: Array<{ mime: string; url: string }>,
) => void
sendMessage: (text: string, providerID?: string, modelID?: string, files?: FileAttachment[]) => void
abort: () => void
compact: () => void
respondToPermission: (permissionId: string, response: "once" | "always" | "reject") => void
@@ -496,12 +492,7 @@ export const SessionProvider: ParentComponent = (props) => {
setSelectedAgentName(name)
}
function sendMessage(
text: string,
providerID?: string,
modelID?: string,
files?: Array<{ mime: string; url: string }>,
) {
function sendMessage(text: string, providerID?: string, modelID?: string, files?: FileAttachment[]) {
if (!server.isConnected()) {
console.warn("[Kilo New] Cannot send message: not connected")
return
@@ -519,6 +519,11 @@ export type ExtensionMessage =
// Messages FROM webview TO extension
// ============================================
export interface FileAttachment {
mime: string
url: string
}
export interface SendMessageRequest {
type: "sendMessage"
text: string
@@ -526,7 +531,7 @@ export interface SendMessageRequest {
providerID?: string
modelID?: string
agent?: string
files?: Array<{ mime: string; url: string }>
files?: FileAttachment[]
}
export interface AbortRequest {