From a13d3d1b11b5c0c4cc07f2200182e14f23e58f3a Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 13:39:52 +0000 Subject: [PATCH] Verify Decompressor Memory Limits and Ratio Added integration tests to `tests/security_limit.rs` to verify that `set_max_memory_limit` and `set_limit_ratio` correctly enforce limits on real compressed data. This ensures protection against zip bombs (highly compressed files) and excessive memory allocation requests across Deflate, Zlib, and Gzip formats. Tests include: - `test_memory_limit_with_real_data`: Confirms decompression fails when output exceeds `max_memory_limit`. - `test_ratio_limit_with_real_data`: Confirms decompression fails when expansion ratio exceeds `limit_ratio` (using strict limit on level 12 compressed data). - `test_gzip_zlib_limits`: Confirms limits apply to Gzip and Zlib streams. Co-authored-by: 404Setup <153366651+404Setup@users.noreply.github.com> --- tests/security_limit.rs | 92 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/tests/security_limit.rs b/tests/security_limit.rs index 07b2832..636290d 100644 --- a/tests/security_limit.rs +++ b/tests/security_limit.rs @@ -107,3 +107,95 @@ fn test_decompression_ratio_limit() { ); } } + +#[test] +fn test_memory_limit_with_real_data() { + let mut compressor = Compressor::new(1).unwrap(); + let mut decompressor = Decompressor::new(); + + // Create a 1MB buffer of zeros (highly compressible) + let original = vec![0u8; 1_000_000]; + let compressed = compressor.compress_deflate(&original).unwrap(); + // Compressed size should be very small (e.g. < 200 bytes) + + // Set max memory limit to 500KB (less than 1MB) + decompressor.set_max_memory_limit(500_000); + + // Try to decompress, requesting 1MB + let result = decompressor.decompress_deflate(&compressed, original.len()); + + // Expect failure due to memory limit + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + assert!(err.to_string().contains("maximum memory limit")); +} + +#[test] +fn test_ratio_limit_with_real_data() { + let mut decompressor = Decompressor::new(); + + // Create a 1MB buffer of zeros + let original = vec![0u8; 1_000_000]; + + // Use level 12 for high compression + let mut compressor = Compressor::new(12).unwrap(); + let compressed = compressor.compress_deflate(&original).unwrap(); + + // Set ratio limit to a value that definitely fails. + // With level 12, 1MB zeros compresses to ~1000 bytes. + // Limit = 1000 * 100 + 4096 = 104096. + // 1,000,000 > 104096. + decompressor.set_limit_ratio(100); + + // Try to decompress high compression + let result = decompressor.decompress_deflate(&compressed, original.len()); + + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + assert!(err.to_string().contains("safety limit")); + + // Set ratio limit back to something permissive + decompressor.set_limit_ratio(100_000); + + let result = decompressor.decompress_deflate(&compressed, original.len()); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), original); +} + +#[test] +fn test_gzip_zlib_limits() { + let mut compressor = Compressor::new(1).unwrap(); + let mut decompressor = Decompressor::new(); + + let original = vec![0u8; 1_000_000]; + + // Zlib + let compressed_zlib = compressor.compress_zlib(&original).unwrap(); + decompressor.set_max_memory_limit(500_000); + let result = decompressor.decompress_zlib(&compressed_zlib, original.len()); + assert!(result.is_err()); + assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidInput); + + // Reset limit + decompressor.set_max_memory_limit(usize::MAX); + // Set ratio limit + decompressor.set_limit_ratio(10); + let result = decompressor.decompress_zlib(&compressed_zlib, original.len()); + assert!(result.is_err()); + assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidInput); + + // Gzip + let compressed_gzip = compressor.compress_gzip(&original).unwrap(); + decompressor.set_max_memory_limit(500_000); + let result = decompressor.decompress_gzip(&compressed_gzip, original.len()); + assert!(result.is_err()); + assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidInput); + + decompressor.set_max_memory_limit(usize::MAX); + decompressor.set_limit_ratio(10); + let result = decompressor.decompress_gzip(&compressed_gzip, original.len()); + assert!(result.is_err()); + assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidInput); +}