Documentation
¶
Overview ¶
Package safearchive implements bounded, fail-closed archive extraction for every remotely acquired artifact in FreeIran (protocol-core releases, provider binaries, any future archive).
v0.9.8.6 security contract — every extraction enforces:
- a maximum ARCHIVE size (the on-disk staged file is checked before a single byte is unpacked);
- a maximum TOTAL extracted byte budget (zip-bomb defence: a 100-byte archive must not be able to exhaust the disk);
- a maximum INDIVIDUAL file size;
- a maximum FILE COUNT;
- path-traversal protection (zip-slip / tar-slip: every entry must resolve strictly inside the destination);
- absolute-path rejection (POSIX "/abs" and Windows "C:\abs", "\\abs" and drive-relative forms);
- symlink / hardlink entry rejection — a remotely acquired executable archive never needs links, and following attacker-controlled link targets is unacceptable. Other non-regular entries (devices, fifos) are rejected as well: malformed/hostile archives fail CLOSED, they never extract partially and report success.
v0.9.11 hardening: the sanitizer is HOST-INDEPENDENT. Archive entry names are validated in ARCHIVE space — lexically, with '/' as the only separator — BEFORE any host path conversion. The pre-0.9.11 sanitizer ran the host's filepath.Clean() over the raw name first: on Windows that translated "/" into "\" (so a POSIX-absolute entry such as "/etc/passwd-clone" became backslash-rooted and slipped past the absolute-entry test, only to be saved — or not — by the last-resort containment check), and the whole contract silently depended on host path semantics. The sanitized result must be IDENTICAL on every platform; the final containment decision is made with an OS-aware filepath.Rel check (case-insensitive on Windows) after the lexical gate.
Supported formats: .zip, .tar, .tar.gz/.tgz, .gz (single-file gzip of one executable, as published by MetaCubeX Mihomo for Linux), with content sniffing (PK / gzip magic) for misnamed staged files — mirroring the historical engine/coremgr behaviour.
A .gz whose decompressed stream is NOT a valid tar archive is treated as a single-file gzip: the staged output file takes the archive's base name with the .gz suffix stripped (e.g. "mihomo-linux-amd64-v1.18.x.gz" → "mihomo-linux-amd64-v1.18.x"). Callers that look up the executable by walking the unpacked directory pick it up by file mode + name.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrLimit = errors.New("safearchive: archive exceeded safety limits")
ErrLimit is the fail-closed family: the archive exceeded a bound or carried a hostile entry. Extraction stopped; the destination holds a PARTIAL tree which callers must discard (the coremgr/provider pipelines unpack into staging and remove it on failure).
Functions ¶
Types ¶
type Limits ¶
type Limits struct {
// MaxArchiveBytes bounds the staged archive FILE size. The check
// runs before any entry is read.
MaxArchiveBytes int64
// MaxTotalBytes bounds the sum of all extracted file sizes.
MaxTotalBytes int64
// MaxFileBytes bounds one extracted file.
MaxFileBytes int64
// MaxFiles bounds the number of extracted entries (files and
// directories).
MaxFiles int
}
Limits bounds one extraction. Zero fields mean "no limit of this kind" — but callers handling remotely acquired archives must always set all four (DefaultLimits provides the shared safe values).
func DefaultLimits ¶
func DefaultLimits() Limits
DefaultLimits returns the shared extraction budget for FreeIran's remotely acquired core/provider archives: archives up to 512 MiB, total expansion capped at 2 GiB, single files capped at 1 GiB and at most 4096 entries. These comfortably cover every official Xray/V2Ray/sing-box/Tor/Psiphon release shape while keeping a hostile archive orders of magnitude away from exhausting disk or memory.