From ea792b0034d4a88668c2feabbbb1e6b9434a0fd9 Mon Sep 17 00:00:00 2001 From: Florian Egger Date: Wed, 6 May 2026 22:38:31 +0200 Subject: [PATCH] Update safeParseJson to check for dangerous object keys recursively Improve detection of prototype pollution patterns in JSON parsing --- coverage/lcov-report/chat-view.ts.html | 167 ++-- coverage/lcov-report/error-handler.ts.html | 2 +- coverage/lcov-report/index.html | 86 +- coverage/lcov-report/ollama-client.ts.html | 507 +++++------ coverage/lcov-report/tool-executor.ts.html | 2 +- coverage/lcov-report/types.ts.html | 44 +- coverage/lcov-report/utils.ts.html | 427 +++++----- coverage/lcov-report/vault-indexer.ts.html | 2 +- coverage/lcov.info | 945 +++++++++++---------- src/utils.ts | 37 +- tests/utils.test.ts | 15 +- 11 files changed, 1105 insertions(+), 1129 deletions(-) diff --git a/coverage/lcov-report/chat-view.ts.html b/coverage/lcov-report/chat-view.ts.html index cf58eee..0b4f0d0 100644 --- a/coverage/lcov-report/chat-view.ts.html +++ b/coverage/lcov-report/chat-view.ts.html @@ -23,30 +23,30 @@
- 91.2% + 90.36% Statements - 197/216 + 197/218 +
+ + +
+ 80.28% + Branches + 57/71
82.85% - Branches - 58/70 -
- - -
- 82.35% Functions - 28/34 + 29/35
- 93.1% + 92.15% Lines - 189/203 + 188/204
@@ -540,21 +540,7 @@ 475 476 477 -478 -479 -480 -481 -482 -483 -484 -485 -486 -487 -488 -489 -490 -4911x -  +4781x       @@ -654,9 +640,10 @@       +136x 23x -8x -8x +  +        @@ -760,21 +747,17 @@     13x -13x -  -  -  -13x -7x -7x -  -7x     +  +  +13x +13x +13x +7x 7x   -  -  +7x   13x   @@ -783,21 +766,12 @@ 8x 2x   -  -  -  8x 2x   -  -  -  8x 2x   -  -  -  8x 8x 8x @@ -1049,7 +1023,6 @@ type KeyboardEvent = globalThis.KeyboardEvent; type HTMLTextAreaElement = globalThis.HTMLTextAreaElement; type HTMLButtonElement = globalThis.HTMLButtonElement; -type MouseEvent = globalThis.MouseEvent;   const DEFAULT_VAULT_SEARCH_LIMIT = 3; const MAX_MESSAGE_HISTORY = 50; @@ -1074,16 +1047,16 @@ export class ChatView extends ItemView { private vaultIndexer: VaultIndexer; private toolExecutor: ToolExecutor; private lastMessageEl: HTMLElement | null = null; - private newChatButton: HTMLElement | null = null; - private sendButton: HTMLElement | null = null; - private inputEl: HTMLElement | null = null; + private newChatButton: HTMLButtonElement | null = null; + private sendButton: HTMLButtonElement | null = null; + private inputEl: HTMLTextAreaElement | null = null; private chatContainer: HTMLElement | null = null; private sendButtonClickHandler: (() => Promise<void>) | null = null; private inputKeyDownHandler: ((e: KeyboardEvent) => Promise<void>) | null = null; private newChatButtonClickHandler: (() => void) | null = null; - private sendButtonEventHandler: ((e: MouseEvent) => void) | null = null; - private inputKeyDownEventHandler: ((e: KeyboardEvent) => void) | null = null; - private newChatButtonEventHandler: ((e: MouseEvent) => void) | null = null; + private sendButtonClickWrapper: (() => void) | null = null; + private inputKeyDownWrapper: ((e: KeyboardEvent) => void) | null = null; + private newChatButtonClickWrapper: (() => void) | null = null; private listenersAttached = false;   // Getters for testing @@ -1143,10 +1116,11 @@ export class ChatView extends ItemView { }   private cleanupStreamingResources(): void { - // Ensure any ongoing streaming is properly cleaned up - if (this.lastMessageEl && this.lastMessageEl.parentElement) { - this.lastMessageEl.parentElement.removeChild(this.lastMessageEl); - this.lastMessageEl = null; + // Only cleanup if there's still an active streaming message + const streamingMessage = this.messages.find((msg) => msg.isStreaming); + Iif (streamingMessage && this.lastMessageEl && this.lastMessageEl.parentElement) { + this.lastMessageEl.parentElement.removeChild(this.lastMessageEl); + this.lastMessageEl = null; } }   @@ -1165,7 +1139,7 @@ export class ChatView extends ItemView { this.sendButton = inputContainer.createEl('button', { cls: 'ollama-send-button', }); - (this.sendButton as HTMLButtonElement).textContent = 'Send'; + this.sendButton.textContent = 'Send'; }   if (!this.newChatButton) { @@ -1175,8 +1149,8 @@ export class ChatView extends ItemView { this.newChatButton = newChatContainer.createEl('button', { cls: 'ollama-new-chat-button', }); - (this.newChatButton as HTMLButtonElement).textContent = '🔄 New Chat'; - (this.newChatButton as HTMLButtonElement).title = 'Start a new conversation'; + this.newChatButton.textContent = '🔄 New Chat'; + this.newChatButton.title = 'Start a new conversation'; }   // Create immutable snapshot for rendering @@ -1228,8 +1202,8 @@ export class ChatView extends ItemView { if (!this.sendButtonClickHandler) { this.sendButtonClickHandler = async () => { Iif (!this.inputEl) return; - await this.handleUserInput((this.inputEl as HTMLTextAreaElement).value); - (this.inputEl as HTMLTextAreaElement).value = ''; + await this.handleUserInput(this.inputEl.value); + this.inputEl.value = ''; }; }   @@ -1237,60 +1211,47 @@ export class ChatView extends ItemView { this.inputKeyDownHandler = async (e: KeyboardEvent) => { if (!this.inputEl || e.key !== 'Enter' || e.shiftKey) return; e.preventDefault(); - await this.handleUserInput((this.inputEl as HTMLTextAreaElement).value); - (this.inputEl as HTMLTextAreaElement).value = ''; + await this.handleUserInput(this.inputEl.value); + this.inputEl.value = ''; }; }   - // Add event listeners - this.sendButtonEventHandler = () => { + // Create wrapper functions for event listeners + this.sendButtonClickWrapper = () => { void this.sendButtonClickHandler?.(); }; - this.inputKeyDownEventHandler = (e: KeyboardEvent) => { + this.inputKeyDownWrapper = (e: KeyboardEvent) => { void this.inputKeyDownHandler?.(e); }; - (this.sendButton as HTMLButtonElement).addEventListener('click', this.sendButtonEventHandler); - (this.inputEl as HTMLTextAreaElement).addEventListener( - 'keydown', - this.inputKeyDownEventHandler - ); + this.newChatButtonClickWrapper = () => { + void this.newChatButtonClickHandler?.(); + }; +  + // Add event listeners using wrappers + this.sendButton.addEventListener('click', this.sendButtonClickWrapper); + this.inputEl.addEventListener('keydown', this.inputKeyDownWrapper); if (this.newChatButton) { if (!this.newChatButtonClickHandler) { this.newChatButtonClickHandler = () => this.clearConversation(); } - this.newChatButtonEventHandler = () => { - this.newChatButtonClickHandler?.(); - }; - (this.newChatButton as HTMLButtonElement).addEventListener( - 'click', - this.newChatButtonEventHandler - ); + this.newChatButton.addEventListener('click', this.newChatButtonClickWrapper); } this.listenersAttached = true; }   private removeEventListeners(): void { - if (this.sendButton && this.sendButtonEventHandler) { - (this.sendButton as HTMLButtonElement).removeEventListener( - 'click', - this.sendButtonEventHandler - ); + if (this.sendButton && this.sendButtonClickWrapper) { + this.sendButton.removeEventListener('click', this.sendButtonClickWrapper); } - if (this.inputEl && this.inputKeyDownEventHandler) { - (this.inputEl as HTMLTextAreaElement).removeEventListener( - 'keydown', - this.inputKeyDownEventHandler - ); + if (this.inputEl && this.inputKeyDownWrapper) { + this.inputEl.removeEventListener('keydown', this.inputKeyDownWrapper); } - if (this.newChatButton && this.newChatButtonEventHandler) { - (this.newChatButton as HTMLButtonElement).removeEventListener( - 'click', - this.newChatButtonEventHandler - ); + if (this.newChatButton && this.newChatButtonClickWrapper) { + this.newChatButton.removeEventListener('click', this.newChatButtonClickWrapper); } - this.sendButtonEventHandler = null; - this.inputKeyDownEventHandler = null; - this.newChatButtonEventHandler = null; + this.sendButtonClickWrapper = null; + this.inputKeyDownWrapper = null; + this.newChatButtonClickWrapper = null; this.listenersAttached = false; }   @@ -1328,7 +1289,7 @@ export class ChatView extends ItemView {   private async handleUserInput(content: string) { Iif (!this.sendButton || !this.inputEl) return; - (this.sendButton as HTMLButtonElement).disabled = true; + this.sendButton.disabled = true;   try { // Guard against empty messages @@ -1528,7 +1489,7 @@ export class ChatView extends ItemView { this.cleanupStreamingResources(); } finally { if (this.sendButton) { - (this.sendButton as HTMLButtonElement).disabled = false; + this.sendButton.disabled = false; } } } @@ -1540,7 +1501,7 @@ export class ChatView extends ItemView {