Skip to content

Commit d2728c7

Browse files
committed
fix: escape backslash and double quote in the ASCII Content-Disposition fallback
A bitstream name containing a double quote closed the quoted-string early and produced an invalid Content-Disposition, which browsers reject. The allzip and by-handle paths in this branch already escape; this brings the single-file download path in line and adds an IT for it. This is a deliberate deviation from vanilla HttpHeadersInitializer, which still has the bug. Also fixes the continuation indent of an expected value in the same IT.
1 parent f2e04a2 commit d2728c7

2 files changed

Lines changed: 47 additions & 4 deletions

File tree

dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/HttpHeadersInitializer.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,9 @@ private String createFallbackAsciiName(String originalFilename) {
283283
}
284284
String normalized = Normalizer.normalize(originalFilename, Normalizer.Form.NFD);
285285
String withoutAccents = normalized.replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
286-
return withoutAccents.replaceAll("[^\\x00-\\x7F]", "");
286+
return withoutAccents.replaceAll("[^\\x00-\\x7F]", "")
287+
.replace("\\", "\\\\")
288+
.replace("\"", "\\\"");
287289
}
288290

289291
/**

dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -365,9 +365,8 @@ public void testBitstreamName() throws Exception {
365365
String bitstreamContent = "0123456789";
366366
String bitstreamName = "ภาษาไทย-com-acentuação.pdf";
367367
String expectedAscii = "-com-acentuacao.pdf";
368-
String expectedUtf8Encoded =
369-
"%E0%B8%A0%E0%B8%B2%E0%B8%A9%E0%B8%B2%E0%B9%84%E0%B8%97%E0%B8%A2-"
370-
+ "com-acentua%C3%A7%C3%A3o.pdf";
368+
String expectedUtf8Encoded = "%E0%B8%A0%E0%B8%B2%E0%B8%A9%E0%B8%B2%E0%B9%84%E0%B8%97%E0%B8%A2-"
369+
+ "com-acentua%C3%A7%C3%A3o.pdf";
371370

372371
try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
373372

@@ -397,6 +396,48 @@ public void testBitstreamName() throws Exception {
397396
));
398397
}
399398

399+
@Test
400+
public void testBitstreamNameWithQuote() throws Exception {
401+
402+
context.turnOffAuthorisationSystem();
403+
404+
parentCommunity = CommunityBuilder
405+
.createCommunity(context)
406+
.build();
407+
408+
Collection collection = CollectionBuilder
409+
.createCollection(context, parentCommunity)
410+
.build();
411+
412+
String bitstreamContent = "0123456789";
413+
String bitstreamName = "file \"quoted\".txt";
414+
String expectedAscii = "file \\\"quoted\\\".txt";
415+
String expectedUtf8Encoded = "file%20%22quoted%22.txt";
416+
417+
try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
418+
419+
Item item = ItemBuilder
420+
.createItem(context, collection)
421+
.build();
422+
423+
bitstream = BitstreamBuilder
424+
.createBitstream(context, item, is)
425+
.withName(bitstreamName)
426+
.build();
427+
}
428+
429+
context.restoreAuthSystemState();
430+
431+
getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content"))
432+
.andExpect(status().isOk())
433+
.andExpect(header().string(
434+
"Content-Disposition",
435+
String.format("attachment; filename=\"%s\"; filename*=UTF-8''%s",
436+
expectedAscii,
437+
expectedUtf8Encoded)
438+
));
439+
}
440+
400441
@Test
401442
public void testBitstreamNotFound() throws Exception {
402443
getClient().perform(get("/api/core/bitstreams/" + UUID.randomUUID() + "/content"))

0 commit comments

Comments
 (0)