mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2026-08-27 12:02:01 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4a5240fcbc | |||
| ecd365767a | |||
| f358140974 | |||
| 541c21f963 | |||
| bdd253383d | |||
| 0fb53d5159 | |||
| b9ff408bdd | |||
| aeb4394cf8 | |||
| 8d419d3265 | |||
| b70e30796f | |||
| 363c603bbb |
+11
-26
@@ -158,37 +158,19 @@ jobs:
|
|||||||
path: web-service/target/package
|
path: web-service/target/package
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# jdeps 分析 → 确定所需 JDK 模块
|
# 选择 JDK 模块
|
||||||
# ============================================================
|
# ============================================================
|
||||||
- name: 分析所需 JDK 模块(jdeps)
|
- name: 选择完整 JDK 模块集合
|
||||||
run: |
|
run: |
|
||||||
MAIN_JAR="web-service/target/package/netdisk-fast-download.jar"
|
# 123 网盘/自定义 JS 解析器会通过 javax.script、Nashorn 和 SPI/反射加载模块,
|
||||||
LIB_DIR="web-service/target/package/lib"
|
# jdeps 无法稳定识别这些运行时依赖。这里优先保证原生包可运行,使用完整
|
||||||
|
# JDK 模块集合生成运行时,避免 Windows 精简 JRE 缺 java.scripting/jdk.dynalink 等模块。
|
||||||
CP=""
|
printf 'JDK_MODULES=ALL-MODULE-PATH\n' >> $GITHUB_ENV
|
||||||
for jar in "$LIB_DIR"/*.jar; do
|
|
||||||
CP="$CP${CP:+:}$jar"
|
|
||||||
done
|
|
||||||
|
|
||||||
RAW_MODULES=$(jdeps --print-module-deps --ignore-missing-deps --multi-release 17 \
|
|
||||||
--class-path "$CP" "$MAIN_JAR" 2>/dev/null | head -n 1 | tr -d '\r\n' || true)
|
|
||||||
|
|
||||||
if [ -z "$RAW_MODULES" ] || [[ "$RAW_MODULES" == *"Missing"* ]] || [[ "$RAW_MODULES" == *"Error"* ]]; then
|
|
||||||
# ⚠️ 回退列表:若项目新增了需要 java.* / jdk.* 模块的依赖,需同步更新此处
|
|
||||||
RAW_MODULES="java.base,java.logging,java.sql,java.naming,java.management,java.xml,java.scripting,jdk.unsupported,java.net.http,java.instrument,java.security.jgss,java.security.sasl,java.desktop,jdk.crypto.ec"
|
|
||||||
echo "jdeps 分析失败,使用回退模块列表"
|
|
||||||
else
|
|
||||||
# 补上 jdeps 无法检测的反射/SPI依赖
|
|
||||||
RAW_MODULES="$RAW_MODULES,java.desktop,java.scripting,jdk.crypto.ec"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "detected modules: $RAW_MODULES"
|
|
||||||
printf 'JDK_MODULES=%s\n' "$RAW_MODULES" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# jlink 生成精简 JRE
|
# jlink 生成完整模块 JRE
|
||||||
# ============================================================
|
# ============================================================
|
||||||
- name: 生成精简 JRE(jlink)
|
- name: 生成完整模块 JRE(jlink)
|
||||||
run: |
|
run: |
|
||||||
jlink \
|
jlink \
|
||||||
--module-path "$JAVA_HOME/jmods" \
|
--module-path "$JAVA_HOME/jmods" \
|
||||||
@@ -215,7 +197,10 @@ jobs:
|
|||||||
ls -la "$JRE_BIN"/*.dll 2>/dev/null || echo "(无 .dll 文件)"
|
ls -la "$JRE_BIN"/*.dll 2>/dev/null || echo "(无 .dll 文件)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "=== 关键 JDK 模块校验 ==="
|
||||||
"native-package/netdisk-fast-download/jre/bin/java" --list-modules | grep -q '^java.scripting@'
|
"native-package/netdisk-fast-download/jre/bin/java" --list-modules | grep -q '^java.scripting@'
|
||||||
|
"native-package/netdisk-fast-download/jre/bin/java" --list-modules | grep -q '^jdk.dynalink@'
|
||||||
|
"native-package/netdisk-fast-download/jre/bin/java" --list-modules | grep -q '^jdk.unsupported@'
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# 组装包目录
|
# 组装包目录
|
||||||
|
|||||||
@@ -531,7 +531,13 @@ Core模块集成Vert.x实现类似spring的注解式路由API
|
|||||||
|
|
||||||
## Star History
|
## Star History
|
||||||
|
|
||||||
[](https://star-history.com/#qaiu/netdisk-fast-download&Date)
|
<a href="https://www.star-history.com/?repos=qaiu%2Fnetdisk-fast-download&type=date&legend=bottom-right">
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=qaiu/netdisk-fast-download&type=date&theme=dark&legend=bottom-right&sealed_token=dfQO_dJcTqcPkEnM7SfxRyHoFbV5Ah4LxoEhdlheMn4T2YLEV_WETxFZexeAbWN5OmNyYuycWan2d42PAFbw0CuU4oCTKgehfErFJ9eVl2CyVpP_4xrdQw" />
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=qaiu/netdisk-fast-download&type=date&legend=bottom-right&sealed_token=dfQO_dJcTqcPkEnM7SfxRyHoFbV5Ah4LxoEhdlheMn4T2YLEV_WETxFZexeAbWN5OmNyYuycWan2d42PAFbw0CuU4oCTKgehfErFJ9eVl2CyVpP_4xrdQw" />
|
||||||
|
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=qaiu/netdisk-fast-download&type=date&legend=bottom-right&sealed_token=dfQO_dJcTqcPkEnM7SfxRyHoFbV5Ah4LxoEhdlheMn4T2YLEV_WETxFZexeAbWN5OmNyYuycWan2d42PAFbw0CuU4oCTKgehfErFJ9eVl2CyVpP_4xrdQw" />
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
|
||||||
## **免责声明**
|
## **免责声明**
|
||||||
- 用户在使用本项目时,应自行承担风险,并确保其行为符合当地法律法规。开发者不对用户因使用本项目而导致的任何后果负责。
|
- 用户在使用本项目时,应自行承担风险,并确保其行为符合当地法律法规。开发者不对用户因使用本项目而导致的任何后果负责。
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.postgresql</groupId>
|
<groupId>org.postgresql</groupId>
|
||||||
<artifactId>postgresql</artifactId>
|
<artifactId>postgresql</artifactId>
|
||||||
<version>42.7.11</version>
|
<version>42.7.12</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -201,6 +204,76 @@ public abstract class PanBase implements IPanTool, Closeable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSRF 防护: 校验通用自定义域名解析器 (CE/Ce4/Kd/Other 等) 即将请求的目标主机,
|
||||||
|
* 拒绝解析到回环/内网/链路本地/组播等非公网地址的域名, 阻止攻击者通过可控 DNS
|
||||||
|
* 记录 (或直接填写内网域名) 让服务端向内网/云元数据接口发起请求。
|
||||||
|
* <p>
|
||||||
|
* 必须在子类 parse() 中构造出 baseUrl/发起任何 clientSession 请求之前调用。
|
||||||
|
*
|
||||||
|
* @param url 从 shareLinkInfo.getShareUrl() 解析出的 URL
|
||||||
|
* @throws IOException 当主机无法解析或解析结果落入禁止的地址段时抛出
|
||||||
|
*/
|
||||||
|
protected static void assertPublicHost(URL url) throws IOException {
|
||||||
|
String host = url.getHost();
|
||||||
|
InetAddress[] addresses;
|
||||||
|
try {
|
||||||
|
addresses = InetAddress.getAllByName(host);
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
throw new IOException("无法解析目标主机: " + host, e);
|
||||||
|
}
|
||||||
|
if (addresses.length == 0) {
|
||||||
|
throw new IOException("无法解析目标主机: " + host);
|
||||||
|
}
|
||||||
|
for (InetAddress addr : addresses) {
|
||||||
|
if (isDisallowedAddress(addr)) {
|
||||||
|
throw new IOException("目标地址不允许访问(内网/回环/链路本地/组播): "
|
||||||
|
+ host + " -> " + addr.getHostAddress());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断地址是否落入禁止访问的范围: 0.0.0.0/8, 10/8, 127/8, 169.254/16(含云元数据
|
||||||
|
* 169.254.169.254), 172.16/12, 192.168/16, 100.64/10(CGNAT), 224/4~255/4(组播/保留),
|
||||||
|
* 以及对应的 IPv6 回环/链路本地/唯一本地地址(fc00::/7)/组播地址; IPv4-映射的 IPv6
|
||||||
|
* 地址(::ffff:a.b.c.d) 会先还原为 IPv4 再判断,避免绕过。
|
||||||
|
*/
|
||||||
|
private static boolean isDisallowedAddress(InetAddress addr) {
|
||||||
|
byte[] bytes = addr.getAddress();
|
||||||
|
if (bytes.length == 16 && isIPv4Mapped(bytes)) {
|
||||||
|
byte[] v4 = new byte[4];
|
||||||
|
System.arraycopy(bytes, 12, v4, 0, 4);
|
||||||
|
bytes = v4;
|
||||||
|
}
|
||||||
|
if (bytes.length == 4) {
|
||||||
|
int b0 = bytes[0] & 0xFF;
|
||||||
|
int b1 = bytes[1] & 0xFF;
|
||||||
|
if (b0 == 0) return true; // 0.0.0.0/8
|
||||||
|
if (b0 == 10) return true; // 10.0.0.0/8
|
||||||
|
if (b0 == 127) return true; // 127.0.0.0/8 loopback
|
||||||
|
if (b0 == 169 && b1 == 254) return true; // 169.254.0.0/16 (含云元数据 169.254.169.254)
|
||||||
|
if (b0 == 172 && b1 >= 16 && b1 <= 31) return true; // 172.16.0.0/12
|
||||||
|
if (b0 == 192 && b1 == 168) return true; // 192.168.0.0/16
|
||||||
|
if (b0 == 100 && b1 >= 64 && b1 <= 127) return true;// 100.64.0.0/10 CGNAT
|
||||||
|
return b0 >= 224; // 224.0.0.0/4 组播 + 240.0.0.0/4 保留
|
||||||
|
}
|
||||||
|
// IPv6
|
||||||
|
if (addr.isAnyLocalAddress() || addr.isLoopbackAddress()
|
||||||
|
|| addr.isLinkLocalAddress() || addr.isSiteLocalAddress()
|
||||||
|
|| addr.isMulticastAddress()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return bytes.length == 16 && (bytes[0] & 0xFE) == 0xFC; // fc00::/7 unique local
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isIPv4Mapped(byte[] b) {
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
if (b[i] != 0) return false;
|
||||||
|
}
|
||||||
|
return (b[10] & 0xFF) == 0xFF && (b[11] & 0xFF) == 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
protected String baseMsg() {
|
protected String baseMsg() {
|
||||||
if (shareLinkInfo.getShareUrl() != null) {
|
if (shareLinkInfo.getShareUrl() != null) {
|
||||||
return shareLinkInfo.getPanName() + "-" + shareLinkInfo.getType() + ": url=" + shareLinkInfo.getShareUrl();
|
return shareLinkInfo.getPanName() + "-" + shareLinkInfo.getType() + ": url=" + shareLinkInfo.getShareUrl();
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ public class Ce4Tool extends PanBase {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
URL url = new URL(shareLinkInfo.getShareUrl());
|
URL url = new URL(shareLinkInfo.getShareUrl());
|
||||||
|
assertPublicHost(url);
|
||||||
String baseUrl = url.getProtocol() + "://" + url.getHost();
|
String baseUrl = url.getProtocol() + "://" + url.getHost();
|
||||||
// 如果有端口,拼接上端口
|
// 如果有端口,拼接上端口
|
||||||
if (url.getPort() != -1) {
|
if (url.getPort() != -1) {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ public class CeTool extends PanBase {
|
|||||||
String pwd = shareLinkInfo.getSharePassword();
|
String pwd = shareLinkInfo.getSharePassword();
|
||||||
try {
|
try {
|
||||||
URL url = new URL(shareLinkInfo.getShareUrl());
|
URL url = new URL(shareLinkInfo.getShareUrl());
|
||||||
|
assertPublicHost(url);
|
||||||
String baseUrl = url.getProtocol() + "://" + url.getHost();
|
String baseUrl = url.getProtocol() + "://" + url.getHost();
|
||||||
// 如果有端口,拼接上端口
|
// 如果有端口,拼接上端口
|
||||||
if (url.getPort() != -1) {
|
if (url.getPort() != -1) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>0.4.0</revision>
|
<revision>0.4.1</revision>
|
||||||
<java.version>17</java.version>
|
<java.version>17</java.version>
|
||||||
<maven.compiler.source>17</maven.compiler.source>
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
<maven.compiler.target>17</maven.compiler.target>
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<parserVersion>10.2.5</parserVersion>
|
<parserVersion>10.2.5</parserVersion>
|
||||||
<jackson.version>2.18.6</jackson.version>
|
<jackson.version>2.18.6</jackson.version>
|
||||||
<!-- Logback 最新稳定版 -->
|
<!-- Logback 最新稳定版 -->
|
||||||
<logback.version>1.5.32</logback.version>
|
<logback.version>1.5.33</logback.version>
|
||||||
<junit.version>4.13.2</junit.version>
|
<junit.version>4.13.2</junit.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "nfd-web",
|
"name": "nfd-web",
|
||||||
"version": "0.4.0",
|
"version": "0.4.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve",
|
"serve": "vue-cli-service serve",
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
"@element-plus/icons-vue": "^2.3.1",
|
"@element-plus/icons-vue": "^2.3.1",
|
||||||
"@monaco-editor/loader": "^1.4.0",
|
"@monaco-editor/loader": "^1.4.0",
|
||||||
"@vueuse/core": "^11.2.0",
|
"@vueuse/core": "^11.2.0",
|
||||||
"axios": "1.16.1",
|
"axios": "1.18.0",
|
||||||
"clipboard": "^2.0.11",
|
"clipboard": "^2.0.11",
|
||||||
"core-js": "^3.8.3",
|
"core-js": "^3.8.3",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
|
|||||||
@@ -386,6 +386,16 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "uuid123"
|
"example": "uuid123"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "stoken",
|
||||||
|
"in": "query",
|
||||||
|
"required": false,
|
||||||
|
"description": "分享 token,用于子目录解析时复用认证状态",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "OASBe5qM0pg2VvvyLM..."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
|
|||||||
@@ -68,9 +68,9 @@ public class URLParamUtil {
|
|||||||
boolean firstParam = !decodedUrl.contains("?");
|
boolean firstParam = !decodedUrl.contains("?");
|
||||||
|
|
||||||
for (String paramName : params.names()) {
|
for (String paramName : params.names()) {
|
||||||
// 忽略 "url", "pwd", "dirId", "uuid", "auth" 参数(这些参数单独处理,不应拼接到分享URL中)
|
// 忽略 "url", "pwd", "dirId", "uuid", "auth", "stoken" 参数(这些参数单独处理,不应拼接到分享URL中)
|
||||||
if (!paramName.equals("url") && !paramName.equals("pwd") && !paramName.equals("dirId")
|
if (!paramName.equals("url") && !paramName.equals("pwd") && !paramName.equals("dirId")
|
||||||
&& !paramName.equals("uuid") && !paramName.equals("auth")) {
|
&& !paramName.equals("uuid") && !paramName.equals("auth") && !paramName.equals("stoken")) {
|
||||||
if (firstParam) {
|
if (firstParam) {
|
||||||
urlBuilder.append("?");
|
urlBuilder.append("?");
|
||||||
firstParam = false;
|
firstParam = false;
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ public class ParserApi {
|
|||||||
|
|
||||||
@RouteMapping("/getFileList")
|
@RouteMapping("/getFileList")
|
||||||
public Future<List<FileInfo>> getFileList(HttpServerRequest request, String pwd, String dirId, String uuid,
|
public Future<List<FileInfo>> getFileList(HttpServerRequest request, String pwd, String dirId, String uuid,
|
||||||
String auth) {
|
String stoken, String auth) {
|
||||||
String url = URLParamUtil.parserParams(request);
|
String url = URLParamUtil.parserParams(request);
|
||||||
ParserCreate parserCreate;
|
ParserCreate parserCreate;
|
||||||
try {
|
try {
|
||||||
@@ -176,6 +176,9 @@ public class ParserApi {
|
|||||||
if (StringUtils.isNotBlank(dirId)) {
|
if (StringUtils.isNotBlank(dirId)) {
|
||||||
parserCreate.getShareLinkInfo().getOtherParam().put("dirId", dirId);
|
parserCreate.getShareLinkInfo().getOtherParam().put("dirId", dirId);
|
||||||
}
|
}
|
||||||
|
if (StringUtils.isNotBlank(stoken)) {
|
||||||
|
parserCreate.getShareLinkInfo().getOtherParam().put("stoken", stoken);
|
||||||
|
}
|
||||||
if (StringUtils.isNotBlank(uuid)) {
|
if (StringUtils.isNotBlank(uuid)) {
|
||||||
parserCreate.getShareLinkInfo().getOtherParam().put("uuid", uuid);
|
parserCreate.getShareLinkInfo().getOtherParam().put("uuid", uuid);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user