authorgravatar for h_n91@hotmail.comDraagrenKirneh <h_n91@hotmail.com> 2023-05-25 07:30:58+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-05-24 22:30:58-07:00
log34865d693805e1a85a42773c49049b457b087636
treea4c16ff25e1d66ace9df338b1650c28e4a2f7bdf
parent5744ceedb8ea4b3e5906175033f634b17287f3ca
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Improve Content-Disposition filename detection (#15844)


1 files changed, 35 insertions(+), 3 deletions(-)

src/Package.zig+35-3
...@@ -526,9 +526,7 @@ fn fetchAndUnpack(...@@ -526,9 +526,7 @@ fn fetchAndUnpack(
526 // whose content-disposition header is: 'attachment; filename="<project>-<sha>.tar.gz"'526 // whose content-disposition header is: 'attachment; filename="<project>-<sha>.tar.gz"'
527 const content_disposition = req.response.headers.getFirstValue("Content-Disposition") orelse527 const content_disposition = req.response.headers.getFirstValue("Content-Disposition") orelse
528 return report.fail(dep.url_tok, "Missing 'Content-Disposition' header for Content-Type=application/octet-stream", .{});528 return report.fail(dep.url_tok, "Missing 'Content-Disposition' header for Content-Type=application/octet-stream", .{});
529 if (mem.startsWith(u8, content_disposition, "attachment;") and529 if (isTarAttachment(content_disposition)) {
530 mem.endsWith(u8, content_disposition, ".tar.gz\""))
531 {
532 try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.gzip);530 try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.gzip);
533 } else return report.fail(dep.url_tok, "Unsupported 'Content-Disposition' header value: '{s}' for Content-Type=application/octet-stream", .{content_disposition});531 } else return report.fail(dep.url_tok, "Unsupported 'Content-Disposition' header value: '{s}' for Content-Type=application/octet-stream", .{content_disposition});
534 } else {532 } else {
...@@ -766,3 +764,37 @@ fn renameTmpIntoCache(...@@ -766,3 +764,37 @@ fn renameTmpIntoCache(
766 break;764 break;
767 }765 }
768}766}
767
768fn isTarAttachment(content_disposition: []const u8) bool {
769 const disposition_type_end = ascii.indexOfIgnoreCase(content_disposition, "attachment;") orelse return false;
770
771 var value_start = ascii.indexOfIgnoreCasePos(content_disposition, disposition_type_end + 1, "filename") orelse return false;
772 value_start += "filename".len;
773 if (content_disposition[value_start] == '*') {
774 value_start += 1;
775 }
776 if (content_disposition[value_start] != '=') return false;
777 value_start += 1;
778
779 var value_end = mem.indexOfPos(u8, content_disposition, value_start, ";") orelse content_disposition.len;
780 if (content_disposition[value_end - 1] == '\"') {
781 value_end -= 1;
782 }
783 return ascii.endsWithIgnoreCase(content_disposition[value_start..value_end], ".tar.gz");
784}
785
786test "isTarAttachment" {
787 try std.testing.expect(isTarAttachment("attaChment; FILENAME=\"stuff.tar.gz\"; size=42"));
788 try std.testing.expect(isTarAttachment("attachment; filename*=\"stuff.tar.gz\""));
789 try std.testing.expect(isTarAttachment("ATTACHMENT; filename=\"stuff.tar.gz\""));
790 try std.testing.expect(isTarAttachment("attachment; FileName=\"stuff.tar.gz\""));
791 try std.testing.expect(isTarAttachment("attachment; FileName*=UTF-8\'\'xyz%2Fstuff.tar.gz"));
792
793 try std.testing.expect(!isTarAttachment("attachment FileName=\"stuff.tar.gz\""));
794 try std.testing.expect(!isTarAttachment("attachment; FileName=\"stuff.tar\""));
795 try std.testing.expect(!isTarAttachment("attachment; FileName\"stuff.gz\""));
796 try std.testing.expect(!isTarAttachment("attachment; size=42"));
797 try std.testing.expect(!isTarAttachment("inline; size=42"));
798 try std.testing.expect(!isTarAttachment("FileName=\"stuff.tar.gz\"; attachment;"));
799 try std.testing.expect(!isTarAttachment("FileName=\"stuff.tar.gz\";"));
800}