Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package melange for openSUSE:Factory checked in at 2026-03-02 17:39:48 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/melange (Old) and /work/SRC/openSUSE:Factory/.melange.new.29461 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "melange" Mon Mar 2 17:39:48 2026 rev:141 rq:1335691 version:0.43.4 Changes: -------- --- /work/SRC/openSUSE:Factory/melange/melange.changes 2026-02-25 21:11:32.112557720 +0100 +++ /work/SRC/openSUSE:Factory/.melange.new.29461/melange.changes 2026-03-02 17:39:58.114458951 +0100 @@ -1,0 +2,22 @@ +Mon Mar 02 06:17:10 UTC 2026 - Johannes Kastl <[email protected]> + +- Update to version 0.43.4: + * build(deps): bump actions/download-artifact from 7.0.0 to 8.0.0 + (#2380) + * build(deps): bump actions/upload-artifact from 6.0.0 to 7.0.0 + (#2381) + * fix(qemu): respect memory limits in pods (#2382) + * Fix melange parsing error line numbers (#2379) + * Add `shallow-submodules` and `submodule-jobs` options for + `git-checkout` (#2368) + * build(deps): bump actions/setup-go in the actions group (#2378) + * build(deps): bump github.com/cloudflare/circl from 1.6.1 to + 1.6.3 (#2377) + * build(deps): bump github.com/go-git/go-git/v5 from 5.16.5 to + 5.17.0 (#2375) + * build(deps): bump step-security/harden-runner in the actions + group (#2376) + * build(deps): bump github.com/google/go-containerregistry + (#2372) + +------------------------------------------------------------------- Old: ---- melange-0.43.3.obscpio New: ---- melange-0.43.4.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ melange.spec ++++++ --- /var/tmp/diff_new_pack.pq7DqA/_old 2026-03-02 17:39:59.798529242 +0100 +++ /var/tmp/diff_new_pack.pq7DqA/_new 2026-03-02 17:39:59.802529409 +0100 @@ -17,7 +17,7 @@ Name: melange -Version: 0.43.3 +Version: 0.43.4 Release: 0 Summary: Build APKs from source code License: Apache-2.0 @@ -26,8 +26,8 @@ Source1: vendor.tar.gz BuildRequires: bash-completion BuildRequires: fish +BuildRequires: go1.25 >= 1.25.6 BuildRequires: zsh -BuildRequires: golang(API) >= 1.25 %description Build apk packages using declarative pipelines. ++++++ _service ++++++ --- /var/tmp/diff_new_pack.pq7DqA/_old 2026-03-02 17:39:59.838530912 +0100 +++ /var/tmp/diff_new_pack.pq7DqA/_new 2026-03-02 17:39:59.842531079 +0100 @@ -3,7 +3,7 @@ <param name="url">https://github.com/chainguard-dev/melange</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v0.43.3</param> + <param name="revision">v0.43.4</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.pq7DqA/_old 2026-03-02 17:39:59.866532080 +0100 +++ /var/tmp/diff_new_pack.pq7DqA/_new 2026-03-02 17:39:59.870532248 +0100 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/chainguard-dev/melange</param> - <param name="changesrevision">66e9282b26b7537ac33b130a3499d9119a0e52f1</param></service></servicedata> + <param name="changesrevision">ba26216d9671b7fd8e2696a3a217f441f2e04b05</param></service></servicedata> (No newline at EOF) ++++++ melange-0.43.3.obscpio -> melange-0.43.4.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/melange-0.43.3/go.mod new/melange-0.43.4/go.mod --- old/melange-0.43.3/go.mod 2026-02-24 17:32:40.000000000 +0100 +++ new/melange-0.43.4/go.mod 2026-02-27 23:24:42.000000000 +0100 @@ -12,9 +12,9 @@ github.com/docker/docker v28.5.2+incompatible github.com/dprotaso/go-yit v0.0.0-20250513224043-18a80f8f6df4 github.com/github/go-spdx/v2 v2.4.0 - github.com/go-git/go-git/v5 v5.16.5 + github.com/go-git/go-git/v5 v5.17.0 github.com/google/go-cmp v0.7.0 - github.com/google/go-containerregistry v0.21.0 + github.com/google/go-containerregistry v0.21.1 github.com/google/licenseclassifier/v2 v2.0.0 github.com/ijt/goparsify v0.0.0-20221203142333-3a5276334b8d github.com/in-toto/attestation v1.1.2 @@ -94,7 +94,7 @@ github.com/charmbracelet/x/ansi v0.10.1 // indirect github.com/charmbracelet/x/cellbuf v0.0.13 // indirect github.com/charmbracelet/x/term v0.2.1 // indirect - github.com/cloudflare/circl v1.6.1 // indirect + github.com/cloudflare/circl v1.6.3 // indirect github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect github.com/containerd/containerd/v2 v2.1.5 // indirect github.com/containerd/log v0.1.0 // indirect @@ -110,7 +110,7 @@ github.com/emirpasic/gods v1.18.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.6.2 // indirect + github.com/go-git/go-billy/v5 v5.8.0 // indirect github.com/go-jose/go-jose/v3 v3.0.4 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.4.3 // indirect diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/melange-0.43.3/go.sum new/melange-0.43.4/go.sum --- old/melange-0.43.3/go.sum 2026-02-24 17:32:40.000000000 +0100 +++ new/melange-0.43.4/go.sum 2026-02-27 23:24:42.000000000 +0100 @@ -63,8 +63,8 @@ github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= -github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= -github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= +github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ= github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w= @@ -127,12 +127,12 @@ github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= -github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0= +github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s= -github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M= +github.com/go-git/go-git/v5 v5.17.0 h1:AbyI4xf+7DsjINHMu35quAh4wJygKBKBuXVjV/pxesM= +github.com/go-git/go-git/v5 v5.17.0/go.mod h1:f82C4YiLx+Lhi8eHxltLeGC5uBTXSFa6PC5WW9o4SjI= github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY= github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= @@ -171,8 +171,8 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-containerregistry v0.21.0 h1:ocqxUOczFwAZQBMNE7kuzfqvDe0VWoZxQMOesXreCDI= -github.com/google/go-containerregistry v0.21.0/go.mod h1:ctO5aCaewH4AK1AumSF5DPW+0+R+d2FmylMJdp5G7p0= +github.com/google/go-containerregistry v0.21.1 h1:sOt/o9BS2b87FnR7wxXPvRKU1XVJn2QCwOS5g8zQXlc= +github.com/google/go-containerregistry v0.21.1/go.mod h1:ctO5aCaewH4AK1AumSF5DPW+0+R+d2FmylMJdp5G7p0= github.com/google/go-licenses/v2 v2.0.1 h1:ti+9bi5o7DKbeeg5eBb/uZTgsaPNoJaLCh93cRcXsW8= github.com/google/go-licenses/v2 v2.0.1/go.mod h1:efibo0EDNGkau6AIMOViGW+rTNPudhxX9rCxtfw5zKE= github.com/google/go-replayers/httpreplay v1.2.0 h1:VM1wEyyjaoU53BwrOnaf9VhAyQQEEioJvFYxYcLRKzk= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/melange-0.43.3/pkg/build/compile.go new/melange-0.43.4/pkg/build/compile.go --- old/melange-0.43.3/pkg/build/compile.go 2026-02-24 17:32:40.000000000 +0100 +++ new/melange-0.43.4/pkg/build/compile.go 2026-02-27 23:24:42.000000000 +0100 @@ -225,6 +225,11 @@ // When compiling an already-compiled config, `uses` will be redundant and FYI only, // so ignore it if there is also a `pipelines` spelled out. if uses != "" && len(pipeline.Pipeline) == 0 { + // Validate that 'uses' does not contain path traversal sequences or absolute paths. + if filepath.IsAbs(uses) || strings.Contains(uses, "..") { + return fmt.Errorf("invalid pipeline 'uses' value %q: must not contain absolute paths or '..' sequences", uses) + } + var data []byte // Set this to fail up front in case there are no pipeline dirs specified // and we can't find them. @@ -232,7 +237,12 @@ for _, pd := range c.PipelineDirs { log.Debugf("trying to load pipeline %q from %q", uses, pd) - data, err = os.ReadFile(filepath.Join(pd, uses+".yaml")) // #nosec G304 - Loading pipeline definition from configured directory + target := filepath.Join(pd, uses+".yaml") + // Verify the resolved path is still within the pipeline directory. + if rel, err := filepath.Rel(pd, filepath.Clean(target)); err != nil || strings.HasPrefix(rel, "..") { + return fmt.Errorf("pipeline 'uses' value %q resolves outside pipeline directory %q", uses, pd) + } + data, err = os.ReadFile(target) // #nosec G304 - Loading pipeline definition from configured directory if err == nil { log.Debugf("Found pipeline %s", string(data)) break diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/melange-0.43.3/pkg/build/pipelines/README.md new/melange-0.43.4/pkg/build/pipelines/README.md --- old/melange-0.43.3/pkg/build/pipelines/README.md 2026-02-24 17:32:40.000000000 +0100 +++ new/melange-0.43.4/pkg/build/pipelines/README.md 2026-02-27 23:24:42.000000000 +0100 @@ -52,7 +52,9 @@ | max-retries | false | Maximum number of retry attempts for git clone operation on failure. | 3 | | recurse-submodules | false | Indicates whether --recurse-submodules should be passed to git clone. | false | | repository | true | The repository to check out sources from. | | +| shallow-submodules | false | Whether to use --shallow-submodules when recurse-submodules is true. Ignored if recurse-submodules is false. | false | | sparse-paths | false | List of directory paths to checkout when using sparse-checkout (cone mode). This is useful for monorepos where you only need specific subdirectories. When specified, only these directories will be checked out from the repository. Uses cone mode for optimal performance. Example: sparse-paths: - omnibump - shared/lib | | +| submodule-jobs | false | The number of concurrent jobs to use when recurse-submodules is true. Ignored if recurse-submodules is false. | 1 | | tag | false | The tag to check out. Branch and tag are mutually exclusive. | | | type-hint | false | Type hint to use during SBOM generation for the provided git repository. This is primarily used to identify Gitlab based sources which are not heuristically identifiable as Gitlab. Supported hints: gitlab. | | diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/melange-0.43.3/pkg/build/pipelines/git-checkout.yaml new/melange-0.43.4/pkg/build/pipelines/git-checkout.yaml --- old/melange-0.43.3/pkg/build/pipelines/git-checkout.yaml 2026-02-24 17:32:40.000000000 +0100 +++ new/melange-0.43.4/pkg/build/pipelines/git-checkout.yaml 2026-02-27 23:24:42.000000000 +0100 @@ -34,6 +34,16 @@ description: | Indicates whether --recurse-submodules should be passed to git clone. default: false + shallow-submodules: + description: | + Whether to use --shallow-submodules when recurse-submodules is true. + Ignored if recurse-submodules is false. + default: false + submodule-jobs: + description: | + The number of concurrent jobs to use when recurse-submodules is true. + Ignored if recurse-submodules is false. + default: 1 cherry-picks: description: | List of cherry picks to apply. @@ -83,7 +93,8 @@ pipeline: - runs: | #!/bin/sh - set -e + # shellcheck shell=busybox + set -eu msg() { echo "[git checkout]" "$@"; } fail() { msg FAIL "$@"; exit 1; } @@ -98,7 +109,7 @@ local attempt=0 local backoff=$initial_backoff - while [ $attempt -le $max_retries ]; do + while [ $attempt -le "$max_retries" ]; do if [ $attempt -gt 0 ]; then msg "Retry attempt $attempt/$max_retries after ${backoff}s backoff..." fi @@ -108,7 +119,7 @@ fi attempt=$((attempt + 1)) - if [ $attempt -gt $max_retries ]; then + if [ $attempt -gt "$max_retries" ]; then msg "All $max_retries retry attempts exhausted" return 1 fi @@ -129,7 +140,7 @@ # Exponential backoff: double the backoff time backoff=$((backoff * 2)) - if [ $backoff -gt $max_backoff ]; then + if [ $backoff -gt "$max_backoff" ]; then backoff=$max_backoff fi done @@ -138,7 +149,7 @@ } process_cherry_picks() { - local cpicksf="$1" oifs="$IFS" count=0 + local cpicksf="$1" count=0 local fetched_branches="" local sdate=${SOURCE_DATE_EPOCH:-0} if [ "$sdate" -lt 315532800 ]; then @@ -166,7 +177,7 @@ # Split the line into branch/hash and comment parts branch=${line%%:*} comment=${line#*:} - comment=$(set -f; echo $comment) # Strip leading/trailing whitespace + comment=$(set -f; echo "$comment") # Strip leading/trailing whitespace if [ -z "$comment" ]; then msg "Empty comment for cherry-pick: $line" @@ -186,7 +197,7 @@ if [ -n "$branch" ]; then case " $fetched_branches " in *" $branch "*) ;; - *) vr git fetch $unshallow_arg origin $branch:$branch || { + *) vr git fetch $unshallow_arg origin "$branch:$branch" || { msg "failed to fetch branch $branch" return 1 } @@ -216,15 +227,21 @@ local tag=$5 expcommit=$6 recurse=${7:-false} local cherry_pick="$8" sparse_paths="$9" local max_retries="${10:-3}" initial_backoff="${11:-2}" max_backoff="${12:-60}" + local shallow_submodules="${13:-false}" submodule_jobs="${14:-1}" msg "repo='$repo' dest='$dest' depth='$depth' branch='$branch'" \ "tag='$tag' expcommit='$expcommit' recurse='$recurse'" \ "sparse_paths='$sparse_paths' max_retries='$max_retries'" \ - "initial_backoff='$initial_backoff' max_backoff='$max_backoff'" + "initial_backoff='$initial_backoff' max_backoff='$max_backoff'" \ + "shallow_submodules='$shallow_submodules' submodule_jobs='$submodule_jobs'" case "$recurse" in true|false) :;; *) fail "recurse must be true or false, not '$recurse'" esac + case "$shallow_submodules" in + true|false) :;; + *) fail "shallow_submodules must be true or false, not '$shallow_submodules'" + esac [ -n "$repo" ] || fail "repository not provided" @@ -242,12 +259,15 @@ flags="--config=advice.detachedHead=false" [ -n "$branch" ] && flags="$flags --branch=$branch" [ -n "$tag" ] && flags="$flags --branch=$tag" - [ "$recurse" = "true" ] && flags="$flags --recurse-submodules" + if [ "$recurse" = "true" ] ; then + flags="$flags --recurse-submodules --jobs=$submodule_jobs" + [ "$shallow_submodules" = "true" ] && flags="$flags --shallow-submodules" + fi [ -n "$sparse_paths" ] && flags="$flags --sparse --filter=blob:none" if [ "$depth" = "unset" ]; then depth=1 - if [ -n "$branch" -a -n "$expcommit" ]; then + if [ -n "$branch" ] && [ -n "$expcommit" ]; then # if we're just checking out a specific commit on a branch # then we need to get history, otherwise it will break # if the expected commit is not tip of the branch. @@ -266,6 +286,7 @@ vr git config --global --add safe.directory "$dest_fullpath" msg "Attempting git clone with retry (max_retries=$max_retries, initial_backoff=${initial_backoff}s, max_backoff=${max_backoff}s)" + # shellcheck disable=SC2086 retry_with_backoff "$max_retries" "$initial_backoff" "$max_backoff" \ git clone $quiet "--origin=$remote" \ "--config=user.name=Melange Build" \ @@ -279,14 +300,15 @@ # Configure sparse-checkout if paths were provided if [ -n "$sparse_paths" ]; then msg "Configuring sparse-checkout with paths: $sparse_paths" - vr git sparse-checkout set --cone $sparse_paths || + vr git sparse-checkout set --cone "$sparse_paths" || fail "failed to configure sparse-checkout --filter=blob:none" fi msg "tar -c . | tar -C \"$dest_fullpath\" -x" ( tar -c . ; echo $? > "$rcfile") | tar -C "$dest_fullpath" -x --no-same-owner + # shellcheck disable=SC2162 # ash doesn't support read -f read rc < "$rcfile" || fail "failed to read rc file" - [ $rc -eq 0 ] || fail "tar creation in $workdir failed" + [ "$rc" -eq 0 ] || fail "tar creation in $workdir failed" rm -rf "$workdir" vr cd "$dest_fullpath" @@ -313,7 +335,7 @@ # git clone --branch=X will pick the branch X if there # exists both a tag and a branch by that name. # since a tag was given, we want the tag. - vr git fetch $quiet $remote ${depthflag:-"$depthflag"} --no-tags \ + vr git fetch $quiet $remote "${depthflag:-"$depthflag"}" --no-tags \ "+refs/tags/$tag:refs/$remote/tags/$tag" vr git checkout $quiet "$remote/tags/$tag" @@ -362,6 +384,7 @@ "${{inputs.recurse-submodules}}" "$cpickf" \ "${{inputs.sparse-paths}}" \ "${{inputs.max-retries}}" "${{inputs.initial-backoff}}" \ - "${{inputs.max-backoff}}" + "${{inputs.max-backoff}}" \ + "${{inputs.shallow-submodules}}" "${{inputs.submodule-jobs}}" rm -f "$cpickf" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/melange-0.43.3/pkg/config/config.go new/melange-0.43.4/pkg/config/config.go --- old/melange-0.43.3/pkg/config/config.go 2026-02-24 17:32:40.000000000 +0100 +++ new/melange-0.43.4/pkg/config/config.go 2026-02-27 23:24:42.000000000 +0100 @@ -22,6 +22,7 @@ "encoding/hex" "errors" "fmt" + "io" "io/fs" "iter" "maps" @@ -1568,24 +1569,23 @@ } defer f.Close() + data, err := io.ReadAll(f) + if err != nil { + return nil, fmt.Errorf("reading %s: %w", configurationFilePath, err) + } + root := yaml.Node{} cfg := Configuration{root: &root} - // Unmarshal into a node first - decoderNode := yaml.NewDecoder(f) + // Unmarshal into a node first, so renovate can operate on its AST. + decoderNode := yaml.NewDecoder(bytes.NewReader(data)) err = decoderNode.Decode(&root) if err != nil { return nil, fmt.Errorf("unable to decode configuration file %q: %w", configurationFilePath, err) } - // XXX(Elizafox) - Node.Decode doesn't allow setting of KnownFields, so we do this cheesy hack below - data, err := yaml.Marshal(&root) - if err != nil { - return nil, fmt.Errorf("unable to decode configuration file %q: %w", configurationFilePath, err) - } - - // Now unmarshal it into the struct, part of said cheesy hack + // Also unmarshal it into the struct. reader := bytes.NewReader(data) decoder := yaml.NewDecoder(reader) decoder.KnownFields(true) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/melange-0.43.3/pkg/config/config_test.go new/melange-0.43.4/pkg/config/config_test.go --- old/melange-0.43.3/pkg/config/config_test.go 2026-02-24 17:32:40.000000000 +0100 +++ new/melange-0.43.4/pkg/config/config_test.go 2026-02-27 23:24:42.000000000 +0100 @@ -2311,3 +2311,32 @@ require.Equal(t, goodLicense, result["Apache-2.0"]) }) } + +func TestLineNumbersInError(t *testing.T) { + ctx := slogtest.Context(t) + + fp := filepath.Join(os.TempDir(), "melange-test-applySubstitutionsInProvides") + if err := os.WriteFile(fp, []byte(`# line 1 is blank so that it's easier to read below +package: + name: replacement-provides + version: 0.0.1 + epoch: 7 + description: example using a replacement in provides + +environment: + # this is a typo for "contents" + content: + packages: + - dep~${{package.version}} +`), 0o644); err != nil { + t.Fatal(err) + } + _, err := ParseConfiguration(ctx, fp) + if err == nil { + t.Fatal("wanted err, got nil") + } + + // This is a bit brittle and will break if we change yaml parsers, + // but what we use doesn't expose the line number in a structured way. + require.Contains(t, err.Error(), "line 10") +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/melange-0.43.3/pkg/container/qemu_runner.go new/melange-0.43.4/pkg/container/qemu_runner.go --- old/melange-0.43.3/pkg/container/qemu_runner.go 2026-02-24 17:32:40.000000000 +0100 +++ new/melange-0.43.4/pkg/container/qemu_runner.go 2026-02-27 23:24:42.000000000 +0100 @@ -1645,11 +1645,47 @@ return num * multiplier / 1024, nil } +// getCgroupMemoryLimitKB reads the cgroup memory limit for the current process. +// It checks cgroup v2 first, then falls back to cgroup v1. Returns 0 if no +// cgroup limit is found or the limit is effectively unlimited. +func getCgroupMemoryLimitKB() int { + // cgroup v2: /sys/fs/cgroup/memory.max + if data, err := os.ReadFile("/sys/fs/cgroup/memory.max"); err == nil { + s := strings.TrimSpace(string(data)) + if s != "max" { + if bytes, err := strconv.ParseInt(s, 10, 64); err == nil && bytes > 0 { + return int(bytes / 1024) + } + } + } + + // cgroup v1: /sys/fs/cgroup/memory/memory.limit_in_bytes + if data, err := os.ReadFile("/sys/fs/cgroup/memory/memory.limit_in_bytes"); err == nil { + s := strings.TrimSpace(string(data)) + if bytes, err := strconv.ParseInt(s, 10, 64); err == nil && bytes > 0 { + // cgroup v1 reports a very large number (close to max int64) when unlimited + const unlimitedThreshold = 1 << 50 // ~1 PiB, well above any real limit + if bytes < unlimitedThreshold { + return int(bytes / 1024) + } + } + } + + return 0 +} + func getAvailableMemoryKB() int { mem := 16000000 switch runtime.GOOS { case "linux": + // Check cgroup limits first — in a container (e.g. Kubernetes pod), + // /proc/meminfo reports the entire node's memory, not the pod's allocation. + // The cgroup limit reflects the actual memory available to this process. + if cgroupMem := getCgroupMemoryLimitKB(); cgroupMem > 0 { + return cgroupMem + } + f, e := os.Open("/proc/meminfo") if e != nil { return mem diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/melange-0.43.3/pkg/linter/results.go new/melange-0.43.4/pkg/linter/results.go --- old/melange-0.43.3/pkg/linter/results.go 2026-02-24 17:32:40.000000000 +0100 +++ new/melange-0.43.4/pkg/linter/results.go 2026-02-27 23:24:42.000000000 +0100 @@ -20,6 +20,7 @@ "fmt" "os" "path/filepath" + "strings" "github.com/chainguard-dev/clog" @@ -27,6 +28,14 @@ "chainguard.dev/melange/pkg/linter/types" ) +// containsPathTraversal checks if a string contains path traversal sequences +// or path separators that could be used to escape the intended directory. +func containsPathTraversal(s string) bool { + return strings.Contains(s, "..") || + strings.Contains(s, string(filepath.Separator)) || + strings.Contains(s, "/") +} + // saveLintResults saves the lint results to JSON files in the packages directory func saveLintResults(ctx context.Context, cfg *config.Configuration, results map[string]*types.PackageLintResults, outputDir, arch string) error { log := clog.FromContext(ctx) @@ -37,6 +46,11 @@ return nil } + // Validate arch to prevent path traversal + if containsPathTraversal(arch) { + return fmt.Errorf("invalid arch %q: contains path traversal sequence", arch) + } + // Ensure the package directory exists packageDir := filepath.Join(outputDir, arch) if err := os.MkdirAll(packageDir, 0o755); err != nil { @@ -45,6 +59,10 @@ // Save results for each package for pkgName, pkgResults := range results { + // Validate pkgName to prevent path traversal + if containsPathTraversal(pkgName) { + return fmt.Errorf("invalid package name %q: contains path traversal sequence", pkgName) + } // Generate the filename: lint-{packagename}-{version}-r{epoch}.json filename := fmt.Sprintf("lint-%s-%s-r%d.json", pkgName, cfg.Package.Version, cfg.Package.Epoch) filepath := filepath.Join(packageDir, filename) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/melange-0.43.3/pkg/renovate/cache/cache.go new/melange-0.43.4/pkg/renovate/cache/cache.go --- old/melange-0.43.3/pkg/renovate/cache/cache.go 2026-02-24 17:32:40.000000000 +0100 +++ new/melange-0.43.4/pkg/renovate/cache/cache.go 2026-02-27 23:24:42.000000000 +0100 @@ -26,6 +26,7 @@ "os" "path" "strings" + "time" "github.com/chainguard-dev/clog" "github.com/dprotaso/go-yit" @@ -219,6 +220,12 @@ return nil } +// maxDownloadSize is the maximum allowed download size (1 GB). +const maxDownloadSize = 1 << 30 // 1 GiB + +// downloadTimeout is the maximum time allowed for the entire download. +const downloadTimeout = 30 * time.Minute + // downloadFile downloads a file and returns a path to it in temporary storage. func downloadFile(ctx context.Context, uri string) (string, error) { targetFile, err := os.CreateTemp("", "melange-update-*") @@ -227,6 +234,10 @@ } defer targetFile.Close() + // Apply a timeout to the download context to prevent hanging connections. + ctx, cancel := context.WithTimeout(ctx, downloadTimeout) + defer cancel() + client := &http.Client{ CheckRedirect: func(req *http.Request, via []*http.Request) error { // delete the referer header else redirects with sourceforge do not work well. See https://stackoverflow.com/questions/67203383/downloading-from-sourceforge-wait-and-redirect @@ -254,8 +265,15 @@ return "", fmt.Errorf("unexpected status code %d (%s) when fetching %s", resp.StatusCode, resp.Status, uri) } - if _, err := io.Copy(targetFile, resp.Body); err != nil { - return "", err + // Limit the download size to prevent unbounded resource consumption. + limitedReader := io.LimitReader(resp.Body, maxDownloadSize+1) + n, err := io.Copy(targetFile, limitedReader) + if err != nil { + return "", fmt.Errorf("failed to download %s: %w", uri, err) + } + if n > maxDownloadSize { + os.Remove(targetFile.Name()) + return "", fmt.Errorf("download from %s exceeds maximum allowed size of %d bytes", uri, maxDownloadSize) } return targetFile.Name(), nil ++++++ melange.obsinfo ++++++ --- /var/tmp/diff_new_pack.pq7DqA/_old 2026-03-02 17:40:01.706608883 +0100 +++ /var/tmp/diff_new_pack.pq7DqA/_new 2026-03-02 17:40:01.718609384 +0100 @@ -1,5 +1,5 @@ name: melange -version: 0.43.3 -mtime: 1771950760 -commit: 66e9282b26b7537ac33b130a3499d9119a0e52f1 +version: 0.43.4 +mtime: 1772231082 +commit: ba26216d9671b7fd8e2696a3a217f441f2e04b05 ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/melange/vendor.tar.gz /work/SRC/openSUSE:Factory/.melange.new.29461/vendor.tar.gz differ: char 132, line 3
