From fd84ff12006ab30dd23a363cad2a0d776d96958e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Apr 2026 11:05:17 +0000 Subject: [PATCH 1/3] Initial plan From d815cc10107ca260ec251fd1403152bb157c6390 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Apr 2026 11:17:06 +0000 Subject: [PATCH 2/3] =?UTF-8?q?fix(PanDomainTemplate):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E7=8E=B0=E6=9C=89=E7=BD=91=E7=9B=98=E5=9F=9F=E5=90=8D?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E6=AD=A3=E5=88=99=E8=A1=A8=E8=BE=BE=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agent-Logs-Url: https://github.com/qaiu/netdisk-fast-download/sessions/5523822b-ffe2-4e95-ac13-fd3f0dc41970 Co-authored-by: qaiu <29825328+qaiu@users.noreply.github.com> --- .../cn/qaiu/parser/PanDomainTemplate.java | 24 ++-- .../cn/qaiu/parser/PanDomainTemplateTest.java | 123 +++++++++++++++++- 2 files changed, 130 insertions(+), 17 deletions(-) diff --git a/parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java b/parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java index 3ce2421..d804202 100644 --- a/parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java +++ b/parser/src/main/java/cn/qaiu/parser/PanDomainTemplate.java @@ -68,8 +68,8 @@ public enum PanDomainTemplate { t-is.cn */ LZ("蓝奏云", - compile("https://(?:[a-zA-Z\\d-]+\\.)?(" + - "lanzoul|" + + compile("https://(?:[a-zA-Z\\d-]+\\.)?(?:" + + "(?:lanzoul|" + "lanzouh|" + "lanosso|" + "lanpv|" + @@ -95,14 +95,16 @@ public enum PanDomainTemplate { "lanzv|" + "dmpdmp|" + "lanrar|" + + "webgetstore|" + "lanzb|" + "lanzoux|" + "lanzout|" + "lanzouc|" + "lanzoui|" + "lanzoug|" + - "lanzoum" + - ")\\.com/(?.+)"), + "lanzoum)\\.com" + + "|t-is\\.cn" + + ")/(?.+)"), "https://w1.lanzn.com/{shareKey}", LzTool.class), @@ -115,7 +117,7 @@ public enum PanDomainTemplate { // https://lecloud.lenovo.com/share/ LE("联想乐云", - compile("https://lecloud?\\.lenovo\\.com/share/(?.+)"), + compile("https://lecloud\\.lenovo\\.com/share/(?.+)"), "https://lecloud.lenovo.com/share/{shareKey}", LeTool.class), @@ -241,7 +243,7 @@ public enum PanDomainTemplate { EcTool.class), // https://cowtransfer.com/s/ COW("奶牛快传", - compile("https://(.*)cowtransfer\\.com/s/(?.+)"), + compile("https://(?:[a-zA-Z\\d-]+\\.)?cowtransfer\\.com/s/(?.+)"), "https://cowtransfer.com/s/{shareKey}", CowTool.class), CT("城通网盘", @@ -264,7 +266,7 @@ public enum PanDomainTemplate { PodTool.class), // 404网盘 https://drive.google.com/file/d/xxx/view?usp=sharing PGD("GoogleDrive", - compile("https://drive\\.google\\.com/file/d/(?.+)/view(\\?usp=(sharing|drive_link))?"), + compile("https://(?:[a-zA-Z\\d-]+\\.)?drive\\.google\\.com/file/d/(?.+)/view(\\?usp=(sharing|drive_link))?"), "https://drive.google.com/file/d/{shareKey}/view?usp=sharing", PgdTool.class), // iCloud https://www.icloud.com.cn/iclouddrive/xxx#fonts @@ -274,11 +276,11 @@ public enum PanDomainTemplate { PicTool.class), // https://www.dropbox.com/scl/fi/cwnbms1yn8u6rcatzyta7/emqx-5.0.26-el7-amd64.tar.gz?rlkey=3uoi4bxz5mv93jmlaws0nlol1&e=8&st=fe0lclc2&dl=0 PDB("dropbox", - compile("https://www.dropbox.com/scl/fi/(?\\w+)/.+?rlkey=(?\\w+).*"), + compile("https://www\\.dropbox\\.com/scl/fi/(?\\w+)/.+?rlkey=(?\\w+).*"), "https://www.dropbox.com/scl/fi/{shareKey}/?rlkey={pwd}&dl=0", PdbTool.class), P115("115网盘", - compile("https://(115|anxia).com/s/(?\\w+)(\\?password=(?\\w+))?([&#].*)?"), + compile("https://(115|anxia)\\.com/s/(?\\w+)(\\?password=(?\\w+))?([&#].*)?"), "https://115.com/s/{shareKey}?password={pwd}", P115Tool.class), // 链接:https://www.yunpan.com/surl_yD7wz4VgU9v(提取码:fc70) @@ -319,7 +321,7 @@ public enum PanDomainTemplate { MnesTool.class), // https://music.163.com/#/song?id=xxx MNE("网易云音乐歌曲详情", - compile("https://(y.)?music\\.163\\.com/(#|m/)?song\\?id=(?.+)(&.*)?"), + compile("https://(y\\.)?music\\.163\\.com/(?:#/|m/)?song\\?id=(?.+)(&.*)?"), "https://music.163.com/#/song?id={shareKey}", MnesTool.MneTool.class), // https://c6.y.qq.com/base/fcgi-bin/u?__=xxx @@ -340,7 +342,7 @@ public enum PanDomainTemplate { MkgsTool.class), // https://www.kugou.com/share/2bi8Fe9CSV3.html?id=2bi8Fe9CSV3#6ed9gna4" MKGS2("酷狗音乐分享2", - compile("https://(?:[a-zA-Z\\d-]+\\.)?kugou\\.com/share/(?.+).html.*"), + compile("https://(?:[a-zA-Z\\d-]+\\.)?kugou\\.com/share/(?.+)\\.html.*"), "https://www.kugou.com/share/{shareKey}.html", MkgsTool.Mkgs2Tool.class), // https://www.kugou.com/mixsong/2bi8Fe9CSV3 diff --git a/parser/src/test/java/cn/qaiu/parser/PanDomainTemplateTest.java b/parser/src/test/java/cn/qaiu/parser/PanDomainTemplateTest.java index 4370cb9..1ced9bf 100644 --- a/parser/src/test/java/cn/qaiu/parser/PanDomainTemplateTest.java +++ b/parser/src/test/java/cn/qaiu/parser/PanDomainTemplateTest.java @@ -129,15 +129,126 @@ public class PanDomainTemplateTest { wsPattern.matcher("https://www.evil.com/f/abc123").matches()); } + @Test + public void testLzPatternWebgetstore() { + Pattern lzPattern = PanDomainTemplate.LZ.getPattern(); + + // webgetstore.com 以前遗漏,现已补入 + Matcher m1 = lzPattern.matcher("https://webgetstore.com/somekey"); + assertTrue("LZ should match webgetstore.com", m1.find()); + assertEquals("somekey", m1.group("KEY")); + + Matcher m2 = lzPattern.matcher("https://www.webgetstore.com/somekey"); + assertTrue("LZ should match www.webgetstore.com", m2.find()); + assertEquals("somekey", m2.group("KEY")); + + // t-is.cn 以前遗漏,现已补入 + Matcher m3 = lzPattern.matcher("https://t-is.cn/somekey"); + assertTrue("LZ should match t-is.cn", m3.find()); + assertEquals("somekey", m3.group("KEY")); + + Matcher m4 = lzPattern.matcher("https://www.t-is.cn/somekey"); + assertTrue("LZ should match www.t-is.cn", m4.find()); + assertEquals("somekey", m4.group("KEY")); + + // 已有域名仍然正常匹配 + Matcher m5 = lzPattern.matcher("https://www.lanzoul.com/somekey"); + assertTrue("LZ should match existing domain lanzoul.com", m5.find()); + assertEquals("somekey", m5.group("KEY")); + } + + @Test + public void testLePatternFix() { + Pattern lePattern = PanDomainTemplate.LE.getPattern(); + + // lecloud.lenovo.com 应匹配 + Matcher m1 = lePattern.matcher("https://lecloud.lenovo.com/share/abc123"); + assertTrue("LE should match lecloud.lenovo.com", m1.find()); + assertEquals("abc123", m1.group("KEY")); + + // leclou.lenovo.com (去掉'd') 不应匹配(原 lecloud? 的 bug) + assertFalse("LE should NOT match leclou.lenovo.com", + lePattern.matcher("https://leclou.lenovo.com/share/abc123").find()); + } + + @Test + public void testCowPatternFix() { + Pattern cowPattern = PanDomainTemplate.COW.getPattern(); + + // 正常域名 + Matcher m1 = cowPattern.matcher("https://cowtransfer.com/s/abc123"); + assertTrue("COW should match cowtransfer.com", m1.find()); + assertEquals("abc123", m1.group("KEY")); + + Matcher m2 = cowPattern.matcher("https://share.cowtransfer.com/s/abc123"); + assertTrue("COW should match share.cowtransfer.com", m2.find()); + assertEquals("abc123", m2.group("KEY")); + + // 潜在的URL注入攻击(修复前 (.*) 能匹配此类URL) + assertFalse("COW should NOT match redirect URLs", + cowPattern.matcher("https://evil.com/redirect/cowtransfer.com/s/abc").find()); + } + + @Test + public void testMnePatternFix() { + Pattern mnePattern = PanDomainTemplate.MNE.getPattern(); + + // 带 #/ 前缀的完整网页链接(修复前因 (y.) 未转义而存在 bug) + Matcher m1 = mnePattern.matcher("https://music.163.com/#/song?id=12345"); + assertTrue("MNE should match #/song format", m1.find()); + assertEquals("12345", m1.group("KEY")); + + // 带 m/ 前缀的移动端链接 + Matcher m2 = mnePattern.matcher("https://music.163.com/m/song?id=12345"); + assertTrue("MNE should match m/song format", m2.find()); + assertEquals("12345", m2.group("KEY")); + + // y.music.163.com 子域名 + Matcher m3 = mnePattern.matcher("https://y.music.163.com/song?id=12345"); + assertTrue("MNE should match y.music.163.com", m3.find()); + assertEquals("12345", m3.group("KEY")); + + // 原 (y.) 未转义时 yXmusic.163.com 会被误匹配(现已修复) + assertFalse("MNE should NOT match yXmusic.163.com", + mnePattern.matcher("https://yXmusic.163.com/song?id=12345").find()); + } + + @Test + public void testP115PatternFix() { + Pattern p115Pattern = PanDomainTemplate.P115.getPattern(); + + // 正常匹配 + Matcher m1 = p115Pattern.matcher("https://115.com/s/abc123"); + assertTrue("P115 should match 115.com", m1.find()); + assertEquals("abc123", m1.group("KEY")); + + Matcher m2 = p115Pattern.matcher("https://anxia.com/s/abc123"); + assertTrue("P115 should match anxia.com", m2.find()); + assertEquals("abc123", m2.group("KEY")); + + // 原 .com 未转义时 115Xcom 会被误匹配(现已修复) + assertFalse("P115 should NOT match 115Xcom", + p115Pattern.matcher("https://115Xcom/s/abc123").find()); + } + + @Test + public void testPgdSubdomain() { + Pattern pgdPattern = PanDomainTemplate.PGD.getPattern(); + + // 标准链接 + Matcher m1 = pgdPattern.matcher("https://drive.google.com/file/d/abc123/view?usp=sharing"); + assertTrue("PGD should match standard drive.google.com", m1.find()); + assertEquals("abc123", m1.group("KEY")); + + // 带子域名的链接(修复后支持) + Matcher m2 = pgdPattern.matcher("https://adsd.drive.google.com/file/d/151bR-nk-tOBm9QAFaozJIVt2WYyCMkoz/view"); + assertTrue("PGD should match subdomain.drive.google.com", m2.find()); + assertEquals("151bR-nk-tOBm9QAFaozJIVt2WYyCMkoz", m2.group("KEY")); + } + @Test public void verifyDuplicates() { - Matcher matcher = compile("https://(?:[a-zA-Z\\d-]+\\.)?drive\\.google\\.com/file/d/(?.+)/view(\\?usp=(sharing|drive_link))?") - .matcher("https://adsd.drive.google.com/file/d/151bR-nk-tOBm9QAFaozJIVt2WYyCMkoz/view"); - if (matcher.find()) { - System.out.println(matcher.group()); - System.out.println(matcher.group("KEY")); - } // 校验重复 Set collect = Arrays.stream(PanDomainTemplate.values()).map(PanDomainTemplate::getRegex).collect(Collectors.toSet()); From 49ec54a3b560c0c3846edef3f9047ed42ddff418 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Apr 2026 11:19:51 +0000 Subject: [PATCH 3/3] =?UTF-8?q?refactor(tests):=20=E6=94=B9=E5=96=84?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=B3=A8=E9=87=8A=E8=AF=B4=E6=98=8E=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E5=8F=AF=E8=AF=BB=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agent-Logs-Url: https://github.com/qaiu/netdisk-fast-download/sessions/5523822b-ffe2-4e95-ac13-fd3f0dc41970 Co-authored-by: qaiu <29825328+qaiu@users.noreply.github.com> --- .../java/cn/qaiu/parser/PanDomainTemplateTest.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/parser/src/test/java/cn/qaiu/parser/PanDomainTemplateTest.java b/parser/src/test/java/cn/qaiu/parser/PanDomainTemplateTest.java index 1ced9bf..c3ecd42 100644 --- a/parser/src/test/java/cn/qaiu/parser/PanDomainTemplateTest.java +++ b/parser/src/test/java/cn/qaiu/parser/PanDomainTemplateTest.java @@ -184,8 +184,10 @@ public class PanDomainTemplateTest { assertTrue("COW should match share.cowtransfer.com", m2.find()); assertEquals("abc123", m2.group("KEY")); - // 潜在的URL注入攻击(修复前 (.*) 能匹配此类URL) - assertFalse("COW should NOT match redirect URLs", + // 潜在的URL注入:`(.*)` 是贪婪捕获组,可匹配 `evil.com/redirect/` 等前缀, + // 使形如 `https://evil.com/redirect/cowtransfer.com/s/key` 的 URL 被误识别。 + // 修复后改为 `(?:[a-zA-Z\d-]+\.)?` 仅匹配一级合法子域名(可选),消除误匹配。 + assertFalse("COW should NOT match redirect URLs containing cowtransfer.com in path", cowPattern.matcher("https://evil.com/redirect/cowtransfer.com/s/abc").find()); } @@ -208,8 +210,10 @@ public class PanDomainTemplateTest { assertTrue("MNE should match y.music.163.com", m3.find()); assertEquals("12345", m3.group("KEY")); - // 原 (y.) 未转义时 yXmusic.163.com 会被误匹配(现已修复) - assertFalse("MNE should NOT match yXmusic.163.com", + // 原 (y.) 中 `.` 未转义(`.` 匹配任意字符):对于 `yXmusic.163.com`, + // `(y.)` 会消费 `yX`(y + 任意字符),剩余 `music.163.com` 再被 `music\.163\.com` 匹配,导致误匹配。 + // 修复后 `(y\.)` 要求字面 `.`,`yX` 中 X ≠ `.` 无法匹配,不再误匹配。 + assertFalse("MNE should NOT match yXmusic.163.com (old (y.) could erroneously match via backtracking)", mnePattern.matcher("https://yXmusic.163.com/song?id=12345").find()); }