From b444f481002a86a7fe23a423dcd1816603769a66 Mon Sep 17 00:00:00 2001 From: guopenghui Date: Thu, 14 May 2026 23:07:10 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat(system):=20=E6=94=AF=E6=8C=81=20Window?= =?UTF-8?q?s=20=E4=B8=8A=E7=9A=84=E5=89=8D=E5=BE=80=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=A4=B9=E6=8C=87=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal-plugins/system/public/plugin.json | 2 +- .../main/systemPluginOpenFolderRegex.test.ts | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/main/systemPluginOpenFolderRegex.test.ts diff --git a/internal-plugins/system/public/plugin.json b/internal-plugins/system/public/plugin.json index 60a32b02..b1c9fb25 100644 --- a/internal-plugins/system/public/plugin.json +++ b/internal-plugins/system/public/plugin.json @@ -69,7 +69,7 @@ "cmds": [ { "type": "regex", - "match": "/^(?:~?\\/[^/\\n\\r\\f\\v]+)+\\/?$/", + "match": "/^(?:(?:~?\\/[^/\\n\\r\\f\\v]+)+\\/?|[A-Za-z]:\\\\(?:[^\\\\\\n\\r\\f\\v]+\\\\?)*)$/", "label": "前往文件夹", "minLength": 2 } diff --git a/tests/main/systemPluginOpenFolderRegex.test.ts b/tests/main/systemPluginOpenFolderRegex.test.ts new file mode 100644 index 00000000..1f549e75 --- /dev/null +++ b/tests/main/systemPluginOpenFolderRegex.test.ts @@ -0,0 +1,48 @@ +import { describe, expect, it } from 'vitest' +import { readFileSync } from 'fs' +import path from 'path' + +function getOpenFolderRegex(): RegExp { + const pluginJsonPath = path.join( + process.cwd(), + 'internal-plugins', + 'system', + 'public', + 'plugin.json' + ) + const pluginConfig = JSON.parse(readFileSync(pluginJsonPath, 'utf-8')) + const feature = pluginConfig.features.find((item: any) => item.code === 'open-folder') + const regexCmd = feature?.cmds?.find((item: any) => item.type === 'regex') + const regexString = regexCmd?.match + const match = typeof regexString === 'string' ? regexString.match(/^\/(.+)\/([gimuy]*)$/) : null + + if (!match) { + throw new Error('open-folder regex command is missing or invalid') + } + + return new RegExp(match[1], match[2]) +} + +describe('system plugin open-folder regex', () => { + const regex = getOpenFolderRegex() + + it('matches Windows drive paths that use backslashes', () => { + expect(regex.test('C:\\Users\\Test\\Desktop')).toBe(true) + expect(regex.test('D:\\Work Projects\\ZTools')).toBe(true) + }) + + it('matches Unix-style folder paths', () => { + expect(regex.test('~/Downloads')).toBe(true) + expect(regex.test('/Users/test/Documents')).toBe(true) + }) + + it('does not match unsupported folder path forms', () => { + expect(regex.test('C:/Users/Test/Desktop')).toBe(false) + expect(regex.test('\\\\server\\share\\folder')).toBe(false) + expect(regex.test('%USERPROFILE%\\Desktop')).toBe(false) + }) + + it('does not match urls', () => { + expect(regex.test('https://example.com')).toBe(false) + }) +}) From f42d8d6445d3948b3fe01f80615de209a680ae4e Mon Sep 17 00:00:00 2001 From: guopenghui Date: Fri, 15 May 2026 00:10:20 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat(system):=20=E6=8E=92=E9=99=A4=20Window?= =?UTF-8?q?s=20=E8=B7=AF=E5=BE=84=E9=9D=9E=E6=B3=95=E5=AD=97=E7=AC=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal-plugins/system/public/plugin.json | 2 +- .../main/systemPluginOpenFolderRegex.test.ts | 32 +++++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/internal-plugins/system/public/plugin.json b/internal-plugins/system/public/plugin.json index b1c9fb25..f51c087b 100644 --- a/internal-plugins/system/public/plugin.json +++ b/internal-plugins/system/public/plugin.json @@ -69,7 +69,7 @@ "cmds": [ { "type": "regex", - "match": "/^(?:(?:~?\\/[^/\\n\\r\\f\\v]+)+\\/?|[A-Za-z]:\\\\(?:[^\\\\\\n\\r\\f\\v]+\\\\?)*)$/", + "match": "/^(?:(?:~?\\/[^/\\n\\r\\f\\v]+)+\\/?|[A-Za-z]:\\\\(?:[^\\\\/:*?\"<>|\\n\\r\\f\\v]+\\\\?)*)$/", "label": "前往文件夹", "minLength": 2 } diff --git a/tests/main/systemPluginOpenFolderRegex.test.ts b/tests/main/systemPluginOpenFolderRegex.test.ts index 1f549e75..4a3394dc 100644 --- a/tests/main/systemPluginOpenFolderRegex.test.ts +++ b/tests/main/systemPluginOpenFolderRegex.test.ts @@ -2,6 +2,20 @@ import { describe, expect, it } from 'vitest' import { readFileSync } from 'fs' import path from 'path' +interface PluginFeatureCommand { + type?: string + match?: string +} + +interface PluginFeature { + code: string + cmds: Array +} + +interface PluginConfig { + features: PluginFeature[] +} + function getOpenFolderRegex(): RegExp { const pluginJsonPath = path.join( process.cwd(), @@ -10,9 +24,11 @@ function getOpenFolderRegex(): RegExp { 'public', 'plugin.json' ) - const pluginConfig = JSON.parse(readFileSync(pluginJsonPath, 'utf-8')) - const feature = pluginConfig.features.find((item: any) => item.code === 'open-folder') - const regexCmd = feature?.cmds?.find((item: any) => item.type === 'regex') + const pluginConfig = JSON.parse(readFileSync(pluginJsonPath, 'utf-8')) as PluginConfig + const feature = pluginConfig.features.find((item) => item.code === 'open-folder') + const regexCmd = feature?.cmds?.find( + (item): item is PluginFeatureCommand => typeof item !== 'string' && item.type === 'regex' + ) const regexString = regexCmd?.match const match = typeof regexString === 'string' ? regexString.match(/^\/(.+)\/([gimuy]*)$/) : null @@ -42,6 +58,16 @@ describe('system plugin open-folder regex', () => { expect(regex.test('%USERPROFILE%\\Desktop')).toBe(false) }) + it('does not match Windows paths that contain invalid path characters', () => { + expect(regex.test('C:\\Users\\bad*name')).toBe(false) + expect(regex.test('C:\\Users\\bad?name')).toBe(false) + expect(regex.test('C:\\Users\\bad"name')).toBe(false) + expect(regex.test('C:\\Users\\badname')).toBe(false) + expect(regex.test('C:\\Users\\bad|name')).toBe(false) + expect(regex.test('C:\\Users\\bad:name')).toBe(false) + }) + it('does not match urls', () => { expect(regex.test('https://example.com')).toBe(false) })