task-162: 路径穿越输入矩阵(../、..\、绝对路径、%2e 编码、全角点、混合分隔符、空字节全部拒绝)+ 9 条测试
This commit is contained in:
+8
-2
@@ -33,15 +33,21 @@ public final class PathSafetyGuard {
|
||||
return childPath.startsWith(rootPath) && !childPath.equals(rootPath);
|
||||
}
|
||||
|
||||
/** 路径名是否含穿越特征(../、绝对路径、盘符、反斜杠)。 */
|
||||
/**
|
||||
* 路径名是否含穿越特征(../、..\\、绝对路径、盘符、URL 编码点 %2e、
|
||||
* 全角点 .、混合分隔符、空字节)。
|
||||
*/
|
||||
public static boolean isTraversal(String name) {
|
||||
if (name == null || name.isBlank()) {
|
||||
return true;
|
||||
}
|
||||
return name.contains("..")
|
||||
|| name.contains("%2e") || name.contains("%2E")
|
||||
|| name.contains(".")
|
||||
|| name.startsWith("/")
|
||||
|| name.matches("^[A-Za-z]:.*")
|
||||
|| name.contains("\\");
|
||||
|| name.contains("\\")
|
||||
|| name.contains("\0");
|
||||
}
|
||||
|
||||
private static Path resolve(File file) {
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.nanri.aiimage.modules.file.service;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* task-162:路径穿越输入矩阵(plan 09)。
|
||||
* ../、..\\、绝对路径、URL 编码 %2e%2e、Unicode 全角点、混合分隔符、空字节
|
||||
* 全部拒绝;普通文件名放行。
|
||||
*/
|
||||
class PathTraversalMatrixTest {
|
||||
|
||||
@Test
|
||||
void dotdotSlashRejected() {
|
||||
assertTrue(PathSafetyGuard.isTraversal("../escape"));
|
||||
assertTrue(PathSafetyGuard.isTraversal("a/../../b"));
|
||||
assertTrue(PathSafetyGuard.isTraversal("..../x"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void dotdotBackslashRejected() {
|
||||
assertTrue(PathSafetyGuard.isTraversal("..\\escape"));
|
||||
assertTrue(PathSafetyGuard.isTraversal("a\\..\\b"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void urlEncodedDotdotRejected() {
|
||||
assertTrue(PathSafetyGuard.isTraversal("%2e%2e/x"));
|
||||
assertTrue(PathSafetyGuard.isTraversal("%2E%2E/x"));
|
||||
assertTrue(PathSafetyGuard.isTraversal("a%2e%2eb"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void fullwidthDotRejected() {
|
||||
assertTrue(PathSafetyGuard.isTraversal("\uFF0E\uFF0E/escape"), "全角点 .. 拒绝");
|
||||
assertTrue(PathSafetyGuard.isTraversal("a\uFF0E\uFF0Eb"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void absoluteWindowsPathRejected() {
|
||||
assertTrue(PathSafetyGuard.isTraversal("C:\\windows\\system32"));
|
||||
assertTrue(PathSafetyGuard.isTraversal("D:/escape/file"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void absoluteUnixPathRejected() {
|
||||
assertTrue(PathSafetyGuard.isTraversal("/etc/passwd"));
|
||||
assertTrue(PathSafetyGuard.isTraversal("/tmp/escape"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void mixedSeparatorsRejected() {
|
||||
assertTrue(PathSafetyGuard.isTraversal("..\\/escape"));
|
||||
assertTrue(PathSafetyGuard.isTraversal("a/..\\b/c"));
|
||||
assertTrue(PathSafetyGuard.isTraversal("C:..\\x"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullByteRejected() {
|
||||
assertTrue(PathSafetyGuard.isTraversal("evil\0.tmp"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void normalNamesAllowed() {
|
||||
assertFalse(PathSafetyGuard.isTraversal("0123456789abcdef0123456789abcdef.xlsx"));
|
||||
assertFalse(PathSafetyGuard.isTraversal("result-2026-09-02.xlsx"));
|
||||
assertFalse(PathSafetyGuard.isTraversal("chunk-1.json"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user