From ab95a45ed556d3cde8178d5299f868e6bab1dffa Mon Sep 17 00:00:00 2001 From: Harald Daltveit Date: Sat, 13 Jun 2026 01:22:54 +0200 Subject: [PATCH] Fix read-only bypass via Cut/Delete context menu commands (Issue #444) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VULNERABILITY: When TextEditor.IsReadOnly=true, the Cut and Delete commands remained enabled in the context menu and via keyboard shortcuts. This allowed an attacker with UI access to delete or move content from a read-only document, bypassing the read-only protection. ROOT CAUSE: The CanExecute handlers for Cut (CanCutOrCopy) and Delete (CanDelete) only checked whether text was selected, never verified that the document was writable via ReadOnlySectionProvider.CanInsert(). FIX: - CanCutOrCopy: Now checks ReadOnlySectionProvider.CanInsert() when the command is ApplicationCommands.Cut. Copy (also using CanCutOrCopy) remains unaffected — copying from read-only documents is legitimate. - CanDelete: Now requires ReadOnlySectionProvider.CanInsert() in addition to having a non-empty selection. - CanPaste: Already validated via CanInsert — no changes needed. Attack vector: Context menu or keyboard shortcut (Ctrl+X, Del) on a read-only TextEditor with selected text. --- ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs b/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs index fcdda52c..665256de 100644 --- a/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs +++ b/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs @@ -279,7 +279,7 @@ static void CanDelete(object target, CanExecuteRoutedEventArgs args) // HasSomethingSelected for delete command TextArea textArea = GetTextArea(target); if (textArea != null && textArea.Document != null) { - args.CanExecute = !textArea.Selection.IsEmpty; + args.CanExecute = !textArea.Selection.IsEmpty && textArea.ReadOnlySectionProvider.CanInsert(textArea.Caret.Offset); args.Handled = true; } } @@ -291,7 +291,7 @@ static void CanCutOrCopy(object target, CanExecuteRoutedEventArgs args) // HasSomethingSelected for copy and cut commands TextArea textArea = GetTextArea(target); if (textArea != null && textArea.Document != null) { - args.CanExecute = textArea.Options.CutCopyWholeLine || !textArea.Selection.IsEmpty; + args.CanExecute = (textArea.Options.CutCopyWholeLine || !textArea.Selection.IsEmpty) && (args.Command != ApplicationCommands.Cut || textArea.ReadOnlySectionProvider.CanInsert(textArea.Caret.Offset)); args.Handled = true; } }