From b0dae69889cf370726bdaed3d757067265f512db Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 21:06:21 +0000 Subject: [PATCH 1/6] Initial plan From 0d9e80eb36c385228fb290d4d1d1d87a73dc3c9b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 21:12:50 +0000 Subject: [PATCH 2/6] Add ExtensionFilterHelper and integrate with ViewWindowManager Co-authored-by: emako <24737061+emako@users.noreply.github.com> --- QuickLook/ViewWindowManager.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/QuickLook/ViewWindowManager.cs b/QuickLook/ViewWindowManager.cs index ee1ce2bdd..121873fe8 100644 --- a/QuickLook/ViewWindowManager.cs +++ b/QuickLook/ViewWindowManager.cs @@ -155,6 +155,10 @@ public void InvokePreview(string path = null) if (!path.StartsWith("::")) // CLSID return; + // Check extension filtering before proceeding + if (!ExtensionFilterHelper.IsExtensionAllowed(path)) + return; + _invokedPath = path; RunFocusMonitor(); @@ -175,6 +179,10 @@ public void InvokePluginPreview(string plugin, string path = null) if (!Directory.Exists(path) && !File.Exists(path)) return; + // Check extension filtering before proceeding + if (!ExtensionFilterHelper.IsExtensionAllowed(path)) + return; + RunFocusMonitor(); var matchedPlugin = PluginManager.GetInstance().LoadedPlugins.Find(p => From 15ec6b47491a596f745f0a1606cc3f90caa76849 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 21:14:29 +0000 Subject: [PATCH 3/6] Add ExtensionFilterHelper to main QuickLook project Co-authored-by: emako <24737061+emako@users.noreply.github.com> --- QuickLook/Helpers/ExtensionFilterHelper.cs | 189 +++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 QuickLook/Helpers/ExtensionFilterHelper.cs diff --git a/QuickLook/Helpers/ExtensionFilterHelper.cs b/QuickLook/Helpers/ExtensionFilterHelper.cs new file mode 100644 index 000000000..4956f37a2 --- /dev/null +++ b/QuickLook/Helpers/ExtensionFilterHelper.cs @@ -0,0 +1,189 @@ +// Copyright © 2017-2025 QL-Win Contributors +// +// This file is part of QuickLook program. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using QuickLook.Common.Helpers; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace QuickLook.Helpers; + +/// +/// Helper class for managing file extension allowlist/blocklist filtering. +/// When allowlist mode is enabled, only extensions in the allowlist can be previewed. +/// When blocklist mode is enabled (default), extensions in the blocklist are blocked from preview. +/// +public static class ExtensionFilterHelper +{ + private const string AllowlistKey = "ExtensionAllowlist"; + private const string BlocklistKey = "ExtensionBlocklist"; + private const string UseAllowlistModeKey = "UseExtensionAllowlistMode"; + + private static HashSet _allowlistCache; + private static HashSet _blocklistCache; + private static bool? _useAllowlistModeCache; + + /// + /// Gets or sets whether to use allowlist mode. + /// When true, only extensions in the allowlist can be previewed. + /// When false (default), extensions in the blocklist are blocked from preview. + /// + public static bool UseAllowlistMode + { + get + { + _useAllowlistModeCache ??= SettingHelper.Get(UseAllowlistModeKey, false); + return _useAllowlistModeCache.Value; + } + set + { + _useAllowlistModeCache = value; + SettingHelper.Set(UseAllowlistModeKey, value); + } + } + + /// + /// Gets the current allowlist of file extensions. + /// Extensions should be in the format ".ext" (with leading dot). + /// + public static HashSet Allowlist + { + get + { + if (_allowlistCache == null) + { + var list = SettingHelper.Get(AllowlistKey, string.Empty); + _allowlistCache = ParseExtensionList(list); + } + return _allowlistCache; + } + } + + /// + /// Gets the current blocklist of file extensions. + /// Extensions should be in the format ".ext" (with leading dot). + /// + public static HashSet Blocklist + { + get + { + if (_blocklistCache == null) + { + var list = SettingHelper.Get(BlocklistKey, string.Empty); + _blocklistCache = ParseExtensionList(list); + } + return _blocklistCache; + } + } + + /// + /// Sets the allowlist of file extensions. + /// + /// Collection of extensions in the format ".ext" (with leading dot). + public static void SetAllowlist(IEnumerable extensions) + { + var normalized = NormalizeExtensions(extensions); + _allowlistCache = normalized; + SettingHelper.Set(AllowlistKey, string.Join(";", normalized)); + } + + /// + /// Sets the blocklist of file extensions. + /// + /// Collection of extensions in the format ".ext" (with leading dot). + public static void SetBlocklist(IEnumerable extensions) + { + var normalized = NormalizeExtensions(extensions); + _blocklistCache = normalized; + SettingHelper.Set(BlocklistKey, string.Join(";", normalized)); + } + + /// + /// Checks if a file path is allowed for preview based on the current filter settings. + /// + /// The file path to check. + /// True if the file is allowed for preview, false if it should be blocked. + public static bool IsExtensionAllowed(string path) + { + if (string.IsNullOrEmpty(path)) + return true; + + // Directories are always allowed (extension filtering only applies to files) + if (Directory.Exists(path)) + return true; + + var extension = Path.GetExtension(path); + if (string.IsNullOrEmpty(extension)) + return true; + + extension = extension.ToLowerInvariant(); + + if (UseAllowlistMode) + { + // In allowlist mode: only allow if extension is in the allowlist + // If allowlist is empty, allow all (no filtering) + return Allowlist.Count == 0 || Allowlist.Contains(extension); + } + else + { + // In blocklist mode: block if extension is in the blocklist + return !Blocklist.Contains(extension); + } + } + + /// + /// Clears the cached settings, forcing a reload from the config file. + /// + public static void ClearCache() + { + _allowlistCache = null; + _blocklistCache = null; + _useAllowlistModeCache = null; + } + + private static HashSet ParseExtensionList(string list) + { + if (string.IsNullOrWhiteSpace(list)) + return new HashSet(StringComparer.OrdinalIgnoreCase); + + return new HashSet( + list.Split(new[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries) + .Select(NormalizeExtension) + .Where(e => !string.IsNullOrEmpty(e)), + StringComparer.OrdinalIgnoreCase); + } + + private static HashSet NormalizeExtensions(IEnumerable extensions) + { + return new HashSet( + extensions.Select(NormalizeExtension).Where(e => !string.IsNullOrEmpty(e)), + StringComparer.OrdinalIgnoreCase); + } + + private static string NormalizeExtension(string ext) + { + if (string.IsNullOrWhiteSpace(ext)) + return null; + + ext = ext.Trim().ToLowerInvariant(); + if (!ext.StartsWith(".")) + ext = "." + ext; + + return ext; + } +} From f54f037b01e075faf7d8489d5cb7c97bf6ff1473 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 21:16:22 +0000 Subject: [PATCH 4/6] Address code review feedback: improve docs and remove redundant I/O Co-authored-by: emako <24737061+emako@users.noreply.github.com> --- QuickLook/Helpers/ExtensionFilterHelper.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/QuickLook/Helpers/ExtensionFilterHelper.cs b/QuickLook/Helpers/ExtensionFilterHelper.cs index 4956f37a2..579c360db 100644 --- a/QuickLook/Helpers/ExtensionFilterHelper.cs +++ b/QuickLook/Helpers/ExtensionFilterHelper.cs @@ -25,8 +25,17 @@ namespace QuickLook.Helpers; /// /// Helper class for managing file extension allowlist/blocklist filtering. -/// When allowlist mode is enabled, only extensions in the allowlist can be previewed. -/// When blocklist mode is enabled (default), extensions in the blocklist are blocked from preview. +/// +/// Blocklist mode (default): All extensions are allowed except those in the blocklist. +/// If the blocklist is empty, all files are allowed. +/// +/// +/// Allowlist mode: Only extensions in the allowlist can be previewed. +/// If the allowlist is empty in allowlist mode, all files are allowed (no filtering). +/// +/// +/// Directories and files without extensions are always allowed regardless of the mode. +/// /// public static class ExtensionFilterHelper { @@ -123,11 +132,9 @@ public static bool IsExtensionAllowed(string path) if (string.IsNullOrEmpty(path)) return true; - // Directories are always allowed (extension filtering only applies to files) - if (Directory.Exists(path)) - return true; - var extension = Path.GetExtension(path); + + // Files without extensions are always allowed (includes directories) if (string.IsNullOrEmpty(extension)) return true; From eade320211f3eb17f06c1dcf37efdb3af349ec1a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 21:21:36 +0000 Subject: [PATCH 5/6] Address code review: optimize directory checks and use static separator array Co-authored-by: emako <24737061+emako@users.noreply.github.com> --- QuickLook/Helpers/ExtensionFilterHelper.cs | 3 ++- QuickLook/ViewWindowManager.cs | 14 ++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/QuickLook/Helpers/ExtensionFilterHelper.cs b/QuickLook/Helpers/ExtensionFilterHelper.cs index 579c360db..32d6f0ec4 100644 --- a/QuickLook/Helpers/ExtensionFilterHelper.cs +++ b/QuickLook/Helpers/ExtensionFilterHelper.cs @@ -42,6 +42,7 @@ public static class ExtensionFilterHelper private const string AllowlistKey = "ExtensionAllowlist"; private const string BlocklistKey = "ExtensionBlocklist"; private const string UseAllowlistModeKey = "UseExtensionAllowlistMode"; + private static readonly char[] ExtensionSeparators = [';', ',']; private static HashSet _allowlistCache; private static HashSet _blocklistCache; @@ -169,7 +170,7 @@ private static HashSet ParseExtensionList(string list) return new HashSet(StringComparer.OrdinalIgnoreCase); return new HashSet( - list.Split(new[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries) + list.Split(ExtensionSeparators, StringSplitOptions.RemoveEmptyEntries) .Select(NormalizeExtension) .Where(e => !string.IsNullOrEmpty(e)), StringComparer.OrdinalIgnoreCase); diff --git a/QuickLook/ViewWindowManager.cs b/QuickLook/ViewWindowManager.cs index 121873fe8..563ead67b 100644 --- a/QuickLook/ViewWindowManager.cs +++ b/QuickLook/ViewWindowManager.cs @@ -151,12 +151,13 @@ public void InvokePreview(string path = null) if (_viewerWindow.IsVisible && path == _invokedPath) return; - if (!Directory.Exists(path) && !File.Exists(path)) + var isDirectory = Directory.Exists(path); + if (!isDirectory && !File.Exists(path)) if (!path.StartsWith("::")) // CLSID return; - // Check extension filtering before proceeding - if (!ExtensionFilterHelper.IsExtensionAllowed(path)) + // Check extension filtering before proceeding (skip for directories) + if (!isDirectory && !ExtensionFilterHelper.IsExtensionAllowed(path)) return; _invokedPath = path; @@ -176,11 +177,12 @@ public void InvokePluginPreview(string plugin, string path = null) if (string.IsNullOrEmpty(path)) return; - if (!Directory.Exists(path) && !File.Exists(path)) + var isDirectory = Directory.Exists(path); + if (!isDirectory && !File.Exists(path)) return; - // Check extension filtering before proceeding - if (!ExtensionFilterHelper.IsExtensionAllowed(path)) + // Check extension filtering before proceeding (skip for directories) + if (!isDirectory && !ExtensionFilterHelper.IsExtensionAllowed(path)) return; RunFocusMonitor(); From d6de335da5d91f21d8bf40cac9b50ff6619719b2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 21:23:11 +0000 Subject: [PATCH 6/6] Add trailing newline to ExtensionFilterHelper.cs Co-authored-by: emako <24737061+emako@users.noreply.github.com> --- QuickLook/Helpers/ExtensionFilterHelper.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/QuickLook/Helpers/ExtensionFilterHelper.cs b/QuickLook/Helpers/ExtensionFilterHelper.cs index 32d6f0ec4..0027e2919 100644 --- a/QuickLook/Helpers/ExtensionFilterHelper.cs +++ b/QuickLook/Helpers/ExtensionFilterHelper.cs @@ -195,3 +195,4 @@ private static string NormalizeExtension(string ext) return ext; } } +