diff --git a/src/backend/commons/common-utils/src/main/java/com/tencent/bk/job/common/util/FilePathValidateUtil.java b/src/backend/commons/common-utils/src/main/java/com/tencent/bk/job/common/util/FilePathValidateUtil.java new file mode 100644 index 0000000000..bea83ad478 --- /dev/null +++ b/src/backend/commons/common-utils/src/main/java/com/tencent/bk/job/common/util/FilePathValidateUtil.java @@ -0,0 +1,80 @@ +package com.tencent.bk.job.common.util; + +import org.apache.commons.lang3.StringUtils; + +import java.util.regex.Pattern; + +/** + * 文件路径合法性校验工具类 + */ +public class FilePathValidateUtil { + // 传统DOS正则表达式 + private static final String CONVENTIONAL_DOS_PATH_REGEX = "(^[A-Za-z]:\\\\([^\\\\])(([^\\\\/:*?\"<>|])*\\\\?)*)|" + + "(^[A-Za-z]:[\\\\])"; + // Linux路径正则表达式 + private static final String LINUX_PATH_REGEX = "^/(((../)*|(./)*)|(\\.?[^.].*/{0,1}))+"; + + // 传统DOS Pattern + private static final Pattern CONVENTIONAL_DOS_PATH_PATTERN = Pattern.compile(CONVENTIONAL_DOS_PATH_REGEX); + // Linux路径Pattern + private static final Pattern LINUX_PATH_PATTERN = Pattern.compile(LINUX_PATH_REGEX); + + /** + * 验证文件系统绝对路径的合法性 + * @param path 绝对路径 + * @return boolean true合法,false非法 + */ + public static boolean validateFileSystemAbsolutePath(String path) { + if (StringUtils.isBlank(path)) { + return false; + } + if (isLinuxAbsolutePath(path)) { + return validateLinuxFileSystemAbsolutePath(path); + } else { + return validateWindowsFileSystemAbsolutePath(path); + } + } + + /** + * 判断是否Linux绝对路径 + * + * @param path 文件路径 + * @return boolean + */ + private static boolean isLinuxAbsolutePath(String path) { + if (path.startsWith("/")) { + return true; + } + return false; + } + + /** + * 1 传统DOS路径 + * 标准的DOS路径可由以下三部分组成: + * 1)卷号或驱动器号,后跟卷分隔符(:)。 + * 2)目录名称。目录分隔符用来分隔嵌套目录层次结构中的子目录。 + * 3)文件名。目录分隔符用来分隔文件路径和文件名。 + * @param path + * @return boolean + */ + private static boolean validateWindowsFileSystemAbsolutePath(String path) { + // 传统DOS + if (CONVENTIONAL_DOS_PATH_PATTERN.matcher(path).matches()) { + return true; + } + return false; + } + + /** + * 文件或目录名,除了/以外,所有的字符都合法 + * + * @param path + * @return boolean + */ + private static boolean validateLinuxFileSystemAbsolutePath(String path) { + if (LINUX_PATH_PATTERN.matcher(path).matches()) { + return true; + } + return false; + } +} diff --git a/src/backend/commons/common-utils/src/test/java/com/tencent/bk/job/common/util/FilePathValidateUtilTest.java b/src/backend/commons/common-utils/src/test/java/com/tencent/bk/job/common/util/FilePathValidateUtilTest.java new file mode 100644 index 0000000000..d8dae63f9e --- /dev/null +++ b/src/backend/commons/common-utils/src/test/java/com/tencent/bk/job/common/util/FilePathValidateUtilTest.java @@ -0,0 +1,36 @@ +package com.tencent.bk.job.common.util; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +public class FilePathValidateUtilTest { + @Test + void testFileSystemAbsolutePath(){ + // 传统DOS路径 + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath("C:\\Documents\\abc.txt")).isTrue(); + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath("c:\\Documents\\abc.txt")).isTrue(); + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath("C:\\Documents\\嘉 abc.txt")).isTrue(); + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath(":\\abc.txt")).isFalse(); + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath("C:")).isFalse(); + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath("C:\\\\")).isFalse(); + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath("C:\\")).isTrue(); + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath("C:\\logs\\..\\access.log")).isTrue(); + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath("C:\\.config\\conf")).isTrue(); + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath("C:\\user\\abc>a")).isFalse(); + + // linux路径 + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath("/data/test_2022-04-12.apk")).isTrue(); + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath("/data/test_2022 04 12.apk")).isTrue(); + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath("/")).isTrue(); + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath("/tmp/")).isTrue(); + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath("/tmp/.conf/abc")).isTrue(); + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath("/tmp/test/../test.log")).isTrue(); + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath("data/test_2022-04-12.apk")).isFalse(); + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath("///")).isTrue(); // 根目录 + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath("/tmp////")).isTrue(); // /tmp/ + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath("/tmp//test/")).isTrue();// /tmp/test/ + assertThat(FilePathValidateUtil.validateFileSystemAbsolutePath("///")).isTrue(); + } + +} diff --git a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/api/esb/v2/impl/EsbFastPushFileResourceImpl.java b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/api/esb/v2/impl/EsbFastPushFileResourceImpl.java index f3a82c8d2d..cb3697d0f3 100644 --- a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/api/esb/v2/impl/EsbFastPushFileResourceImpl.java +++ b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/api/esb/v2/impl/EsbFastPushFileResourceImpl.java @@ -38,6 +38,7 @@ import com.tencent.bk.job.common.model.ValidateResult; import com.tencent.bk.job.common.service.AppScopeMappingService; import com.tencent.bk.job.common.util.ArrayUtil; +import com.tencent.bk.job.common.util.FilePathValidateUtil; import com.tencent.bk.job.common.util.date.DateUtils; import com.tencent.bk.job.common.web.metrics.CustomTimed; import com.tencent.bk.job.execute.api.esb.v2.EsbFastPushFileResource; @@ -67,8 +68,6 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; @RestController @Slf4j @@ -126,7 +125,7 @@ public EsbResp fastPushFile(EsbFastPushFileRequest request) { } private ValidateResult checkFastPushFileRequest(EsbFastPushFileRequest request) { - if (!validateFileSystemPath(request.getTargetPath())) { + if (!FilePathValidateUtil.validateFileSystemAbsolutePath(request.getTargetPath())) { log.warn("Fast transfer file, target path is invalid!path={}", request.getTargetPath()); return ValidateResult.fail(ErrorCode.MISSING_OR_ILLEGAL_PARAM_WITH_PARAM_NAME, "file_target_path"); } @@ -164,7 +163,7 @@ private ValidateResult validateFileSource(EsbFastPushFileRequest request) { return ValidateResult.fail(ErrorCode.MISSING_PARAM_WITH_PARAM_NAME, "file_source.files"); } for (String file : files) { - if (!validateFileSystemPath(file)) { + if (!FilePathValidateUtil.validateFileSystemAbsolutePath(file)) { log.warn("Invalid path:{}", file); return ValidateResult.fail(ErrorCode.ILLEGAL_PARAM_WITH_PARAM_NAME, "file_source.files"); } @@ -185,32 +184,6 @@ private ValidateResult validateFileSource(EsbFastPushFileRequest request) { return ValidateResult.pass(); } - private boolean validateFileSystemPath(String path) { - if (StringUtils.isBlank(path)) { - return false; - } - if (path.indexOf(' ') != -1) { - return false; - } - Pattern p1 = Pattern.compile("(//|\\\\)+"); - Matcher m1 = p1.matcher(path); - if (m1.matches()) { - return false; - } - - Pattern p2 = Pattern.compile("^[a-zA-Z]:(/|\\\\).*");//windows - Matcher m2 = p2.matcher(path); - - if (!m2.matches()) { //非windows - if (path.charAt(0) == '/') { - return !path.contains("\\\\"); - } else { - return false; - } - } - return true; - } - private TaskInstanceDTO buildFastFileTaskInstance(EsbFastPushFileRequest request) { TaskInstanceDTO taskInstance = new TaskInstanceDTO(); taskInstance.setType(TaskTypeEnum.FILE.getValue()); diff --git a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/api/esb/v3/EsbFastTransferFileV3ResourceImpl.java b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/api/esb/v3/EsbFastTransferFileV3ResourceImpl.java index d472a7b898..f3d00ab608 100644 --- a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/api/esb/v3/EsbFastTransferFileV3ResourceImpl.java +++ b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/api/esb/v3/EsbFastTransferFileV3ResourceImpl.java @@ -40,6 +40,7 @@ import com.tencent.bk.job.common.model.InternalResponse; import com.tencent.bk.job.common.model.ValidateResult; import com.tencent.bk.job.common.service.AppScopeMappingService; +import com.tencent.bk.job.common.util.FilePathValidateUtil; import com.tencent.bk.job.common.util.date.DateUtils; import com.tencent.bk.job.common.web.metrics.CustomTimed; import com.tencent.bk.job.execute.client.FileSourceResourceClient; @@ -68,8 +69,6 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; @RestController @Slf4j @@ -155,7 +154,7 @@ private ValidateResult checkFileSource(EsbFileSourceV3DTO fileSource) { for (String file : files) { if ((fileType == null || TaskFileTypeEnum.SERVER.getType() == fileType) - && !validateFileSystemPath(file)) { + && !FilePathValidateUtil.validateFileSystemAbsolutePath(file)) { log.warn("Invalid path:{}", file); return ValidateResult.fail(ErrorCode.ILLEGAL_PARAM_WITH_PARAM_NAME, "file_source.file_list"); } @@ -189,7 +188,7 @@ private ValidateResult checkFileSource(EsbFileSourceV3DTO fileSource) { } private ValidateResult checkFastTransferFileRequest(EsbFastTransferFileV3Request request) { - if (!validateFileSystemPath(request.getTargetPath())) { + if (!FilePathValidateUtil.validateFileSystemAbsolutePath(request.getTargetPath())) { log.warn("Fast transfer file, target path is invalid!path={}", request.getTargetPath()); return ValidateResult.fail(ErrorCode.MISSING_OR_ILLEGAL_PARAM_WITH_PARAM_NAME, "file_target_path"); } @@ -216,32 +215,6 @@ private ValidateResult checkFastTransferFileRequest(EsbFastTransferFileV3Request return ValidateResult.pass(); } - private boolean validateFileSystemPath(String path) { - if (StringUtils.isBlank(path)) { - return false; - } - if (path.indexOf(' ') != -1) { - return false; - } - Pattern p1 = Pattern.compile("(//|\\\\)+"); - Matcher m1 = p1.matcher(path); - if (m1.matches()) { - return false; - } - - Pattern p2 = Pattern.compile("^[a-zA-Z]:(/|\\\\).*");//windows - Matcher m2 = p2.matcher(path); - - if (!m2.matches()) { //非windows - if (path.charAt(0) == '/') { - return !path.contains("\\\\"); - } else { - return false; - } - } - return true; - } - private TaskInstanceDTO buildFastFileTaskInstance(EsbFastTransferFileV3Request request) { TaskInstanceDTO taskInstance = new TaskInstanceDTO(); taskInstance.setType(TaskTypeEnum.FILE.getValue());