diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/DatasetFileApplicationService.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/DatasetFileApplicationService.java index 43b81720e..9dd1ea529 100644 --- a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/DatasetFileApplicationService.java +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/DatasetFileApplicationService.java @@ -20,6 +20,7 @@ import com.datamate.datamanagement.domain.model.dataset.DatasetFile; import com.datamate.datamanagement.domain.model.dataset.DatasetFileUploadCheckInfo; import com.datamate.datamanagement.infrastructure.exception.DataManagementErrorCode; +import com.datamate.datamanagement.infrastructure.config.DataManagementProperties; import com.datamate.datamanagement.infrastructure.persistence.repository.DatasetFileRepository; import com.datamate.datamanagement.infrastructure.persistence.repository.DatasetRepository; import com.datamate.datamanagement.interfaces.converter.DatasetConverter; @@ -82,12 +83,16 @@ public class DatasetFileApplicationService { @Value("${datamate.data-management.file.duplicate:COVER}") private DuplicateMethod duplicateMethod; + private final DataManagementProperties dataManagementProperties; + @Autowired public DatasetFileApplicationService(DatasetFileRepository datasetFileRepository, - DatasetRepository datasetRepository, FileService fileService) { + DatasetRepository datasetRepository, FileService fileService, + DataManagementProperties dataManagementProperties) { this.datasetFileRepository = datasetFileRepository; this.datasetRepository = datasetRepository; this.fileService = fileService; + this.dataManagementProperties = dataManagementProperties; } /** @@ -1114,6 +1119,13 @@ private void addFile(String sourPath, String targetPath, boolean softAdd) { throw BusinessException.of(SystemErrorCode.FILE_SYSTEM_ERROR); } + // 检查源路径是否在允许的目录范围内,防止任意文件读取 + if (!isSourcePathAllowed(source)) { + log.warn("Source file path is outside allowed directories: {}", sourPath); + throw BusinessException.of(CommonErrorCode.PARAM_ERROR, + "Source file path is outside allowed directories: " + sourPath); + } + try { if (Files.exists(target) && Files.isSameFile(source, target)) { return; @@ -1165,6 +1177,32 @@ private static DatasetFile getDatasetFileForAdd(AddFilesRequest req, AddFilesReq .build(); } + /** + * 检查源文件路径是否在允许的目录范围内 + * + * @param sourcePath 规范化后的源文件路径 + * @return true 如果路径在任一允许目录内 + */ + private boolean isSourcePathAllowed(Path sourcePath) { + List allowedDirs = dataManagementProperties.getFileStorage().getAllowedSourceDirs(); + if (allowedDirs == null || allowedDirs.isEmpty()) { + log.warn("No allowed source directories configured, denying source path: {}", sourcePath); + return false; + } + + Path normalizedSource = sourcePath.normalize().toAbsolutePath(); + for (String allowedDir : allowedDirs) { + if (StringUtils.isBlank(allowedDir)) { + continue; + } + Path normalizedAllowed = Paths.get(allowedDir).normalize().toAbsolutePath(); + if (normalizedSource.startsWith(normalizedAllowed)) { + return true; + } + } + return false; + } + /** * 安全地验证并获取文件路径,防止路径遍历攻击 * diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/config/DataManagementProperties.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/config/DataManagementProperties.java index 6a91a1da5..0f9149c7f 100644 --- a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/config/DataManagementProperties.java +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/config/DataManagementProperties.java @@ -3,6 +3,9 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; +import java.util.ArrayList; +import java.util.List; + /** * 数据管理服务配置属性 */ @@ -33,6 +36,7 @@ public static class FileStorage { private String uploadDir = "./uploads"; private long maxFileSize = 10485760; // 10MB private long maxRequestSize = 52428800; // 50MB + private List allowedSourceDirs = new ArrayList<>(); public String getUploadDir() { return uploadDir; @@ -57,6 +61,14 @@ public long getMaxRequestSize() { public void setMaxRequestSize(long maxRequestSize) { this.maxRequestSize = maxRequestSize; } + + public List getAllowedSourceDirs() { + return allowedSourceDirs; + } + + public void setAllowedSourceDirs(List allowedSourceDirs) { + this.allowedSourceDirs = allowedSourceDirs; + } } public static class Cache { diff --git a/backend/services/data-management-service/src/main/resources/config/application-datamanagement.yml b/backend/services/data-management-service/src/main/resources/config/application-datamanagement.yml index df603c3d2..ba9b5021e 100644 --- a/backend/services/data-management-service/src/main/resources/config/application-datamanagement.yml +++ b/backend/services/data-management-service/src/main/resources/config/application-datamanagement.yml @@ -4,6 +4,9 @@ dataMate: upload-dir: ${FILE_UPLOAD_DIR:./uploads} max-file-size: 10485760 # 10MB max-request-size: 52428800 # 50MB + allowed-source-dirs: + - ${DATASET_BASE_PATH:/dataset} + - ${FILE_UPLOAD_DIR:./uploads} cache: ttl: 3600 max-size: 1000