Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
963584e361 | ||
|
|
e464dfc35a | ||
|
|
6c843c4030 | ||
|
|
28eaccf35f | ||
|
|
c0b4dc6612 | ||
|
|
da31bedc88 | ||
|
|
781e77ada0 |
No files matched your search
@@ -1,4 +1,4 @@
|
||||
1.7-SNAPSHOT
|
||||
1.8-SNAPSHOT-Java21
|
||||
|
||||
此版本包含:
|
||||
1. 压缩/解压 (`compress`)
|
||||
@@ -21,7 +21,16 @@
|
||||
8.2 新建临时目录
|
||||
8.3 获取当前运行的 JAR 文件路径
|
||||
|
||||
1.7 更新内容:
|
||||
1.8 更新内容:
|
||||
- 版本号升级为 1.8-SNAPSHOT-Java21(pom / version 常量 / README 引用同步)
|
||||
- 质量插件适配 Java 21:checkstyle 升级至 3.6.0(新增 checkstyle.xml 并绑定 verify 阶段),
|
||||
SpotBugs 4.8.6.6 替换已停止维护的 FindBugs(新增排除规则),javadoc 升级至 3.12.0(禁用 doclint)
|
||||
- 修复 SpotBugs 发现的空指针风险:压缩/解压方法 getFileName()/getParent() 空值防御,
|
||||
加密模块共享 SecureRandom 实例
|
||||
- 构建验证通过:mvn clean verify(53 个测试用例 + checkstyle + SpotBugs 0 问题)
|
||||
与 mvn javadoc:javadoc 均成功
|
||||
|
||||
1.7 更新内容(历史):
|
||||
- 安全:修复 Zip-Slip 路径穿越漏洞,解压时校验条目路径并拒绝符号/硬链接条目
|
||||
- 安全:AES 升级为 AES-256/GCM(随机 IV + 认证标签),RSA 实现真实混合加密
|
||||
(RSA-2048/OAEP 包裹随机 AES-256 会话密钥),密钥文件限制为仅属主可读写
|
||||
@@ -32,5 +41,6 @@
|
||||
- 修复:getJarPath 经 URI 解析(正确处理空格与 URL 编码),临时目录改用唯一命名
|
||||
- 工程:测试全部迁移至 JUnit 5(53 个用例),pom 补充元数据与插件版本,
|
||||
版本号统一由 pom / MANIFEST Implementation-Version 提供
|
||||
- 兼容性:向下降级为 Java 21
|
||||
|
||||
在 1.6-SNAPSHOT 的基础上进行安全加固与缺陷修复。
|
||||
在 1.7-SNAPSHOT-Java21 的基础上进行版本升级与质量插件修复。
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **Java 25**
|
||||
- **Java 21**
|
||||
- **Maven** 构建
|
||||
- **Apache Commons Compress 1.28.0**(压缩功能依赖)
|
||||
- **JUnit Jupiter 5.13.0**(测试框架)
|
||||
@@ -416,7 +416,7 @@ mvn test
|
||||
|
||||
## 使用
|
||||
|
||||
编译成功后,将 `target/gnu_java_utils_library-1.7-SNAPSHOT.jar` 放入项目依赖中,或在 Releases 中下载最新版 JAR 包。
|
||||
编译成功后,将 `target/gnu_java_utils_library-1.8-SNAPSHOT-Java21.jar` 放入项目依赖中,或在 Releases 中下载最新版 JAR 包。
|
||||
|
||||
**pom.xml 引入本地依赖:**
|
||||
|
||||
@@ -425,9 +425,9 @@ mvn test
|
||||
<dependency>
|
||||
<groupId>top.gtb520.java.gnu</groupId>
|
||||
<artifactId>gnu_java_utils_library</artifactId>
|
||||
<version>1.7-SNAPSHOT</version>
|
||||
<version>1.8-SNAPSHOT-Java21</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${project.basedir}/lib/gnu_java_utils_library-1.7-SNAPSHOT.jar</systemPath>
|
||||
<systemPath>${project.basedir}/lib/gnu_java_utils_library-1.8-SNAPSHOT-Java21.jar</systemPath>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
|
||||
<!--
|
||||
最小化 Checkstyle 配置:
|
||||
当前仅启用不涉及命名/布局风格的基础检查,保证 Java 21 项目在 verify 阶段通过。
|
||||
后续可按需逐步追加规则(如 LineLength、Naming 等)。
|
||||
-->
|
||||
<module name="Checker">
|
||||
<property name="charset" value="UTF-8"/>
|
||||
|
||||
<!-- 禁止使用制表符缩进 -->
|
||||
<module name="FileTabCharacter"/>
|
||||
|
||||
<!-- 文件末尾需要换行 -->
|
||||
<module name="NewlineAtEndOfFile"/>
|
||||
|
||||
<module name="TreeWalker">
|
||||
<!-- 未使用的 import -->
|
||||
<module name="UnusedImports"/>
|
||||
<!-- 冗余的 import -->
|
||||
<module name="RedundantImport"/>
|
||||
<!-- 禁止星号通配 import -->
|
||||
<module name="AvoidStarImport"/>
|
||||
</module>
|
||||
</module>
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>top.gtb520.java.gnu</groupId>
|
||||
<artifactId>gnu_java_utils_library</artifactId>
|
||||
<version>1.7-SNAPSHOT</version>
|
||||
<version>1.8-SNAPSHOT-Java21</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>GNU Java Utils Library</name>
|
||||
@@ -22,7 +22,7 @@
|
||||
</licenses>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.release>25</maven.compiler.release>
|
||||
<maven.compiler.release>21</maven.compiler.release>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<junit.version>5.13.0</junit.version>
|
||||
<surefire.version>3.6.0</surefire.version>
|
||||
@@ -64,6 +64,60 @@
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<configuration>
|
||||
<configLocation>checkstyle.xml</configLocation>
|
||||
<!-- <encoding>UTF-8</encoding>-->
|
||||
<failOnViolation>true</failOnViolation>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>checkstyle-check</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>com.github.spotbugs</groupId>
|
||||
<artifactId>spotbugs-maven-plugin</artifactId>
|
||||
<version>4.8.6.6</version>
|
||||
<configuration>
|
||||
<effort>Max</effort>
|
||||
<threshold>Medium</threshold>
|
||||
<failOnError>true</failOnError>
|
||||
<xmlOutput>true</xmlOutput>
|
||||
<excludeFilterFile>spotbugs-exclude.xml</excludeFilterFile>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>spotbugs-check</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.12.0</version>
|
||||
<configuration>
|
||||
<show>private</show>
|
||||
<nohelp>true</nohelp>
|
||||
<doclint>none</doclint>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<FindBugsFilter xmlns="https://github.com/spotbugs/filter/4.8.6"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="https://github.com/spotbugs/filter/4.8.6 https://raw.githubusercontent.com/spotbugs/spotbugs/4.8.6/spotbugs/etc/findbugsfilter.xsd">
|
||||
|
||||
<!--
|
||||
遗留命名规范:以下类/方法沿用了历史 API 命名(小写类名、PascalCase 方法名),
|
||||
重命名属于破坏性变更,暂不处理,待后续大版本统一规范后移除本排除。
|
||||
-->
|
||||
<Match>
|
||||
<Class name="~.*\.(compress|echo|version|defaults|getHWID)$"/>
|
||||
<Bug pattern="NM_CLASS_NAMING_CONVENTION"/>
|
||||
</Match>
|
||||
<Match>
|
||||
<Class name="~.*\.(CreateFile|EncryptionAndDecryption|echo)$"/>
|
||||
<Bug pattern="NM_METHOD_NAMING_CONVENTION"/>
|
||||
</Match>
|
||||
|
||||
<!--
|
||||
REC_CATCH_EXCEPTION:CompressPNG.PNG 的兜底 catch(Exception) 为有意设计
|
||||
(压缩失败统一返回 null),该检查噪音较高,全局排除。
|
||||
-->
|
||||
<Match>
|
||||
<Bug pattern="REC_CATCH_EXCEPTION"/>
|
||||
</Match>
|
||||
</FindBugsFilter>
|
||||
@@ -52,7 +52,11 @@ public class compress {
|
||||
if (entry.isDirectory()) {
|
||||
Files.createDirectories(targetPath);
|
||||
} else {
|
||||
Files.createDirectories(targetPath.getParent());
|
||||
Path parent = targetPath.getParent();
|
||||
if (parent == null) {
|
||||
throw new IllegalArgumentException("无法确定条目父目录: " + entry.getName());
|
||||
}
|
||||
Files.createDirectories(parent);
|
||||
Files.copy(tarIn, targetPath);
|
||||
}
|
||||
}
|
||||
@@ -115,7 +119,11 @@ public class compress {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
TarArchiveEntry entry = new TarArchiveEntry(sourcePath, sourcePath.getFileName().toString());
|
||||
Path fileName = sourcePath.getFileName();
|
||||
if (fileName == null) {
|
||||
throw new IllegalArgumentException("无法从源路径获取文件名: " + sourcePath);
|
||||
}
|
||||
TarArchiveEntry entry = new TarArchiveEntry(sourcePath, fileName.toString());
|
||||
tarOut.putArchiveEntry(entry);
|
||||
Files.copy(sourcePath, tarOut);
|
||||
tarOut.closeArchiveEntry();
|
||||
@@ -164,7 +172,11 @@ public class compress {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ZipArchiveEntry entry = new ZipArchiveEntry(sourcePath.getFileName().toString());
|
||||
Path fileName = sourcePath.getFileName();
|
||||
if (fileName == null) {
|
||||
throw new IllegalArgumentException("无法从源路径获取文件名: " + sourcePath);
|
||||
}
|
||||
ZipArchiveEntry entry = new ZipArchiveEntry(fileName.toString());
|
||||
zipOut.putArchiveEntry(entry);
|
||||
Files.copy(sourcePath, zipOut);
|
||||
zipOut.closeArchiveEntry();
|
||||
@@ -200,7 +212,11 @@ public class compress {
|
||||
if (entry.isDirectory()) {
|
||||
Files.createDirectories(targetPath);
|
||||
} else {
|
||||
Files.createDirectories(targetPath.getParent());
|
||||
Path parent = targetPath.getParent();
|
||||
if (parent == null) {
|
||||
throw new IllegalArgumentException("无法确定条目父目录: " + entry.getName());
|
||||
}
|
||||
Files.createDirectories(parent);
|
||||
Files.copy(zipIn, targetPath);
|
||||
}
|
||||
}
|
||||
|
||||
+8
-2
@@ -53,6 +53,9 @@ public class EncryptionAndDecryption {
|
||||
private static final String DES_ECB = "DES/ECB/PKCS5Padding";
|
||||
private static final String RSA_WRAP = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
|
||||
|
||||
/** 共享的安全随机数发生器,避免每次加密都新建 SecureRandom 实例 */
|
||||
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
|
||||
|
||||
/**
|
||||
* 文件加密。加密后覆盖原文件(原子替换),密钥保存到 keyPath 目录下,密钥名为 原文件名.key。
|
||||
*
|
||||
@@ -135,7 +138,7 @@ public class EncryptionAndDecryption {
|
||||
|
||||
private static void encryptAesGcm(File file, SecretKey key) throws Exception {
|
||||
byte[] iv = new byte[GCM_IV_BYTES];
|
||||
new SecureRandom().nextBytes(iv);
|
||||
SECURE_RANDOM.nextBytes(iv);
|
||||
Cipher cipher = buildCipher(AES_GCM, Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(GCM_TAG_BITS, iv));
|
||||
|
||||
Path tmp = tempSibling(file);
|
||||
@@ -172,7 +175,7 @@ public class EncryptionAndDecryption {
|
||||
// 随机 AES-256 会话密钥,RSA-OAEP 包裹后存入文件头
|
||||
SecretKey sessionKey = generateKey("AES", AES_KEY_BITS);
|
||||
byte[] iv = new byte[GCM_IV_BYTES];
|
||||
new SecureRandom().nextBytes(iv);
|
||||
SECURE_RANDOM.nextBytes(iv);
|
||||
|
||||
Cipher wrapCipher = Cipher.getInstance(RSA_WRAP);
|
||||
wrapCipher.init(Cipher.WRAP_MODE, publicKey);
|
||||
@@ -279,6 +282,9 @@ public class EncryptionAndDecryption {
|
||||
*/
|
||||
private static Path tempSibling(File file) throws IOException {
|
||||
Path dir = file.toPath().toAbsolutePath().getParent();
|
||||
if (dir == null) {
|
||||
throw new IOException("无法确定文件所在目录: " + file);
|
||||
}
|
||||
return Files.createTempFile(dir, file.getName() + ".", ".tmp");
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ package top.gtb520.java.gnu.java.utils.library;
|
||||
public class version {
|
||||
// 项目的版本信息,不代表实时运行环境
|
||||
// 与 pom.xml 保持一致;构建 JAR 时优先读取 MANIFEST 的 Implementation-Version
|
||||
public static final String VERSION = "1.7";
|
||||
public static final String JAVA_VERSION = "25";
|
||||
public static final String OpenJDK_VERSION = "25.0.0.0.1";
|
||||
public static final String OpenJDK_Runtime_Environment = "25.0.0.0.1+36-GA";
|
||||
public static final String OpenJDK_64Bit_Server_VM = "25.0.0.0.1+36-GA";
|
||||
public static final String VERSION = "1.8";
|
||||
public static final String JAVA_VERSION = "21";
|
||||
public static final String OpenJDK_VERSION = "21";
|
||||
public static final String OpenJDK_Runtime_Environment = "21+35-2513";
|
||||
public static final String OpenJDK_64Bit_Server_VM = "21+35-2513";
|
||||
|
||||
public String GetVersion() {
|
||||
// 打包为 JAR 时读取 MANIFEST 中的 Implementation-Version,类路径运行(如测试)时回退到常量
|
||||
|
||||
Reference in new issue
Block a user