Documentation
¶
Overview ¶
Package archive reads untrusted archives and writes what Extract unpacks under a root the caller supplies.
Format comes from the magic bytes, never the extension: zip, RAR 4 and 5, 7z, tar, and gzip, xz, lzma, zstd, bzip2 or lz4, each alone or wrapped around a tar. Every reader is bounded in memory and in bytes before it produces output.
Extract enforces the name, kind and size policy. Lister walks the same entries and only bounds decompression.
Index ¶
- Constants
- Variables
- func DeclaredSize(src Source, size int64) (_ int64, err error)
- func Extract(src Source, size int64, baseName string, dest *os.Root, opts Options) (err error)
- func RecoverMalformed(err *error)
- func WindowsSegment(seg string) bool
- type Entry
- type Error
- type Lister
- type ListerOptions
- type Options
- type Source
Constants ¶
const ( DefaultMaxEntryBytes int64 = 32 << 30 DefaultMaxTotalBytes int64 = 64 << 30 DefaultMaxEntries = 20000 // Below DefaultRatioFloor an archive may unpack to any size within the caps; // above it, to at most DefaultMaxRatio times its own size. Deflate tops out // near 1030:1, so real archives stay well inside. DefaultMaxRatio int64 = 1024 DefaultRatioFloor int64 = 2 << 30 )
Default caps, applied to every zero field of Options. The byte caps sit far above any real archive, so the ratio guard rather than these stops a small archive claiming them.
Variables ¶
var ( ErrUnsupportedFormat = errors.New("not a zip, RAR, 7z, tar, gzip, xz, lzma, zstd, bzip2 or lz4 archive") ErrEncrypted = errors.New("archive is encrypted") ErrTraversal = errors.New("path escapes the destination") ErrUnsafeName = errors.New("name is not allowed") ErrCaseCollision = errors.New("name collides with another entry") ErrLink = errors.New("symlinks and hardlinks are not allowed") ErrSpecialFile = errors.New("device, pipe and socket entries are not allowed") ErrEntryTooLarge = errors.New("entry exceeds the size cap") ErrArchiveTooLarge = errors.New("archive exceeds the extracted size cap") ErrTooManyEntries = errors.New("archive exceeds the entry cap") ErrRatio = errors.New("archive unpacks to far more than its size allows") ErrChecksum = errors.New("checksum mismatch") ErrMalformed = errors.New("archive is malformed") ErrDecoderMemory = errors.New("decoder memory exceeds the extraction cap") )
Reasons carried by Error. A disk-full write is reported as the underlying error, so errors.Is(err, syscall.ENOSPC) holds.
Functions ¶
func DeclaredSize ¶
DeclaredSize sums the uncompressed sizes the archive's headers declare. It is informational (an untrusted archive can lie); Extract's caps count bytes. A stream declares no size, so it is read within the default caps and one over them reports the cap.
func Extract ¶
Extract unpacks the archive src (size bytes) into dest, detecting the format from its magic bytes. dest must exist and is rooted by the caller. baseName names the single file a bare compressed stream yields. Extraction stops at the first failure and leaves what it wrote in dest, so the caller passes a temp directory and discards it on error. A zero Options field means its default.
func RecoverMalformed ¶
func RecoverMalformed(err *error)
RecoverMalformed turns a panic in a format reader, which a crafted archive can cause, into ErrMalformed for *err, so an untrusted download fails its item instead of ending the process.
func WindowsSegment ¶
WindowsSegment reports whether one path segment is a name Windows stores as written. Windows drops a trailing dot or space, reads the characters in windowsUnsafe as separators or invalid, and hands a device name to any file whose stem matches one.
Types ¶
type Error ¶
Error names the entry that failed and why. Entry is empty for a failure of the archive as a whole.
type Lister ¶
type Lister struct {
// contains filtered or unexported fields
}
Lister walks an archive's entries once, forward.
Next reports each entry in order and returns io.EOF at the end. Open gives the current entry's bytes, valid only until the next Next; a caller wanting names and sizes alone calls Next without Open and the decoder skips that entry's bytes. Beyond the 7z header check every reader runs, nothing here counts entries, checks a name against a destination or refuses a link: those are extraction's decisions.
func NewLister ¶
NewLister detects the archive's format from its magic bytes and returns a Lister positioned before the first entry. It decompresses nothing until Next.
func (*Lister) Close ¶
Close releases the decoders the walk holds. It never closes the caller's Source, and calling it twice does nothing.
func (*Lister) Entries ¶
Entries adapts the same walk to range-over-func. A size comes from the entry's header, like DeclaredSize's; a bare stream declares none, so its one entry is drained to learn it, which Next without Open would leave at -1.
type ListerOptions ¶
type ListerOptions struct {
// MaxEntryBytes bounds one entry's decompressed bytes. Zero means 256 MiB, which is
// stricter than an extraction cap: callers that list streams must raise it.
MaxEntryBytes int64
}
ListerOptions bounds what a Lister may decompress. A zero field means the default.
type Options ¶
type Options struct {
MaxEntryBytes int64
MaxTotalBytes int64
MaxEntries int
MaxRatio int64
RatioFloor int64
// Segment reports whether one path segment is a name the destination platform
// will store as written. nil means WindowsSegment.
Segment func(seg string) bool
}
Options lowers or raises the extraction caps; a zero field means its default.