security(import): reject path-traversal filenames in MassImportService.processRows #650
@@ -291,6 +291,11 @@ public class MassImportService {
|
|||||||
if (index.isBlank()) continue;
|
if (index.isBlank()) continue;
|
||||||
|
|
||||||
String filename = index.contains(".") ? index : index + ".pdf";
|
String filename = index.contains(".") ? index : index + ".pdf";
|
||||||
|
if (!isValidImportFilename(filename)) {
|
||||||
|
log.warn("Skipping import row {}: filename rejected — {}", i, filename);
|
||||||
|
skippedFiles.add(new SkippedFile(filename, "INVALID_FILENAME_PATH_TRAVERSAL"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
Optional<File> fileOnDisk = findFileRecursive(filename);
|
Optional<File> fileOnDisk = findFileRecursive(filename);
|
||||||
if (fileOnDisk.isEmpty()) {
|
if (fileOnDisk.isEmpty()) {
|
||||||
log.warn("Datei nicht gefunden, importiere nur Metadaten: {}", filename);
|
log.warn("Datei nicht gefunden, importiere nur Metadaten: {}", filename);
|
||||||
|
|||||||
@@ -494,6 +494,24 @@ class MassImportServiceTest {
|
|||||||
assertThat(result).isTrue();
|
assertThat(result).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void processRows_skipsRowAndContinues_whenFilenameIsPathTraversal() {
|
||||||
|
when(documentService.findByOriginalFilename("legitimate.pdf")).thenReturn(Optional.empty());
|
||||||
|
when(documentService.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||||
|
|
||||||
|
List<List<String>> rows = List.of(
|
||||||
|
List.of("header"),
|
||||||
|
minimalCells("../evil"), // row 1: path traversal — should be skipped
|
||||||
|
minimalCells("legitimate.pdf") // row 2: valid — should be processed
|
||||||
|
);
|
||||||
|
MassImportService.ProcessResult result = ReflectionTestUtils.invokeMethod(service, "processRows", rows);
|
||||||
|
|
||||||
|
assertThat(result.processed()).isEqualTo(1);
|
||||||
|
assertThat(result.skippedFiles())
|
||||||
|
.extracting(MassImportService.SkippedFile::reason)
|
||||||
|
.containsExactly("INVALID_FILENAME_PATH_TRAVERSAL");
|
||||||
|
}
|
||||||
|
|
||||||
// ─── importSingleDocument — non-blank optional fields ────────────────────
|
// ─── importSingleDocument — non-blank optional fields ────────────────────
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user