From 34865d693805e1a85a42773c49049b457b087636 Mon Sep 17 00:00:00 2001 From: DraagrenKirneh Date: Thu, 25 May 2023 07:30:58 +0200 Subject: [PATCH] Improve Content-Disposition filename detection (#15844) --- src/Package.zig | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/src/Package.zig b/src/Package.zig index cde3f38e28..d3ac71af1a 100644 --- a/src/Package.zig +++ b/src/Package.zig @@ -526,9 +526,7 @@ fn fetchAndUnpack( // whose content-disposition header is: 'attachment; filename="-.tar.gz"' const content_disposition = req.response.headers.getFirstValue("Content-Disposition") orelse return report.fail(dep.url_tok, "Missing 'Content-Disposition' header for Content-Type=application/octet-stream", .{}); - if (mem.startsWith(u8, content_disposition, "attachment;") and - mem.endsWith(u8, content_disposition, ".tar.gz\"")) - { + if (isTarAttachment(content_disposition)) { try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.gzip); } else return report.fail(dep.url_tok, "Unsupported 'Content-Disposition' header value: '{s}' for Content-Type=application/octet-stream", .{content_disposition}); } else { @@ -766,3 +764,37 @@ fn renameTmpIntoCache( break; } } + +fn isTarAttachment(content_disposition: []const u8) bool { + const disposition_type_end = ascii.indexOfIgnoreCase(content_disposition, "attachment;") orelse return false; + + var value_start = ascii.indexOfIgnoreCasePos(content_disposition, disposition_type_end + 1, "filename") orelse return false; + value_start += "filename".len; + if (content_disposition[value_start] == '*') { + value_start += 1; + } + if (content_disposition[value_start] != '=') return false; + value_start += 1; + + var value_end = mem.indexOfPos(u8, content_disposition, value_start, ";") orelse content_disposition.len; + if (content_disposition[value_end - 1] == '\"') { + value_end -= 1; + } + return ascii.endsWithIgnoreCase(content_disposition[value_start..value_end], ".tar.gz"); +} + +test "isTarAttachment" { + try std.testing.expect(isTarAttachment("attaChment; FILENAME=\"stuff.tar.gz\"; size=42")); + try std.testing.expect(isTarAttachment("attachment; filename*=\"stuff.tar.gz\"")); + try std.testing.expect(isTarAttachment("ATTACHMENT; filename=\"stuff.tar.gz\"")); + try std.testing.expect(isTarAttachment("attachment; FileName=\"stuff.tar.gz\"")); + try std.testing.expect(isTarAttachment("attachment; FileName*=UTF-8\'\'xyz%2Fstuff.tar.gz")); + + try std.testing.expect(!isTarAttachment("attachment FileName=\"stuff.tar.gz\"")); + try std.testing.expect(!isTarAttachment("attachment; FileName=\"stuff.tar\"")); + try std.testing.expect(!isTarAttachment("attachment; FileName\"stuff.gz\"")); + try std.testing.expect(!isTarAttachment("attachment; size=42")); + try std.testing.expect(!isTarAttachment("inline; size=42")); + try std.testing.expect(!isTarAttachment("FileName=\"stuff.tar.gz\"; attachment;")); + try std.testing.expect(!isTarAttachment("FileName=\"stuff.tar.gz\";")); +}