VYPR
Medium severity4.4OSV Advisory· Published Jul 18, 2025· Updated Apr 15, 2026

CVE-2025-54059

CVE-2025-54059

Description

melange allows users to build apk packages using declarative pipelines. Starting in version 0.23.0 and prior to version 0.29.5, SBOM files generated by melange in apks had file system permissions mode 666. This potentially allows an unprivileged user to tamper with apk SBOMs on a running image, potentially confusing security scanners. An attacker could also perform a DoS under special circumstances. Version 0.29.5 fixes the issue.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
chainguard.dev/melangeGo
>= 0.23.0, < 0.29.50.29.5

Affected products

1

Patches

3
e29494b4a40a

fix: tighten up permissions for written SBOM files and signature tarballs (#2086)

https://github.com/chainguard-dev/melangeEvan GiblerJul 16, 2025via ghsa
3 files changed · +4 4
  • pkg/build/build.go+1 1 modified
    @@ -977,7 +977,7 @@ func (b Build) writeSBOM(pkgName string, doc *spdx.Document) error {
     
     	pkgVersion := b.Configuration.Package.FullVersion()
     	sbomPath := getPathForPackageSBOM(sbomDirPath, pkgName, pkgVersion)
    -	f, err := b.WorkspaceDirFS.Create(sbomPath)
    +	f, err := b.WorkspaceDirFS.OpenFile(sbomPath, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0o644)
     	if err != nil {
     		return fmt.Errorf("opening SBOM file for writing: %w", err)
     	}
    
  • pkg/sign/apk.go+1 1 modified
    @@ -129,7 +129,7 @@ func EmitSignature(signer ApkSigner, controlData []byte, sde time.Time) ([]byte,
     		Name:     signer.SignatureName(),
     		Typeflag: tar.TypeReg,
     		Size:     int64(len(sig)),
    -		Mode:     int64(os.ModePerm),
    +		Mode:     int64(0o644),
     		Uid:      0,
     		Gid:      0,
     		Uname:    "root",
    
  • pkg/sign/apk_test.go+2 2 modified
    @@ -161,15 +161,15 @@ func TestEmitSignature(t *testing.T) {
     		Name:     MockName,
     		Typeflag: tar.TypeReg,
     		Size:     int64(len(controlData)),
    -		Mode:     int64(os.ModePerm),
    +		Mode:     int64(0o644),
     		Uid:      0,
     		Gid:      0,
     		Uname:    "root",
     		Gname:    "root",
     		ModTime:  sde,
     	}
     	if diff := cmp.Diff(hdr, hdrWant, cmpopts.IgnoreFields(tar.Header{}, "AccessTime", "ChangeTime", "Format")); diff != "" {
    -		t.Errorf("Expected %v got %v", hdr, hdrWant)
    +		t.Errorf("Expected %v got %v", hdrWant, hdr)
     	}
     
     	if hdr.Name != "mockiavelli" {
    
1b272db2a0bb

Persist workspace filesystem throughout package builds (#1836)

https://github.com/chainguard-dev/melangeEvan GiblerMar 13, 2025via ghsa
14 files changed · +179 169
  • e2e-tests/ipset-build-test.yaml+90 0 added
    @@ -0,0 +1,90 @@
    +package:
    +  name: ipset
    +  version: "7.22"
    +  epoch: 3
    +  description: Manage Linux IP sets
    +  copyright:
    +    - license: GPL-2.0-only
    +
    +environment:
    +  contents:
    +    packages:
    +      - autoconf
    +      - automake
    +      - build-base
    +      - busybox
    +      - ca-certificates-bundle
    +      - libmnl-dev
    +      - libtool
    +      - linux-headers
    +      - pkgconf-dev
    +
    +pipeline:
    +  - uses: git-checkout
    +    with:
    +      repository: https://git.netfilter.org/ipset
    +      tag: v${{package.version}}
    +      expected-commit: a50abde9c959be364782c01c61429a951454f5ef
    +      depth: "-1"
    +
    +  - runs: |
    +      ./autogen.sh
    +
    +  - uses: autoconf/configure
    +    with:
    +      opts: |
    +        --build=${{host.triplet.gnu}} \
    +        --host=${{host.triplet.gnu}} \
    +        --with-kmod=no \
    +        --prefix=/usr
    +
    +  - uses: autoconf/make
    +
    +  - uses: autoconf/make-install
    +
    +  - uses: strip
    +
    +subpackages:
    +  - name: ipset-dev
    +    pipeline:
    +      - uses: split/dev
    +    dependencies:
    +      runtime:
    +        - ipset
    +    description: ipset dev
    +    test:
    +      environment:
    +        contents:
    +          packages:
    +            - busybox
    +      pipeline:
    +        - runs: |
    +            test -e /usr/include/libipset/ipset.h
    +            test -s /usr/include/libipset/ipset.h
    +  - name: ipset-doc
    +    pipeline:
    +      - uses: split/manpages
    +    description: ipset manpages
    +    test:
    +      environment:
    +        contents:
    +          packages:
    +            - busybox
    +      pipeline:
    +        - runs: |
    +            man ipset | head -n 10
    +
    +update:
    +  enabled: true
    +  release-monitor:
    +    identifier: 1393
    +
    +test:
    +  environment:
    +    contents:
    +      packages:
    +        - busybox
    +  pipeline:
    +    - runs: |
    +        ipset --help
    +        ipset-translate --help
    
  • .github/workflows/wolfi-presubmit.yaml+1 0 modified
    @@ -70,6 +70,7 @@ jobs:
               - s3cmd
               - perl-yaml-syck
               - ncurses
    +          - fping
               # TODO: https://github.com/wolfi-dev/os/issues/26442
               #- xmlto
     
    
  • go.mod+4 4 modified
    @@ -3,7 +3,7 @@ module chainguard.dev/melange
     go 1.23.4
     
     require (
    -	chainguard.dev/apko v0.25.3
    +	chainguard.dev/apko v0.25.4
     	cloud.google.com/go/storage v1.51.0
     	dagger.io/dagger v0.16.3
     	github.com/chainguard-dev/clog v1.7.0
    @@ -42,7 +42,6 @@ require (
     	golang.org/x/crypto v0.36.0
     	golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa
     	golang.org/x/sync v0.12.0
    -	golang.org/x/sys v0.31.0
     	golang.org/x/text v0.23.0
     	golang.org/x/time v0.11.0
     	google.golang.org/api v0.225.0
    @@ -209,19 +208,20 @@ require (
     	go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
     	go.opentelemetry.io/otel/trace v1.35.0 // indirect
     	go.opentelemetry.io/proto/otlp v1.4.0 // indirect
    -	go.step.sm/crypto v0.58.1 // indirect
    +	go.step.sm/crypto v0.59.1 // indirect
     	go.uber.org/multierr v1.11.0 // indirect
     	go.uber.org/zap v1.27.0 // indirect
     	golang.org/x/mod v0.23.0 // indirect
     	golang.org/x/net v0.37.0 // indirect
     	golang.org/x/oauth2 v0.28.0 // indirect
    +	golang.org/x/sys v0.31.0 // indirect
     	golang.org/x/term v0.30.0 // indirect
     	google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
     	google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
     	google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect
     	google.golang.org/grpc v1.71.0 // indirect
     	google.golang.org/protobuf v1.36.5 // indirect
     	gopkg.in/warnings.v0 v0.1.2 // indirect
    -	k8s.io/apimachinery v0.32.2 // indirect
    +	k8s.io/apimachinery v0.32.3 // indirect
     	k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
     )
    
  • go.sum+6 6 modified
    @@ -1,7 +1,7 @@
     cel.dev/expr v0.19.2 h1:V354PbqIXr9IQdwy4SYA4xa0HXaWq1BUPAGzugBY5V4=
     cel.dev/expr v0.19.2/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
    -chainguard.dev/apko v0.25.3 h1:wN8Ki/2s++XwK4E19x6MW1PIWq0I9j5gdZsVlp2NYBQ=
    -chainguard.dev/apko v0.25.3/go.mod h1:4i/zhP74WpHcGV8t9bQCueokNY27M9QRtocELa3aG8Y=
    +chainguard.dev/apko v0.25.4 h1:QFFCD7QR3q9AT5H+/ZCXQReCNN0CgRn1mkOq1T+FZfQ=
    +chainguard.dev/apko v0.25.4/go.mod h1:1xdjg538oPqb7VCiDMc4IlNtS5gRfpJqv2VIveR6wIo=
     chainguard.dev/go-grpc-kit v0.17.7 h1:TqHua7er5k8m6WM96y0Tm7IoLLkuZ5vh3+5SR1gruKg=
     chainguard.dev/go-grpc-kit v0.17.7/go.mod h1:JroMzTY9mdhKe/bvtyChgfECaNh80+bMZH3HS+TGXHw=
     chainguard.dev/sdk v0.1.31 h1:Blvpa0Ji/tC1VVV8/l8UyQe022LoRxZLfgasyFE1EhQ=
    @@ -534,8 +534,8 @@ go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt
     go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
     go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg=
     go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY=
    -go.step.sm/crypto v0.58.1 h1:2PpEYTbytA3el9dW0gh9uJEe/CR/J6wS+x2vWYLG83M=
    -go.step.sm/crypto v0.58.1/go.mod h1:yluOL5OqY7mXGGQ7JUmAv/6h8T8Ge3yXdlEESWHOqDQ=
    +go.step.sm/crypto v0.59.1 h1:jUL+5p19YS9YJKLaPUgkS2OdGm7s0+hwP7AqTFyF9Cg=
    +go.step.sm/crypto v0.59.1/go.mod h1:XHavmnzfTyPpQE/n4YokEtjiBzP3LZI9/1O061f5y0o=
     go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
     go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
     go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
    @@ -721,8 +721,8 @@ gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
     honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
     honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
     honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
    -k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ=
    -k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
    +k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
    +k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
     k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
     k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
     k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
    
  • pkg/build/build.go+10 7 modified
    @@ -101,6 +101,7 @@ type Build struct {
     
     	SourceDateEpoch time.Time
     	WorkspaceDir    string
    +	WorkspaceDirFS  apkofs.FullFS
     	WorkspaceIgnore string
     	// Ordered directories where to find 'uses' pipelines.
     	PipelineDirs          []string
    @@ -311,6 +312,7 @@ func (b *Build) buildGuest(ctx context.Context, imgConfig apko_types.ImageConfig
     	if b.Runner.Name() == container.QemuName {
     		b.ExtraPackages = append(b.ExtraPackages, []string{
     			"melange-microvm-init",
    +			"gnutar",
     		}...)
     	}
     
    @@ -626,7 +628,7 @@ func (b *Build) populateCache(ctx context.Context) error {
     		defer os.RemoveAll(tmp)
     		log.Infof("cache bucket copied to %s", tmp)
     
    -		fsys := os.DirFS(tmp)
    +		fsys := apkofs.DirFS(tmp)
     
     		// mkdir /var/cache/melange
     		if err := os.MkdirAll(b.CacheDir, 0o755); err != nil {
    @@ -842,7 +844,7 @@ func (b *Build) BuildPackage(ctx context.Context) error {
     		}
     
     		log.Infof("populating workspace %s from %s", b.WorkspaceDir, b.SourceDir)
    -		if err := b.populateWorkspace(ctx, os.DirFS(b.SourceDir)); err != nil {
    +		if err := b.populateWorkspace(ctx, apkofs.DirFS(b.SourceDir)); err != nil {
     			return fmt.Errorf("unable to populate workspace: %w", err)
     		}
     	}
    @@ -949,8 +951,9 @@ func (b *Build) BuildPackage(ctx context.Context) error {
     
     	// Retrieve the post build workspace from the runner
     	log.Infof("retrieving workspace from builder: %s", cfg.PodID)
    -	fsys := apkofs.DirFS(b.WorkspaceDir)
    -	if err := b.retrieveWorkspace(ctx, fsys); err != nil {
    +	b.WorkspaceDirFS = apkofs.DirFS(b.WorkspaceDir)
    +
    +	if err := b.retrieveWorkspace(ctx, b.WorkspaceDirFS); err != nil {
     		return fmt.Errorf("retrieving workspace: %w", err)
     	}
     	log.Infof("retrieved and wrote post-build workspace to: %s", b.WorkspaceDir)
    @@ -1070,15 +1073,15 @@ func (b *Build) BuildPackage(ctx context.Context) error {
     // filesystem in the directory `/var/lib/db/sbom`. The pkgName parameter should
     // be set to the name of the origin package or subpackage.
     func (b Build) writeSBOM(pkgName string, doc *spdx.Document) error {
    -	apkFSPath := filepath.Join(b.WorkspaceDir, melangeOutputDirName, pkgName)
    +	apkFSPath := filepath.Join(melangeOutputDirName, pkgName)
     	sbomDirPath := filepath.Join(apkFSPath, "/var/lib/db/sbom")
    -	if err := os.MkdirAll(sbomDirPath, os.FileMode(0o755)); err != nil {
    +	if err := b.WorkspaceDirFS.MkdirAll(sbomDirPath, os.FileMode(0o755)); err != nil {
     		return fmt.Errorf("creating SBOM directory: %w", err)
     	}
     
     	pkgVersion := b.Configuration.Package.FullVersion()
     	sbomPath := getPathForPackageSBOM(sbomDirPath, pkgName, pkgVersion)
    -	f, err := os.Create(sbomPath)
    +	f, err := b.WorkspaceDirFS.Create(sbomPath)
     	if err != nil {
     		return fmt.Errorf("opening SBOM file for writing: %w", err)
     	}
    
  • pkg/build/package.go+8 4 modified
    @@ -30,6 +30,7 @@ import (
     	"strings"
     	"text/template"
     
    +	apkofs "chainguard.dev/apko/pkg/apk/fs"
     	apko_types "chainguard.dev/apko/pkg/build/types"
     
     	"github.com/klauspost/compress/gzip"
    @@ -371,7 +372,7 @@ func combine(out io.Writer, inputs ...io.Reader) error {
     }
     
     // TODO(kaniini): generate APKv3 packages
    -func (pc *PackageBuild) calculateInstalledSize(fsys fs.FS) error {
    +func (pc *PackageBuild) calculateInstalledSize(fsys apkofs.FullFS) error {
     	if err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
     		if err != nil {
     			return err
    @@ -391,7 +392,7 @@ func (pc *PackageBuild) calculateInstalledSize(fsys fs.FS) error {
     	return nil
     }
     
    -func (pc *PackageBuild) emitDataSection(ctx context.Context, fsys fs.FS, userinfofs fs.FS, remapUIDs map[int]int, remapGIDs map[int]int, w io.WriteSeeker) error {
    +func (pc *PackageBuild) emitDataSection(ctx context.Context, fsys apkofs.FullFS, userinfofs apkofs.FullFS, remapUIDs map[int]int, remapGIDs map[int]int, w io.WriteSeeker) error {
     	log := clog.FromContext(ctx)
     	tarctx, err := tarball.NewContext(
     		tarball.WithSourceDateEpoch(pc.Build.SourceDateEpoch),
    @@ -445,10 +446,13 @@ func (pc *PackageBuild) EmitPackage(ctx context.Context) error {
     	log.Info("generating package " + pc.Identity())
     
     	// filesystem for the data package
    -	fsys := readlinkFS(pc.WorkspaceSubdir())
    +	fsys, err := apkofs.Sub(pc.Build.WorkspaceDirFS, filepath.Join(melangeOutputDirName, pc.PackageName))
    +	if err != nil {
    +		return fmt.Errorf("failed to return filesystem for workspace subtree: %w", err)
    +	}
     
     	// provide the tar writer etc/passwd and etc/group of guest filesystem
    -	userinfofs := os.DirFS(pc.Build.GuestDir)
    +	userinfofs := apkofs.DirFS(pc.Build.GuestDir)
     
     	hdl := &SCABuildInterface{
     		PackageBuild: pc,
    
  • pkg/build/readlinkfs.go+0 142 removed
    @@ -1,142 +0,0 @@
    -// Copyright 2022, 2023 Chainguard, Inc.
    -//
    -// Licensed under the Apache License, Version 2.0 (the "License");
    -// you may not use this file except in compliance with the License.
    -// You may obtain a copy of the License at
    -//
    -//     http://www.apache.org/licenses/LICENSE-2.0
    -//
    -// Unless required by applicable law or agreed to in writing, software
    -// distributed under the License is distributed on an "AS IS" BASIS,
    -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    -// See the License for the specific language governing permissions and
    -// limitations under the License.
    -
    -package build
    -
    -import (
    -	"io/fs"
    -	"os"
    -	"path/filepath"
    -
    -	apkofs "chainguard.dev/apko/pkg/apk/fs"
    -	"golang.org/x/sys/unix"
    -)
    -
    -type rlfs struct {
    -	apkofs.XattrFS
    -
    -	base string
    -	f    fs.FS
    -}
    -
    -func (f *rlfs) Readlink(name string) (string, error) {
    -	target, err := os.Readlink(filepath.Join(f.base, name))
    -	if err != nil {
    -		return "", err
    -	}
    -	return target, nil
    -}
    -
    -func (f *rlfs) Open(name string) (fs.File, error) {
    -	return f.f.Open(name)
    -}
    -
    -func (f *rlfs) Stat(name string) (fs.FileInfo, error) {
    -	return os.Stat(filepath.Join(f.base, name))
    -}
    -
    -func (f *rlfs) SetXattr(path string, attr string, data []byte) error {
    -	return unix.Setxattr(filepath.Join(f.base, path), attr, data, 0)
    -}
    -
    -func (f *rlfs) GetXattr(path string, attr string) ([]byte, error) {
    -	realPath := filepath.Join(f.base, path)
    -
    -	size, err := unix.Getxattr(realPath, attr, nil)
    -	if err != nil {
    -		return []byte{}, err
    -	}
    -
    -	buf := make([]byte, size)
    -	_, err = unix.Getxattr(realPath, attr, buf)
    -	if err != nil {
    -		return []byte{}, err
    -	}
    -
    -	return buf, nil
    -}
    -
    -func (f *rlfs) RemoveXattr(path string, attr string) error {
    -	return unix.Removexattr(filepath.Join(f.base, path), attr)
    -}
    -
    -// stringsFromByteSlice converts a sequence of attributes to a []string.
    -// On Linux, each entry is a NULL-terminated string.
    -// Taken from golang.org/x/sys/unix/syscall_linux_test.go.
    -func stringsFromByteSlice(buf []byte) []string {
    -	var result []string
    -	off := 0
    -	for i, b := range buf {
    -		if b == 0 {
    -			result = append(result, string(buf[off:i]))
    -			off = i + 1
    -		}
    -	}
    -	return result
    -}
    -
    -// xattrIgnoreList contains a mapping of xattr names used by various
    -// security features which leak their state into packages.  We need to
    -// ignore these xattrs because they require special permissions to be
    -// set when the underlying security features are in use.
    -var xattrIgnoreList = map[string]bool{
    -	"com.apple.provenance":          true,
    -	"security.csm":                  true,
    -	"security.selinux":              true,
    -	"com.docker.grpcfuse.ownership": true,
    -}
    -
    -func (f *rlfs) ListXattrs(path string) (map[string][]byte, error) {
    -	realPath := filepath.Join(f.base, path)
    -
    -	size, err := unix.Listxattr(realPath, nil)
    -	if err != nil {
    -		return map[string][]byte{}, err
    -	}
    -
    -	// If the xattr list is empty, the size will be 0.
    -	if size <= 0 {
    -		return map[string][]byte{}, nil
    -	}
    -
    -	buf := make([]byte, size)
    -	read, err := unix.Listxattr(realPath, buf)
    -	if err != nil {
    -		return map[string][]byte{}, err
    -	}
    -
    -	xattrMap := map[string][]byte{}
    -	xattrNames := stringsFromByteSlice(buf[:read])
    -	for _, xattrName := range xattrNames {
    -		if _, ok := xattrIgnoreList[xattrName]; ok {
    -			continue
    -		}
    -
    -		result, err := f.GetXattr(path, xattrName)
    -		if err != nil {
    -			return map[string][]byte{}, err
    -		}
    -
    -		xattrMap[xattrName] = result
    -	}
    -
    -	return xattrMap, nil
    -}
    -
    -func readlinkFS(dir string) apkofs.ReadLinkFS {
    -	return &rlfs{
    -		base: dir,
    -		f:    os.DirFS(dir),
    -	}
    -}
    
  • pkg/build/sca_interface.go+5 2 modified
    @@ -18,6 +18,7 @@ import (
     	"fmt"
     	"path/filepath"
     
    +	apkofs "chainguard.dev/apko/pkg/apk/fs"
     	"chainguard.dev/melange/pkg/config"
     	"chainguard.dev/melange/pkg/sca"
     )
    @@ -54,8 +55,10 @@ func (scabi *SCABuildInterface) Version() string {
     // FilesystemForRelative implements an abstract filesystem for any of the packages being
     // built.
     func (scabi *SCABuildInterface) FilesystemForRelative(pkgName string) (sca.SCAFS, error) {
    -	pkgDir := filepath.Join(scabi.PackageBuild.Build.WorkspaceDir, melangeOutputDirName, pkgName)
    -	rlFS := readlinkFS(pkgDir)
    +	rlFS, err := apkofs.Sub(scabi.PackageBuild.Build.WorkspaceDirFS, filepath.Join(melangeOutputDirName, pkgName))
    +	if err != nil {
    +		return nil, fmt.Errorf("package build subFS: %w", err)
    +	}
     	scaFS, ok := rlFS.(sca.SCAFS)
     	if !ok {
     		return nil, fmt.Errorf("SCAFS not implemented")
    
  • pkg/build/test.go+3 2 modified
    @@ -311,7 +311,7 @@ func (t *Test) PopulateWorkspace(ctx context.Context, src fs.FS) error {
     
     	log.Infof("populating workspace %s from %s", t.WorkspaceDir, t.SourceDir)
     
    -	fsys := os.DirFS(t.SourceDir)
    +	fsys := apkofs.DirFS(t.SourceDir)
     
     	return fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
     		if err != nil {
    @@ -349,6 +349,7 @@ func (t *Test) TestPackage(ctx context.Context) error {
     	if t.Runner.Name() == container.QemuName {
     		t.ExtraTestPackages = append(t.ExtraTestPackages, []string{
     			"melange-microvm-init",
    +			"gnutar",
     		}...)
     	}
     
    @@ -425,7 +426,7 @@ func (t *Test) TestPackage(ctx context.Context) error {
     			return fmt.Errorf("mkdir -p %s: %w", t.WorkspaceDir, err)
     		}
     
    -		if err := t.PopulateWorkspace(ctx, os.DirFS(t.SourceDir)); err != nil {
    +		if err := t.PopulateWorkspace(ctx, apkofs.DirFS(t.SourceDir)); err != nil {
     			return fmt.Errorf("unable to populate workspace: %w", err)
     		}
     	}
    
  • pkg/config/schema.json+45 0 modified
    @@ -35,6 +35,40 @@
           ],
           "description": "BuildOption describes an optional deviation to a package build."
         },
    +    "CPE": {
    +      "properties": {
    +        "part": {
    +          "type": "string"
    +        },
    +        "vendor": {
    +          "type": "string"
    +        },
    +        "product": {
    +          "type": "string"
    +        },
    +        "edition": {
    +          "type": "string"
    +        },
    +        "language": {
    +          "type": "string"
    +        },
    +        "sw_edition": {
    +          "type": "string"
    +        },
    +        "target_sw": {
    +          "type": "string"
    +        },
    +        "target_hw": {
    +          "type": "string"
    +        },
    +        "other": {
    +          "type": "string"
    +        }
    +      },
    +      "additionalProperties": false,
    +      "type": "object",
    +      "description": "CPE stores values used to produce a CPE to describe the package, suitable for matching against NVD records."
    +    },
         "Capabilities": {
           "properties": {
             "add": {
    @@ -530,6 +564,13 @@
               "type": "string",
               "description": "A human-readable description of the package"
             },
    +        "annotations": {
    +          "additionalProperties": {
    +            "type": "string"
    +          },
    +          "type": "object",
    +          "description": "Annotations for this package"
    +        },
             "url": {
               "type": "string",
               "description": "The URL to the package's homepage"
    @@ -568,6 +609,10 @@
               "$ref": "#/$defs/Checks",
               "description": "Optional: enabling, disabling, and configuration of build checks"
             },
    +        "cpe": {
    +          "$ref": "#/$defs/CPE",
    +          "description": "The CPE field values to be used for matching against NVD vulnerability\nrecords, if known."
    +        },
             "timeout": {
               "type": "integer",
               "description": "Optional: The amount of time to allow this build to take before timing out."
    
  • pkg/container/qemu_runner.go+1 1 modified
    @@ -234,7 +234,7 @@ func (bw *qemu) WorkspaceTar(ctx context.Context, cfg *Config) (io.ReadCloser, e
     		nil,
     		outFile,
     		false,
    -		[]string{"sh", "-c", "cd /home/build && tar cvzf - melange-out"},
    +		[]string{"sh", "-c", "cd /home/build && tar cvpzf - --xattrs --acls melange-out"},
     	)
     	if err != nil {
     		return nil, err
    
  • pkg/linter/linter.go+2 1 modified
    @@ -29,6 +29,7 @@ import (
     	"slices"
     	"strings"
     
    +	apkofs "chainguard.dev/apko/pkg/apk/fs"
     	"github.com/chainguard-dev/clog"
     	"github.com/dustin/go-humanize"
     	"golang.org/x/exp/maps"
    @@ -677,7 +678,7 @@ func LintBuild(ctx context.Context, cfg *config.Configuration, packageName strin
     	}
     
     	log := clog.FromContext(ctx)
    -	fsys := os.DirFS(path)
    +	fsys := apkofs.DirFS(path)
     
     	if err := lintPackageFS(ctx, cfg, packageName, fsys, warn); err != nil {
     		log.Warn(err.Error())
    
  • pkg/sca/sca.go+4 0 modified
    @@ -731,6 +731,10 @@ func generateShbangDeps(ctx context.Context, hdl SCAHandle, generated *config.De
     			return err
     		}
     
    +		if d.Type()&fs.ModeSymlink == fs.ModeSymlink {
    +			return nil
    +		}
    +
     		if !strings.HasPrefix(path, "usr/bin/") && !strings.HasPrefix(path, "bin/") {
     			return nil
     		}
    
  • pkg/sca/testdata/generated/x86_64/shbang-test-1-r1.apk+0 0 modified

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

9

News mentions

0

No linked articles in our index yet.