mirror of
https://github.com/anchore/syft.git
synced 2026-10-30 22:12:28 +01:00
`put` had no per-entry cap, unlike the legacy java path which capped every file at 2 GB through `intFile.SafeCopy`. At depth 1, one entry of an overlapping-entry zip bomb could decompress all the way to the 100 GB disk limit before anything stopped it.
- entries are now read through the same 2 GB cap (`intFile.PerFileReadLimit`, now exported). An entry over it is dropped after cap+1 bytes and the rest of the archive is still extracted
- `Resolver` gains `TruncatedReason`, so the truncation unknown says what happened ("an entry larger than 2.0 GiB was skipped", or the existing disk-limit text) instead of always blaming the limits
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
32 lines
1.2 KiB
Go
32 lines
1.2 KiB
Go
package file
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
// PerFileReadLimit is the most read from any one archive entry, guarding against decompression bombs.
|
|
const PerFileReadLimit = 2 * GB
|
|
|
|
// SafeCopy bounds the copy to guard against decompression bombs when extracting from archives.
|
|
func SafeCopy(writer io.Writer, reader io.Reader) error {
|
|
numBytes, err := io.Copy(writer, io.LimitReader(reader, PerFileReadLimit))
|
|
if numBytes >= PerFileReadLimit {
|
|
return fmt.Errorf("zip read limit hit (potential decompression bomb attack)")
|
|
}
|
|
// Propagate decompression / read errors up to the caller. io.Copy
|
|
// on the happy path returns (n, nil); the only way err is non-nil
|
|
// here is that the underlying reader surfaced a real failure
|
|
// ("flate: corrupt input before offset X" on a mangled ZIP entry,
|
|
// a network-backed reader erroring mid-stream, etc.). The previous
|
|
// implementation dropped that error and the caller stored a
|
|
// partial / empty buffer as a "successful" extract, which silently
|
|
// downgraded Java cataloger output and caused SBOM scanners to
|
|
// miss known CVEs (#4806).
|
|
if err != nil && !errors.Is(err, io.EOF) {
|
|
return fmt.Errorf("failed to read archive entry: %w", err)
|
|
}
|
|
return nil
|
|
}
|