From 1f559223ffe4f3640157bef1d35924e004ca21b2 Mon Sep 17 00:00:00 2001 From: Kelvin Date: Thu, 22 Oct 2026 12:26:13 +0530 Subject: [PATCH 1/7] fix: harden plugin unzip against symlinks, zip bombs and FD leaks [#85] Addresses code scanning alert #47 (go/zipslip, CWE-22). The zip-slip guard in unzip() already stops "../" entries from escaping dest. Close the remaining gaps in the same extraction path: - Create directory entries with MkdirAll instead of failing on os.Create. - Reject non-regular entries (symlinks, devices); a symlink inside dest could redirect a later entry's write outside it (CWE-59). - Move per-entry extraction into extractZipFile so src/dst files close each iteration instead of piling up deferred closes until return, and surface the dst Close error (CWE-775). - Cap each entry at 100 MiB via io.LimitReader to stop zip-bomb disk exhaustion (CWE-409). - Use 0755/0644 instead of os.ModePerm for created paths (CWE-732). Tests cover a directory entry in the success case and symlink rejection in the failure cases. Refs #85 --- pkg/plugin/manager.go | 60 ++++++++++++++++++++++++++------------ pkg/plugin/manager_test.go | 43 +++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 19 deletions(-) diff --git a/pkg/plugin/manager.go b/pkg/plugin/manager.go index 6c446b696..475e91b38 100644 --- a/pkg/plugin/manager.go +++ b/pkg/plugin/manager.go @@ -760,7 +760,6 @@ func unzip(src, dest string) error { } for _, f := range r.File { - fpath := filepath.Join(dest, f.Name) // Reject a zip entry whose name climbs out of dest (zip slip): // f.Name is attacker-controlled, and filepath.Join alone does not @@ -768,30 +767,53 @@ func unzip(src, dest string) error { if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) { return fmt.Errorf("illegal file path in zip: %s", f.Name) } - // Ensure directory exists - log.Debugf(context.Background(), "Pain : fpath: %s,filepath.Dir(fpath): %s", fpath, filepath.Dir(fpath)) - if err := os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil { - return err + if f.FileInfo().IsDir() { + if err := os.MkdirAll(fpath, 0755); err != nil { + return err + } + continue } - // Open the file inside the zip - srcFile, err := f.Open() - if err != nil { - return err + // Only regular files are extracted; a symlink entry could point + // outside dest and a later entry would write through it. + if !f.Mode().IsRegular() { + return fmt.Errorf("unsupported file type in zip: %s", f.Name) } - defer srcFile.Close() - - // Create the destination file - dstFile, err := os.Create(fpath) - if err != nil { + if err := extractZipFile(f, fpath); err != nil { return err } - defer dstFile.Close() + } - // Copy file contents - if _, err := io.Copy(dstFile, srcFile); err != nil { - return err - } + return nil +} + +// maxUnzipFileSize caps one extracted entry so a zip bomb cannot fill the disk. +const maxUnzipFileSize = 100 << 20 + +// extractZipFile writes one zip entry to fpath, closing both files before it +// returns so descriptors are not held until the whole archive is extracted. +func extractZipFile(f *zip.File, fpath string) error { + if err := os.MkdirAll(filepath.Dir(fpath), 0755); err != nil { + return err } + srcFile, err := f.Open() + if err != nil { + return err + } + defer srcFile.Close() + dstFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return err + } + n, err := io.Copy(dstFile, io.LimitReader(srcFile, maxUnzipFileSize+1)) + if cerr := dstFile.Close(); err == nil { + err = cerr + } + if err != nil { + return err + } + if n > maxUnzipFileSize { + return fmt.Errorf("zip entry %s exceeds %d bytes", f.Name, maxUnzipFileSize) + } return nil } diff --git a/pkg/plugin/manager_test.go b/pkg/plugin/manager_test.go index c56fdd278..d7e98657f 100644 --- a/pkg/plugin/manager_test.go +++ b/pkg/plugin/manager_test.go @@ -1705,6 +1705,11 @@ func TestUnzipSuccess(t *testing.T) { } } + // An explicit directory entry, as zip tools emit for folders. + if _, err := zipWriter.Create("emptydir/"); err != nil { + t.Fatalf("Failed to create dir in zip: %v", err) + } + zipWriter.Close() zipFile.Close() @@ -1731,6 +1736,10 @@ func TestUnzipSuccess(t *testing.T) { t.Fatalf("Extracted file %s content = %v, want %v", path, string(content), expectedContent) } } + + if info, err := os.Stat(filepath.Join(dest, "emptydir")); err != nil || !info.IsDir() { + t.Fatalf("Extracted emptydir is not a directory: %v", err) + } }, }, } @@ -1914,6 +1923,40 @@ func TestUnzipFailure(t *testing.T) { }, expectedError: "illegal file path in zip", }, + { + name: "symlink entry rejected", + setupFunc: func() (string, string, func()) { + tempDir, err := os.MkdirTemp("", "unzip-test-*") + if err != nil { + t.Fatalf("Failed to create temp dir: %v", err) + } + + zipPath := filepath.Join(tempDir, "link.zip") + zipFile, err := os.Create(zipPath) + if err != nil { + t.Fatalf("Failed to create zip file: %v", err) + } + + zipWriter := zip.NewWriter(zipFile) + hdr := &zip.FileHeader{Name: "link"} + hdr.SetMode(os.ModeSymlink | 0777) + linkFile, err := zipWriter.CreateHeader(hdr) + if err != nil { + t.Fatalf("Failed to create symlink in zip: %v", err) + } + if _, err := linkFile.Write([]byte("/etc")); err != nil { + t.Fatalf("Failed to write symlink target: %v", err) + } + + zipWriter.Close() + zipFile.Close() + + return zipPath, filepath.Join(tempDir, "extracted"), func() { + os.RemoveAll(tempDir) + } + }, + expectedError: "unsupported file type in zip", + }, } for _, tt := range tests { From dd39ce04aa4e4fdc72297166a34b93f8ad2f1a88 Mon Sep 17 00:00:00 2001 From: Kelvin Date: Thu, 22 Oct 2026 12:27:09 +0530 Subject: [PATCH 2/7] fix: bump kin-openapi to v0.149.0 to clear Trivy CVEs [#85] github.com/getkin/kin-openapi v0.144.0 -> v0.149.0 (used by schemav2validator). Clears code scanning alerts #78-#84: - CVE-2026-100351 (HIGH) auth bypass via URL path decoding discrepancy - CVE-2026-100349 (HIGH) DoS via nil pointer deref in response validation - CVE-2026-100352 (HIGH) DoS via non-finite number handling - CVE-2026-100353 (HIGH) DoS via nil pointer deref in request validation - CVE-2026-100354 (HIGH) DoS via unbounded schema recursion - CVE-2026-100347 (MEDIUM) DoS via unsupported HTTP methods on static routes - CVE-2026-100348 (MEDIUM) fixed in 0.145.0 Transitive: santhosh-tekuri/jsonschema/v6 v6.0.2 -> v6.0.3. Refs #85 --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index f7a62ba6d..38cfcbae9 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/beckn-one/beckn-onix go 1.26.9 require ( - github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 + github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 golang.org/x/crypto v0.57.0 ) @@ -93,7 +93,7 @@ require ( github.com/alicebob/miniredis/v2 v2.37.0 github.com/beckn/catalog-core v0.2.0 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 - github.com/getkin/kin-openapi v0.144.0 + github.com/getkin/kin-openapi v0.149.0 github.com/google/uuid v1.6.0 github.com/hashicorp/go-retryablehttp v0.7.8 github.com/hashicorp/vault/api v1.23.0 diff --git a/go.sum b/go.sum index c9ec23f18..e1f29717a 100644 --- a/go.sum +++ b/go.sum @@ -44,8 +44,8 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8 github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/foxcpp/go-mockdns v1.2.0 h1:omK3OrHRD1IWJz1FuFBCFquhXslXoF17OvBS6JPzZF0= github.com/foxcpp/go-mockdns v1.2.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= -github.com/getkin/kin-openapi v0.144.0 h1:hIRcTH+KjLfkLpYU6bSSfdFpi0fZi1fp+hSPi4aQu9Y= -github.com/getkin/kin-openapi v0.144.0/go.mod h1:3BH9M9XDe/y9M5DSvEocVYAYq1w0qrhJHjC/vZi0AaY= +github.com/getkin/kin-openapi v0.149.0 h1:ZbhmVJ4yq5RZDUsyP8lcBcGMsjsaTqXEFt6isdtMDfA= +github.com/getkin/kin-openapi v0.149.0/go.mod h1:1+BHDzstro+P5CKtPy1X4PfofnFgmRe6uvMy9+r9fKY= github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -181,8 +181,8 @@ github.com/rs/zerolog v1.35.1 h1:m7xQeoiLIiV0BCEY4Hs+j2NG4Gp2o2KPKmhnnLiazKI= github.com/rs/zerolog v1.35.1/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= -github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 h1:1EYB5IzjZawrrnELUi78f9fPu57HuXjmddZPjrls/28= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.3/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= From 21a679f96971076f560454ba6c50214f4b6945f7 Mon Sep 17 00:00:00 2001 From: Kelvin Date: Thu, 22 Oct 2026 12:45:19 +0530 Subject: [PATCH 3/7] test: cover absolute-path and oversized entries in plugin unzip [#85] Extends the zip extraction tests for code scanning alert #47 (go/zipslip, CWE-22) and the hardening in 1f559223: - TestUnzipAbsolutePathStaysInDest: an absolute entry name is joined under dest and never written to its absolute location. - TestUnzipOversizedEntry: an entry one byte over maxUnzipFileSize is rejected (CWE-409 zip bomb); an entry exactly at the cap still extracts. maxUnzipFileSize becomes an int64 var so the test can lower the cap instead of writing a 100 MiB fixture. Default is unchanged. Refs #85 --- pkg/plugin/manager.go | 3 +- pkg/plugin/manager_test.go | 71 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/pkg/plugin/manager.go b/pkg/plugin/manager.go index 475e91b38..b754721c9 100644 --- a/pkg/plugin/manager.go +++ b/pkg/plugin/manager.go @@ -787,7 +787,8 @@ func unzip(src, dest string) error { } // maxUnzipFileSize caps one extracted entry so a zip bomb cannot fill the disk. -const maxUnzipFileSize = 100 << 20 +// A var so tests can lower it. +var maxUnzipFileSize int64 = 100 << 20 // extractZipFile writes one zip entry to fpath, closing both files before it // returns so descriptors are not held until the whole archive is extracted. diff --git a/pkg/plugin/manager_test.go b/pkg/plugin/manager_test.go index d7e98657f..fc835e2b1 100644 --- a/pkg/plugin/manager_test.go +++ b/pkg/plugin/manager_test.go @@ -1976,6 +1976,77 @@ func TestUnzipFailure(t *testing.T) { } } +// writeTestZip builds a zip at dir/name holding entries (name -> content). +func writeTestZip(t *testing.T, dir, name string, entries map[string]string) string { + t.Helper() + zipPath := filepath.Join(dir, name) + zipFile, err := os.Create(zipPath) + if err != nil { + t.Fatalf("Failed to create zip file: %v", err) + } + defer zipFile.Close() + + zipWriter := zip.NewWriter(zipFile) + for entry, content := range entries { + w, err := zipWriter.Create(entry) + if err != nil { + t.Fatalf("Failed to create %s in zip: %v", entry, err) + } + if _, err := w.Write([]byte(content)); err != nil { + t.Fatalf("Failed to write %s: %v", entry, err) + } + } + if err := zipWriter.Close(); err != nil { + t.Fatalf("Failed to close zip writer: %v", err) + } + return zipPath +} + +// TestUnzipAbsolutePathStaysInDest checks that an absolute entry name is +// joined under dest rather than written to the filesystem root. +func TestUnzipAbsolutePathStaysInDest(t *testing.T) { + tempDir := t.TempDir() + outside := filepath.Join(tempDir, "outside.txt") + zipPath := writeTestZip(t, tempDir, "abs.zip", map[string]string{outside: "pwned"}) + dest := filepath.Join(tempDir, "extracted") + + if err := unzip(zipPath, dest); err != nil { + t.Fatalf("unzip() error = %v, want nil", err) + } + if _, err := os.Stat(outside); !os.IsNotExist(err) { + t.Fatalf("absolute entry was written outside dest at %s", outside) + } + if _, err := os.Stat(filepath.Join(dest, outside)); err != nil { + t.Fatalf("absolute entry not extracted under dest: %v", err) + } +} + +// TestUnzipOversizedEntry checks that an entry over maxUnzipFileSize is +// rejected instead of filling the disk. +func TestUnzipOversizedEntry(t *testing.T) { + orig := maxUnzipFileSize + maxUnzipFileSize = 16 + t.Cleanup(func() { maxUnzipFileSize = orig }) + + tempDir := t.TempDir() + zipPath := writeTestZip(t, tempDir, "bomb.zip", map[string]string{ + "big.txt": strings.Repeat("A", 17), + }) + + err := unzip(zipPath, filepath.Join(tempDir, "extracted")) + if err == nil || !strings.Contains(err.Error(), "exceeds") { + t.Fatalf("unzip() error = %v, want error containing %q", err, "exceeds") + } + + // An entry exactly at the cap is still allowed. + okZip := writeTestZip(t, tempDir, "ok.zip", map[string]string{ + "fit.txt": strings.Repeat("A", 16), + }) + if err := unzip(okZip, filepath.Join(tempDir, "ok")); err != nil { + t.Fatalf("unzip() error = %v, want nil for entry at cap", err) + } +} + // TestValidateMgrCfgSuccess tests the successful scenarios of the validateMgrCfg function. func TestValidateMgrCfgSuccess(t *testing.T) { tests := []struct { From 8d53c77d7389b1ed71fb746ac2b233ba9bcf024b Mon Sep 17 00:00:00 2001 From: Kelvin Date: Thu, 22 Oct 2026 14:42:50 +0530 Subject: [PATCH 4/7] chore: bump Go to 1.26.10 [#85] Move the toolchain from 1.26.9 to the latest 1.26 patch release: - go.mod: go 1.26.9 -> go 1.26.10 - Dockerfile.adapter, Dockerfile.adapter-with-plugins: golang:1.26.9-bookworm -> golang:1.26.10-bookworm - README.md: prerequisite floor updated to match CI workflows read go-version-file: go.mod, so they follow without edits. All open Go stdlib alerts were already cleared by 1.26.9 (eac74d96); this keeps the builder on the newest patch release. Verified with go1.26.10: make build and make test pass; Trivy go.mod scan shows 0 findings at CRITICAL,HIGH,MEDIUM,LOW. Refs #85 --- Dockerfile.adapter | 2 +- Dockerfile.adapter-with-plugins | 2 +- README.md | 2 +- go.mod | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile.adapter b/Dockerfile.adapter index e10daedb3..88af04d6b 100644 --- a/Dockerfile.adapter +++ b/Dockerfile.adapter @@ -1,4 +1,4 @@ -FROM golang:1.26.9-bookworm AS builder +FROM golang:1.26.10-bookworm AS builder WORKDIR /workspace/app COPY cmd/adapter ./cmd/adapter diff --git a/Dockerfile.adapter-with-plugins b/Dockerfile.adapter-with-plugins index 12eca4ff4..699702800 100644 --- a/Dockerfile.adapter-with-plugins +++ b/Dockerfile.adapter-with-plugins @@ -1,4 +1,4 @@ -FROM golang:1.26.9-bookworm AS builder +FROM golang:1.26.10-bookworm AS builder WORKDIR /workspace/app diff --git a/README.md b/README.md index 156a4071b..65ab756f5 100644 --- a/README.md +++ b/README.md @@ -174,7 +174,7 @@ Results land in `benchmarks/results//`. The latest committed report i ### Prerequisites -- Go 1.26.9 or higher — the version `go.mod` requires, and the floor that +- Go 1.26.10 or higher — the version `go.mod` requires, and the floor that clears the current Go standard-library advisories (`make security`) - Redis (for caching) - Docker (optional, for containerized deployment) diff --git a/go.mod b/go.mod index 38cfcbae9..fa8584f85 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/beckn-one/beckn-onix -go 1.26.9 +go 1.26.10 require ( github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 From b105636fa82d4c8f69fdf99d508d3ade130484a1 Mon Sep 17 00:00:00 2001 From: Kelvin Date: Thu, 22 Oct 2026 14:51:33 +0530 Subject: [PATCH 5/7] revert: drop plugin unzip hardening from #85 scope Reverts 21a679f9 and 1f559223. The plugin bundle is operator-supplied and holds .so files the adapter executes, so whoever controls the zip already has code execution; symlink rejection and the size cap add no real protection there. Alert #47 (go/zipslip) is closed by the HasPrefix guard from c093f1f0, which stays. --- pkg/plugin/manager.go | 61 +++++++------------- pkg/plugin/manager_test.go | 114 ------------------------------------- 2 files changed, 19 insertions(+), 156 deletions(-) diff --git a/pkg/plugin/manager.go b/pkg/plugin/manager.go index b754721c9..6c446b696 100644 --- a/pkg/plugin/manager.go +++ b/pkg/plugin/manager.go @@ -760,6 +760,7 @@ func unzip(src, dest string) error { } for _, f := range r.File { + fpath := filepath.Join(dest, f.Name) // Reject a zip entry whose name climbs out of dest (zip slip): // f.Name is attacker-controlled, and filepath.Join alone does not @@ -767,54 +768,30 @@ func unzip(src, dest string) error { if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) { return fmt.Errorf("illegal file path in zip: %s", f.Name) } - if f.FileInfo().IsDir() { - if err := os.MkdirAll(fpath, 0755); err != nil { - return err - } - continue - } - // Only regular files are extracted; a symlink entry could point - // outside dest and a later entry would write through it. - if !f.Mode().IsRegular() { - return fmt.Errorf("unsupported file type in zip: %s", f.Name) + // Ensure directory exists + log.Debugf(context.Background(), "Pain : fpath: %s,filepath.Dir(fpath): %s", fpath, filepath.Dir(fpath)) + if err := os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil { + return err } - if err := extractZipFile(f, fpath); err != nil { + // Open the file inside the zip + srcFile, err := f.Open() + if err != nil { return err } - } + defer srcFile.Close() - return nil -} - -// maxUnzipFileSize caps one extracted entry so a zip bomb cannot fill the disk. -// A var so tests can lower it. -var maxUnzipFileSize int64 = 100 << 20 + // Create the destination file + dstFile, err := os.Create(fpath) + if err != nil { + return err + } + defer dstFile.Close() -// extractZipFile writes one zip entry to fpath, closing both files before it -// returns so descriptors are not held until the whole archive is extracted. -func extractZipFile(f *zip.File, fpath string) error { - if err := os.MkdirAll(filepath.Dir(fpath), 0755); err != nil { - return err - } - srcFile, err := f.Open() - if err != nil { - return err + // Copy file contents + if _, err := io.Copy(dstFile, srcFile); err != nil { + return err + } } - defer srcFile.Close() - dstFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) - if err != nil { - return err - } - n, err := io.Copy(dstFile, io.LimitReader(srcFile, maxUnzipFileSize+1)) - if cerr := dstFile.Close(); err == nil { - err = cerr - } - if err != nil { - return err - } - if n > maxUnzipFileSize { - return fmt.Errorf("zip entry %s exceeds %d bytes", f.Name, maxUnzipFileSize) - } return nil } diff --git a/pkg/plugin/manager_test.go b/pkg/plugin/manager_test.go index fc835e2b1..c56fdd278 100644 --- a/pkg/plugin/manager_test.go +++ b/pkg/plugin/manager_test.go @@ -1705,11 +1705,6 @@ func TestUnzipSuccess(t *testing.T) { } } - // An explicit directory entry, as zip tools emit for folders. - if _, err := zipWriter.Create("emptydir/"); err != nil { - t.Fatalf("Failed to create dir in zip: %v", err) - } - zipWriter.Close() zipFile.Close() @@ -1736,10 +1731,6 @@ func TestUnzipSuccess(t *testing.T) { t.Fatalf("Extracted file %s content = %v, want %v", path, string(content), expectedContent) } } - - if info, err := os.Stat(filepath.Join(dest, "emptydir")); err != nil || !info.IsDir() { - t.Fatalf("Extracted emptydir is not a directory: %v", err) - } }, }, } @@ -1923,40 +1914,6 @@ func TestUnzipFailure(t *testing.T) { }, expectedError: "illegal file path in zip", }, - { - name: "symlink entry rejected", - setupFunc: func() (string, string, func()) { - tempDir, err := os.MkdirTemp("", "unzip-test-*") - if err != nil { - t.Fatalf("Failed to create temp dir: %v", err) - } - - zipPath := filepath.Join(tempDir, "link.zip") - zipFile, err := os.Create(zipPath) - if err != nil { - t.Fatalf("Failed to create zip file: %v", err) - } - - zipWriter := zip.NewWriter(zipFile) - hdr := &zip.FileHeader{Name: "link"} - hdr.SetMode(os.ModeSymlink | 0777) - linkFile, err := zipWriter.CreateHeader(hdr) - if err != nil { - t.Fatalf("Failed to create symlink in zip: %v", err) - } - if _, err := linkFile.Write([]byte("/etc")); err != nil { - t.Fatalf("Failed to write symlink target: %v", err) - } - - zipWriter.Close() - zipFile.Close() - - return zipPath, filepath.Join(tempDir, "extracted"), func() { - os.RemoveAll(tempDir) - } - }, - expectedError: "unsupported file type in zip", - }, } for _, tt := range tests { @@ -1976,77 +1933,6 @@ func TestUnzipFailure(t *testing.T) { } } -// writeTestZip builds a zip at dir/name holding entries (name -> content). -func writeTestZip(t *testing.T, dir, name string, entries map[string]string) string { - t.Helper() - zipPath := filepath.Join(dir, name) - zipFile, err := os.Create(zipPath) - if err != nil { - t.Fatalf("Failed to create zip file: %v", err) - } - defer zipFile.Close() - - zipWriter := zip.NewWriter(zipFile) - for entry, content := range entries { - w, err := zipWriter.Create(entry) - if err != nil { - t.Fatalf("Failed to create %s in zip: %v", entry, err) - } - if _, err := w.Write([]byte(content)); err != nil { - t.Fatalf("Failed to write %s: %v", entry, err) - } - } - if err := zipWriter.Close(); err != nil { - t.Fatalf("Failed to close zip writer: %v", err) - } - return zipPath -} - -// TestUnzipAbsolutePathStaysInDest checks that an absolute entry name is -// joined under dest rather than written to the filesystem root. -func TestUnzipAbsolutePathStaysInDest(t *testing.T) { - tempDir := t.TempDir() - outside := filepath.Join(tempDir, "outside.txt") - zipPath := writeTestZip(t, tempDir, "abs.zip", map[string]string{outside: "pwned"}) - dest := filepath.Join(tempDir, "extracted") - - if err := unzip(zipPath, dest); err != nil { - t.Fatalf("unzip() error = %v, want nil", err) - } - if _, err := os.Stat(outside); !os.IsNotExist(err) { - t.Fatalf("absolute entry was written outside dest at %s", outside) - } - if _, err := os.Stat(filepath.Join(dest, outside)); err != nil { - t.Fatalf("absolute entry not extracted under dest: %v", err) - } -} - -// TestUnzipOversizedEntry checks that an entry over maxUnzipFileSize is -// rejected instead of filling the disk. -func TestUnzipOversizedEntry(t *testing.T) { - orig := maxUnzipFileSize - maxUnzipFileSize = 16 - t.Cleanup(func() { maxUnzipFileSize = orig }) - - tempDir := t.TempDir() - zipPath := writeTestZip(t, tempDir, "bomb.zip", map[string]string{ - "big.txt": strings.Repeat("A", 17), - }) - - err := unzip(zipPath, filepath.Join(tempDir, "extracted")) - if err == nil || !strings.Contains(err.Error(), "exceeds") { - t.Fatalf("unzip() error = %v, want error containing %q", err, "exceeds") - } - - // An entry exactly at the cap is still allowed. - okZip := writeTestZip(t, tempDir, "ok.zip", map[string]string{ - "fit.txt": strings.Repeat("A", 16), - }) - if err := unzip(okZip, filepath.Join(tempDir, "ok")); err != nil { - t.Fatalf("unzip() error = %v, want nil for entry at cap", err) - } -} - // TestValidateMgrCfgSuccess tests the successful scenarios of the validateMgrCfg function. func TestValidateMgrCfgSuccess(t *testing.T) { tests := []struct { From 54a0ffb457ebb1ed0f33b6a0a0613468a1195855 Mon Sep 17 00:00:00 2001 From: Kelvin Date: Thu, 22 Oct 2026 14:59:03 +0530 Subject: [PATCH 6/7] fix: add deprecation notice for unmaintained openpgp package in .trivyignore --- .trivyignore | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.trivyignore b/.trivyignore index cc28caa7f..f63a080b8 100644 --- a/.trivyignore +++ b/.trivyignore @@ -6,3 +6,12 @@ # place so the fixed build is picked up the moment Chainguard publishes it -- # remove this line at that point, not before. CVE-2026-85091 + +# GO-2026-5932 (golang.org/x/crypto/openpgp): a deprecation notice, not a bug +# -- the openpgp package is unmaintained and will never get a fix version, so +# every x/crypto release is flagged. We do not import openpgp (`go list -deps +# ./...` shows only x/crypto/blake2b and pbkdf2). x/crypto itself stays: blake2b +# is the Beckn signing hash (signer.go, signvalidator.go), and grpc, x/net, +# opa, jwx and vault require the module anyway. Remove this line if anything +# here starts importing x/crypto/openpgp. +GO-2026-5932 From 4bb7b7e3cd858829cf6214f244e9fa23e63aece1 Mon Sep 17 00:00:00 2001 From: Kelvin Date: Fri, 23 Oct 2026 10:47:00 +0530 Subject: [PATCH 7/7] docs: shorten the GO-2026-5932 note in .trivyignore [#85] Cut the openpgp explanation down to two lines, per review. --- .trivyignore | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.trivyignore b/.trivyignore index f63a080b8..547903de7 100644 --- a/.trivyignore +++ b/.trivyignore @@ -7,11 +7,6 @@ # remove this line at that point, not before. CVE-2026-85091 -# GO-2026-5932 (golang.org/x/crypto/openpgp): a deprecation notice, not a bug -# -- the openpgp package is unmaintained and will never get a fix version, so -# every x/crypto release is flagged. We do not import openpgp (`go list -deps -# ./...` shows only x/crypto/blake2b and pbkdf2). x/crypto itself stays: blake2b -# is the Beckn signing hash (signer.go, signvalidator.go), and grpc, x/net, -# opa, jwx and vault require the module anyway. Remove this line if anything -# here starts importing x/crypto/openpgp. +# GO-2026-5932: x/crypto/openpgp is unmaintained and has no fix; we never import it +# (only blake2b and pbkdf2). Remove this line if anything starts importing openpgp. GO-2026-5932