Compare commits

..

4 Commits

Author SHA1 Message Date
qaiu
442f9d1d2e Merge pull request #176 from qaiu/copilot/optimize-pandomain-template
fix(PanDomainTemplate): 修复现有网盘域名模板正则表达式中的多处缺陷
2026-04-12 19:45:06 +08:00
copilot-swe-agent[bot]
49ec54a3b5 refactor(tests): 改善测试注释说明,增强可读性
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>
2026-04-12 11:19:51 +00:00
copilot-swe-agent[bot]
d815cc1010 fix(PanDomainTemplate): 优化现有网盘域名模板正则表达式
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>
2026-04-12 11:17:06 +00:00
copilot-swe-agent[bot]
fd84ff1200 Initial plan 2026-04-12 11:05:17 +00:00
2 changed files with 126 additions and 28 deletions

View File

@@ -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/(?<KEY>.+)"),
"lanzoum)\\.com" +
"|t-is\\.cn" +
")/(?<KEY>.+)"),
"https://w1.lanzn.com/{shareKey}",
LzTool.class),
@@ -113,9 +115,9 @@ public enum PanDomainTemplate {
"https://www.feijix.com/s/{shareKey}",
FjTool.class),
// https://lecloud.lenovo.com/share/ https://lecloud.lenovo.com/mshare/
// https://lecloud.lenovo.com/share/
LE("联想乐云",
compile("https://lecloud?\\.lenovo\\.com/m?share/(?<KEY>.+)"),
compile("https://lecloud\\.lenovo\\.com/share/(?<KEY>.+)"),
"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/(?<KEY>.+)"),
compile("https://(?:[a-zA-Z\\d-]+\\.)?cowtransfer\\.com/s/(?<KEY>.+)"),
"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/(?<KEY>.+)/view(\\?usp=(sharing|drive_link))?"),
compile("https://(?:[a-zA-Z\\d-]+\\.)?drive\\.google\\.com/file/d/(?<KEY>.+)/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/(?<KEY>\\w+)/.+?rlkey=(?<PWD>\\w+).*"),
compile("https://www\\.dropbox\\.com/scl/fi/(?<KEY>\\w+)/.+?rlkey=(?<PWD>\\w+).*"),
"https://www.dropbox.com/scl/fi/{shareKey}/?rlkey={pwd}&dl=0",
PdbTool.class),
P115("115网盘",
compile("https://(115|anxia).com/s/(?<KEY>\\w+)(\\?password=(?<PWD>\\w+))?([&#].*)?"),
compile("https://(115|anxia)\\.com/s/(?<KEY>\\w+)(\\?password=(?<PWD>\\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=(?<KEY>.+)(&.*)?"),
compile("https://(y\\.)?music\\.163\\.com/(?:#/|m/)?song\\?id=(?<KEY>.+)(&.*)?"),
"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/(?<KEY>.+).html.*"),
compile("https://(?:[a-zA-Z\\d-]+\\.)?kugou\\.com/share/(?<KEY>.+)\\.html.*"),
"https://www.kugou.com/share/{shareKey}.html",
MkgsTool.Mkgs2Tool.class),
// https://www.kugou.com/mixsong/2bi8Fe9CSV3

View File

@@ -130,33 +130,129 @@ public class PanDomainTemplateTest {
}
@Test
public void testLePatternMatching() {
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();
// /share/ 格式
// lecloud.lenovo.com 应匹配
Matcher m1 = lePattern.matcher("https://lecloud.lenovo.com/share/abc123");
assertTrue("LE pattern should match /share/ format", m1.matches());
assertTrue("LE should match lecloud.lenovo.com", m1.find());
assertEquals("abc123", m1.group("KEY"));
// /mshare/ 格式
Matcher m2 = lePattern.matcher("https://lecloud.lenovo.com/mshare/xyz789");
assertTrue("LE pattern should match /mshare/ format", m2.matches());
assertEquals("xyz789", m2.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());
}
// 负例:错误路径不匹配
assertFalse("Wrong path should not match",
lePattern.matcher("https://lecloud.lenovo.com/s/abc123").matches());
@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注入`(.*)` 是贪婪捕获组,可匹配 `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());
}
@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`
// `(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());
}
@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/(?<KEY>.+)/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<String> collect =
Arrays.stream(PanDomainTemplate.values()).map(PanDomainTemplate::getRegex).collect(Collectors.toSet());