NOTE: Please evaluate if archive/zip is sufficient for your use case before using this library.
The gozip library can be used to add, list and extract zipped content into a
zip file or behind an executable binary. The use case for adding zip files
behind a binary is to distribute one executable that can automatically extract
required files.
go get -v -u github.com/sanderhahn/gozip/cmd/gozip
The api consist of the Zip, UnzipList and Unzip functions:
import "github.com/sanderhahn/gozip"
// zip files/directories into file.zip (file.zip can also be an executable)
err := gozip.Zip("file.zip", []string{"content.txt", ...})
// list the zip file contents
list, err := gozip.UnzipList("file.zip")
for _, f := range list {
fmt.Printf("%s\n", f)
}
// unzip the zip file into destination
err := gozip.Unzip("file.zip", "destination")The gozip.Unzip function includes path sanitization to prevent zip-slip and
path traversal attacks. It validates paths to ensure extracted files remain
within the destination directory.
Implementation Note: As of recent versions, gozip uses an internal
pathsafe package for path validation (replacing the previous external
github.com/cyphar/filepath-securejoin dependency). The internal implementation
provides defense-in-depth against common attack vectors but has inherent
limitations due to TOCTOU (time-of-check to time-of-use) race conditions.
For typical use cases where archives come from trusted sources and the extraction
environment is under user control, the current implementation provides appropriate
protection. For high-security scenarios involving untrusted archives in shared or
hostile environments, additional hardening measures may be necessary. See the
pathsafe package documentation for detailed security considerations.
The zip functions also work when the actual zip content starts behind a binary. This makes it possible to distribute a single executable that carries its own payload files and extracts them at runtime.
$ gozip
Usage of gozip:
-c create zip (arguments: zipfile [files...])
-l list zip (arguments: zipfile)
-x extract zip (arguments: zipfile [destination])
# make temporary copy of gozip
$ cp `which gozip` gozip
# add readme.md and LICENSE.txt as zip archive behind binary
$ gozip -c gozip readme.md LICENSE.txt
# list archive with the binary itself
$ ./gozip -l ./gozip
readme.md
LICENSE.txtA complete example lives in examples/selfextract/.
The key idea is that your program calls os.Executable() first and then
gozip.Unzip(self, dest) to extract files from itself:
package main
import (
"fmt"
"log"
"os"
"github.com/sanderhahn/gozip"
)
func main() {
self, err := os.Executable()
if err != nil {
log.Fatalf("cannot determine own path: %v", err)
}
if !gozip.IsZip(self) {
fmt.Println("No embedded files. Append them first:")
fmt.Printf(" gozip -c %s <files...>\n", os.Args[0])
os.Exit(1)
}
if err := gozip.Unzip(self, "extracted"); err != nil {
log.Fatal(err)
}
fmt.Println("Files extracted to ./extracted")
}Then build and pack:
go build -o myapp .
gozip -c myapp payload/ # append files to the binary
./myapp # extracts payload/ into ./extracted/A complete example lives in examples/selfextract/main.go.
Run the full demo with:
cd examples/selfextract
bash demo.shSince gozip appends a standard zip archive, you can also extract the embedded
files using only archive/zip from the Go standard library. zip.OpenReader
is a convenience wrapper around opening the file and calling zip.NewReader
with the total file size. Both ultimately scan backwards from the end of the
file to locate the zip central directory, which works even when the zip is
appended to a binary. Use zip.NewReader when you already have an io.ReaderAt
and size, or zip.OpenReader when you just have a file path.
The example below uses zip.OpenReader for simplicity, which opens the file
and constructs the reader for you.
A brief note on Zip Slip: if zip entry paths are not validated, a crafted
archive can write files outside the intended destination (for example via
../ segments), leading to arbitrary file overwrite. Learn more at
Zip Slip.
A complete example lives in examples/selfextract-stdlib/main.go.
Run the stdlib demo with:
cd examples/selfextract-stdlib
bash demo.shThe source code uses the MIT license.
Contributors: eqawasm, dixonwille
Agents Models: Copilot (GPT-5.2-Codex)