Preflight ZIP central directory metadata (T-612)
This commit is contained in:
@@ -21,7 +21,12 @@ func TestExtractorExtractsPayloadOnly(t *testing.T) {
|
||||
destination := filepath.Join(t.TempDir(), "staging")
|
||||
extractor := mustExtractor(t, testLimits())
|
||||
|
||||
result, err := extractor.ExtractFile(archivePath, destination, "bin/App.exe")
|
||||
result, err := extractor.ExtractFile(
|
||||
archivePath,
|
||||
destination,
|
||||
"bin/App.exe",
|
||||
archiveSize(t, archivePath),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("ExtractFile() error = %v", err)
|
||||
}
|
||||
@@ -216,9 +221,11 @@ func TestExtractorRejectsAttackArchives(t *testing.T) {
|
||||
testZIPEntry{name: "payload/extra.txt", body: []byte("x")}),
|
||||
entrypoint: "App.exe",
|
||||
limits: Limits{
|
||||
MaxEntries: 2,
|
||||
MaxUncompressedBytes: 1024,
|
||||
MaxCompressionRatio: 100,
|
||||
MaxEntries: 2,
|
||||
MaxArchiveBytes: 16 * 1024,
|
||||
MaxCentralDirectoryBytes: 1024,
|
||||
MaxUncompressedBytes: 1024,
|
||||
MaxCompressionRatio: 100,
|
||||
},
|
||||
wantErr: ErrTooManyEntries,
|
||||
},
|
||||
@@ -230,9 +237,11 @@ func TestExtractorRejectsAttackArchives(t *testing.T) {
|
||||
},
|
||||
entrypoint: "App.exe",
|
||||
limits: Limits{
|
||||
MaxEntries: 10,
|
||||
MaxUncompressedBytes: 8,
|
||||
MaxCompressionRatio: 100,
|
||||
MaxEntries: 10,
|
||||
MaxArchiveBytes: 16 * 1024,
|
||||
MaxCentralDirectoryBytes: 1024,
|
||||
MaxUncompressedBytes: 8,
|
||||
MaxCompressionRatio: 100,
|
||||
},
|
||||
wantErr: ErrExpandedTooLarge,
|
||||
},
|
||||
@@ -248,9 +257,11 @@ func TestExtractorRejectsAttackArchives(t *testing.T) {
|
||||
},
|
||||
entrypoint: "App.exe",
|
||||
limits: Limits{
|
||||
MaxEntries: 10,
|
||||
MaxUncompressedBytes: 8192,
|
||||
MaxCompressionRatio: 2,
|
||||
MaxEntries: 10,
|
||||
MaxArchiveBytes: 16 * 1024,
|
||||
MaxCentralDirectoryBytes: 1024,
|
||||
MaxUncompressedBytes: 8192,
|
||||
MaxCompressionRatio: 2,
|
||||
},
|
||||
wantErr: ErrCompressionRatio,
|
||||
},
|
||||
@@ -263,7 +274,12 @@ func TestExtractorRejectsAttackArchives(t *testing.T) {
|
||||
destination := filepath.Join(root, "staging")
|
||||
extractor := mustExtractor(t, test.limits)
|
||||
|
||||
_, err := extractor.ExtractFile(archivePath, destination, test.entrypoint)
|
||||
_, err := extractor.ExtractFile(
|
||||
archivePath,
|
||||
destination,
|
||||
test.entrypoint,
|
||||
archiveSize(t, archivePath),
|
||||
)
|
||||
if !errors.Is(err, test.wantErr) {
|
||||
t.Fatalf("ExtractFile() error = %v, want %v", err, test.wantErr)
|
||||
}
|
||||
@@ -303,7 +319,12 @@ func TestExtractorRejectsInvalidEntrypoints(t *testing.T) {
|
||||
t.Run(test.entrypoint, func(t *testing.T) {
|
||||
destination := filepath.Join(t.TempDir(), "staging")
|
||||
extractor := mustExtractor(t, testLimits())
|
||||
_, err := extractor.ExtractFile(archivePath, destination, test.entrypoint)
|
||||
_, err := extractor.ExtractFile(
|
||||
archivePath,
|
||||
destination,
|
||||
test.entrypoint,
|
||||
archiveSize(t, archivePath),
|
||||
)
|
||||
if !errors.Is(err, test.wantErr) {
|
||||
t.Fatalf("ExtractFile() error = %v, want %v", err, test.wantErr)
|
||||
}
|
||||
@@ -323,7 +344,12 @@ func TestExtractorAcceptsUnicodeNestedPaths(t *testing.T) {
|
||||
destination := filepath.Join(t.TempDir(), "staging")
|
||||
extractor := mustExtractor(t, testLimits())
|
||||
|
||||
result, err := extractor.ExtractFile(archivePath, destination, "工具/解析器.exe")
|
||||
result, err := extractor.ExtractFile(
|
||||
archivePath,
|
||||
destination,
|
||||
"工具/解析器.exe",
|
||||
archiveSize(t, archivePath),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("ExtractFile() error = %v", err)
|
||||
}
|
||||
@@ -346,7 +372,12 @@ func TestExtractorRejectsExistingDestination(t *testing.T) {
|
||||
}
|
||||
extractor := mustExtractor(t, testLimits())
|
||||
|
||||
_, err := extractor.ExtractFile(archivePath, destination, "App.exe")
|
||||
_, err := extractor.ExtractFile(
|
||||
archivePath,
|
||||
destination,
|
||||
"App.exe",
|
||||
archiveSize(t, archivePath),
|
||||
)
|
||||
if !errors.Is(err, ErrDestinationExists) {
|
||||
t.Fatalf("ExtractFile() error = %v, want %v", err, ErrDestinationExists)
|
||||
}
|
||||
@@ -361,7 +392,12 @@ func TestExtractorRemovesDestinationAfterCopyFailure(t *testing.T) {
|
||||
destination := filepath.Join(t.TempDir(), "staging")
|
||||
extractor := mustExtractor(t, testLimits())
|
||||
|
||||
_, err := extractor.ExtractFile(archivePath, destination, "App.exe")
|
||||
_, err := extractor.ExtractFile(
|
||||
archivePath,
|
||||
destination,
|
||||
"App.exe",
|
||||
archiveSize(t, archivePath),
|
||||
)
|
||||
if !errors.Is(err, ErrArchiveCorrupt) {
|
||||
t.Fatalf("ExtractFile() error = %v, want %v", err, ErrArchiveCorrupt)
|
||||
}
|
||||
@@ -380,12 +416,23 @@ type testZIPEntry struct {
|
||||
|
||||
func testLimits() Limits {
|
||||
return Limits{
|
||||
MaxEntries: 20,
|
||||
MaxUncompressedBytes: 16 * 1024,
|
||||
MaxCompressionRatio: 100,
|
||||
MaxEntries: 20,
|
||||
MaxArchiveBytes: 64 * 1024,
|
||||
MaxCentralDirectoryBytes: 1024,
|
||||
MaxUncompressedBytes: 16 * 1024,
|
||||
MaxCompressionRatio: 100,
|
||||
}
|
||||
}
|
||||
|
||||
func archiveSize(t *testing.T, archivePath string) int64 {
|
||||
t.Helper()
|
||||
info, err := os.Stat(archivePath)
|
||||
if err != nil {
|
||||
t.Fatalf("stat archive: %v", err)
|
||||
}
|
||||
return info.Size()
|
||||
}
|
||||
|
||||
func mustExtractor(t *testing.T, limits Limits) Extractor {
|
||||
t.Helper()
|
||||
extractor, err := NewExtractor(limits)
|
||||
@@ -483,11 +530,14 @@ func TestDefaultLimitsAreValid(t *testing.T) {
|
||||
|
||||
func TestExtractorRejectsInvalidLimits(t *testing.T) {
|
||||
tests := []Limits{
|
||||
{MaxEntries: 0, MaxUncompressedBytes: 1, MaxCompressionRatio: 1},
|
||||
{MaxEntries: 1, MaxUncompressedBytes: 0, MaxCompressionRatio: 1},
|
||||
{MaxEntries: 1, MaxUncompressedBytes: 1, MaxCompressionRatio: 0},
|
||||
{MaxEntries: 1, MaxUncompressedBytes: 1, MaxCompressionRatio: math.NaN()},
|
||||
{MaxEntries: 1, MaxUncompressedBytes: 1, MaxCompressionRatio: math.Inf(1)},
|
||||
{MaxEntries: 0, MaxArchiveBytes: 1, MaxCentralDirectoryBytes: 1, MaxUncompressedBytes: 1, MaxCompressionRatio: 1},
|
||||
{MaxEntries: 1, MaxArchiveBytes: 0, MaxCentralDirectoryBytes: 1, MaxUncompressedBytes: 1, MaxCompressionRatio: 1},
|
||||
{MaxEntries: 1, MaxArchiveBytes: 1, MaxCentralDirectoryBytes: 0, MaxUncompressedBytes: 1, MaxCompressionRatio: 1},
|
||||
{MaxEntries: 1, MaxArchiveBytes: 1, MaxCentralDirectoryBytes: 2, MaxUncompressedBytes: 1, MaxCompressionRatio: 1},
|
||||
{MaxEntries: 1, MaxArchiveBytes: 1, MaxCentralDirectoryBytes: 1, MaxUncompressedBytes: 0, MaxCompressionRatio: 1},
|
||||
{MaxEntries: 1, MaxArchiveBytes: 1, MaxCentralDirectoryBytes: 1, MaxUncompressedBytes: 1, MaxCompressionRatio: 0},
|
||||
{MaxEntries: 1, MaxArchiveBytes: 1, MaxCentralDirectoryBytes: 1, MaxUncompressedBytes: 1, MaxCompressionRatio: math.NaN()},
|
||||
{MaxEntries: 1, MaxArchiveBytes: 1, MaxCentralDirectoryBytes: 1, MaxUncompressedBytes: 1, MaxCompressionRatio: math.Inf(1)},
|
||||
}
|
||||
|
||||
for index, limits := range tests {
|
||||
|
||||
Reference in New Issue
Block a user