Skip to content

archive/zip: lack of 0x7075 (Unicode Path Extra Field) support #78807

Description

@ont

Go version

go version go1.26.2 linux/amd64

Output of go env in your module/workspace:

AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/root/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/root/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build1088487257=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/dev/null'
GOMODCACHE='/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/root/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='local'
GOTOOLDIR='/usr/local/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.26.2'
GOWORK=''
PKG_CONFIG='pkg-config'

What did you do?

Create bad.zip with this code:

  package main

  import (
  	"archive/zip"
  	"os"
  )

  func main() {
  	f, err := os.Create("bad.zip")
  	if err != nil {
  		panic(err)
  	}
  	defer f.Close()

  	zw := zip.NewWriter(f)
  	defer zw.Close()

  	w, err := zw.Create("uploads/Это строка в UTF8.png")
  	if err != nil {
  		panic(err)
  	}

  	_, err = w.Write([]byte("test"))
  	if err != nil {
  		panic(err)
  	}
  }

Then check output with:

  go run main.go
  unzip -Z -1 bad.zip
  unzip -l bad.zip

It will be broken.

What did you see happen?

inside docker run --rm -ti golang:1.26:

root@3938f10f21ec:/tmp# go run main.go 

root@3938f10f21ec:/tmp# unzip -Z -1 bad.zip
uploads/���� ��������� � UTF8.png

root@3938f10f21ec:/tmp# unzip -l bad.zip
Archive:  bad.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        4  1980-00-00 00:00   uploads/���� ��������� � UTF8.png
---------                     -------
        4                     1 file

What did you expect to see?

If we change program to:

  package main

  import (
  	"archive/zip"
  	"encoding/binary"
  	"hash/crc32"
  	"os"
  )

  const infoZipUnicodePathExtraID = 0x7075

  func main() {
  	f, err := os.Create("good.zip")
  	if err != nil {
  		panic(err)
  	}
  	defer f.Close()

  	zw := zip.NewWriter(f)
  	defer zw.Close()

  	name := "uploads/Это строка в UTF8.png"

  	w, err := zw.CreateHeader(&zip.FileHeader{
  		Name:   name,
  		Method: zip.Deflate,
  		Extra:  unicodePathExtraField(name),
  	})
  	if err != nil {
  		panic(err)
  	}

  	_, err = w.Write([]byte("test"))
  	if err != nil {
  		panic(err)
  	}
  }

  func unicodePathExtraField(name string) []byte {
  	nameBytes := []byte(name)
  	dataSize := uint16(1 + 4 + len(nameBytes))

  	extra := binary.LittleEndian.AppendUint16(nil, infoZipUnicodePathExtraID)
  	extra = binary.LittleEndian.AppendUint16(extra, dataSize)
  	extra = append(extra, 1) // Info-ZIP Unicode Path version.
  	extra = binary.LittleEndian.AppendUint32(extra, crc32.ChecksumIEEE(nameBytes))
  	extra = append(extra, nameBytes...)

  	return extra
  }

Then:

root@3938f10f21ec:/tmp# go run main.go 

root@3938f10f21ec:/tmp# unzip -Z -1 good.zip
uploads/Это строка в UTF8.png

root@3938f10f21ec:/tmp# unzip -l good.zip 
Archive:  good.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        4  1980-00-00 00:00   uploads/Это строка в UTF8.png
---------                     -------
        4                     1 file

And here is correct filename output into console.

So library archive/zip doesn't automatically handles unicode filenames as expected.

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions