Skip to content

Commit 0b76873

Browse files
Verify Decompressor Memory Limits and Ratio (#382)
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: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent 183fc18 commit 0b76873

1 file changed

Lines changed: 92 additions & 0 deletions

File tree

tests/security_limit.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,95 @@ fn test_decompression_ratio_limit() {
107107
);
108108
}
109109
}
110+
111+
#[test]
112+
fn test_memory_limit_with_real_data() {
113+
let mut compressor = Compressor::new(1).unwrap();
114+
let mut decompressor = Decompressor::new();
115+
116+
// Create a 1MB buffer of zeros (highly compressible)
117+
let original = vec![0u8; 1_000_000];
118+
let compressed = compressor.compress_deflate(&original).unwrap();
119+
// Compressed size should be very small (e.g. < 200 bytes)
120+
121+
// Set max memory limit to 500KB (less than 1MB)
122+
decompressor.set_max_memory_limit(500_000);
123+
124+
// Try to decompress, requesting 1MB
125+
let result = decompressor.decompress_deflate(&compressed, original.len());
126+
127+
// Expect failure due to memory limit
128+
assert!(result.is_err());
129+
let err = result.err().unwrap();
130+
assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
131+
assert!(err.to_string().contains("maximum memory limit"));
132+
}
133+
134+
#[test]
135+
fn test_ratio_limit_with_real_data() {
136+
let mut decompressor = Decompressor::new();
137+
138+
// Create a 1MB buffer of zeros
139+
let original = vec![0u8; 1_000_000];
140+
141+
// Use level 12 for high compression
142+
let mut compressor = Compressor::new(12).unwrap();
143+
let compressed = compressor.compress_deflate(&original).unwrap();
144+
145+
// Set ratio limit to a value that definitely fails.
146+
// With level 12, 1MB zeros compresses to ~1000 bytes.
147+
// Limit = 1000 * 100 + 4096 = 104096.
148+
// 1,000,000 > 104096.
149+
decompressor.set_limit_ratio(100);
150+
151+
// Try to decompress high compression
152+
let result = decompressor.decompress_deflate(&compressed, original.len());
153+
154+
assert!(result.is_err());
155+
let err = result.err().unwrap();
156+
assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
157+
assert!(err.to_string().contains("safety limit"));
158+
159+
// Set ratio limit back to something permissive
160+
decompressor.set_limit_ratio(100_000);
161+
162+
let result = decompressor.decompress_deflate(&compressed, original.len());
163+
assert!(result.is_ok());
164+
assert_eq!(result.unwrap(), original);
165+
}
166+
167+
#[test]
168+
fn test_gzip_zlib_limits() {
169+
let mut compressor = Compressor::new(1).unwrap();
170+
let mut decompressor = Decompressor::new();
171+
172+
let original = vec![0u8; 1_000_000];
173+
174+
// Zlib
175+
let compressed_zlib = compressor.compress_zlib(&original).unwrap();
176+
decompressor.set_max_memory_limit(500_000);
177+
let result = decompressor.decompress_zlib(&compressed_zlib, original.len());
178+
assert!(result.is_err());
179+
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidInput);
180+
181+
// Reset limit
182+
decompressor.set_max_memory_limit(usize::MAX);
183+
// Set ratio limit
184+
decompressor.set_limit_ratio(10);
185+
let result = decompressor.decompress_zlib(&compressed_zlib, original.len());
186+
assert!(result.is_err());
187+
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidInput);
188+
189+
// Gzip
190+
let compressed_gzip = compressor.compress_gzip(&original).unwrap();
191+
decompressor.set_max_memory_limit(500_000);
192+
let result = decompressor.decompress_gzip(&compressed_gzip, original.len());
193+
assert!(result.is_err());
194+
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidInput);
195+
196+
decompressor.set_max_memory_limit(usize::MAX);
197+
decompressor.set_limit_ratio(10);
198+
let result = decompressor.decompress_gzip(&compressed_gzip, original.len());
199+
assert!(result.is_err());
200+
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidInput);
201+
}

0 commit comments

Comments
 (0)