diff --git a/SPECS/jx/CVE-2025-58058.patch b/SPECS/jx/CVE-2025-58058.patch new file mode 100644 index 00000000000..8e0053daae2 --- /dev/null +++ b/SPECS/jx/CVE-2025-58058.patch @@ -0,0 +1,496 @@ +From 4a84bf8d6bbc00d87ae228682261aa62421052ed Mon Sep 17 00:00:00 2001 +From: Ulrich Kunitz +Date: Mon, 12 Dec 2022 20:41:07 +0100 +Subject: [PATCH 1/2] lzma: fix handling of small dictionary sizes + +As Matt Dainty (@bodgit) reported there is an issue if the header of the +LZMA stream is less than the minimum dictionary size of 4096 byte. The +specification of the LZMA format says that in that case a dictionary +size of 4096 byte should be used, our code returns an error. + +This commit changes the behavior and adds a simple test case to test for +the right behavior. + +Fixes [#52](https://github.com/ulikunitz/xz/pull/52) +--- + vendor/github.com/ulikunitz/xz/lzma/reader.go | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/vendor/github.com/ulikunitz/xz/lzma/reader.go b/vendor/github.com/ulikunitz/xz/lzma/reader.go +index 7b7eef3..2c3d073 100644 +--- a/vendor/github.com/ulikunitz/xz/lzma/reader.go ++++ b/vendor/github.com/ulikunitz/xz/lzma/reader.go +@@ -70,7 +70,7 @@ func (c ReaderConfig) NewReader(lzma io.Reader) (r *Reader, err error) { + return nil, err + } + if r.h.dictCap < MinDictCap { +- return nil, errors.New("lzma: dictionary capacity too small") ++ r.h.dictCap = MinDictCap + } + dictCap := r.h.dictCap + if c.DictCap > dictCap { +-- +2.45.4 + + +From 9a5723d0810d91768139f32e0157f5a65c6d542c Mon Sep 17 00:00:00 2001 +From: Ulrich Kunitz +Date: Thu, 21 Aug 2025 17:57:47 +0200 +Subject: [PATCH 2/2] Address Security Issue GHSA-jc7w-c686-c4v9 + +This commit addresses security issue GHSA-jc7w-c686-c4v9. + +The mitigating measures are described for the Reader type and I added a +TestZeroPrefixIssue function to test the mitigations. + +// # Security concerns +// +// Note that LZMA format doesn't support a magic marker in the header. So +// [NewReader] cannot determine whether it reads the actual header. For instance +// the LZMA stream might have a zero byte in front of the reader, leading to +// larger dictionary sizes and file sizes. The code will detect later that there +// are problems with the stream, but the dictionary has already been allocated +// and this might consume a lot of memory. +// +// Version 0.5.14 introduces built-in mitigations: +// +// - The [ReaderConfig] DictCap field is now interpreted as a limit for the +// dictionary size. +// - The default is 2 Gigabytes (2^31 bytes). +// - Users can check with the [Reader.Header] method what the actual values are in +// their LZMA files and set a smaller limit using [ReaderConfig]. +// - The dictionary size doesn't exceed the larger of the file size and +// the minimum dictionary size. This is another measure to prevent huge +// memory allocations for the dictionary. +// - The code supports stream sizes only up to a pebibyte (1024^5). + +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: https://github.com/ulikunitz/xz/commit/4ce6f08566c86bf66a9bc1c2f811336ae2e462c0.patch https://github.com/ulikunitz/xz/commit/88ddf1d0d98d688db65de034f48960b2760d2ae2.patch +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: https://github.com/ulikunitz/xz/commit/4ce6f08566c86bf66a9bc1c2f811336ae2e462c0.patch https://github.com/ulikunitz/xz/commit/88ddf1d0d98d688db65de034f48960b2760d2ae2.patch +--- + vendor/github.com/ulikunitz/xz/TODO.md | 11 +- + vendor/github.com/ulikunitz/xz/lzma/header.go | 55 ++++---- + vendor/github.com/ulikunitz/xz/lzma/reader.go | 123 +++++++++++++++--- + vendor/github.com/ulikunitz/xz/lzma/writer.go | 30 ++--- + 4 files changed, 160 insertions(+), 59 deletions(-) + +diff --git a/vendor/github.com/ulikunitz/xz/TODO.md b/vendor/github.com/ulikunitz/xz/TODO.md +index 88c7341..35e9bc6 100644 +--- a/vendor/github.com/ulikunitz/xz/TODO.md ++++ b/vendor/github.com/ulikunitz/xz/TODO.md +@@ -1,8 +1,13 @@ + # TODO list + +-## Release v0.5.x +- +-1. Support check flag in gxz command. ++## Release v0.5.14 ++ ++* If the DictionarySize is larger than the UncompressedSize set it to ++ UncompressedSize ++* make a Header() (h Header, ok bool) function so the user can implement its own ++ policy ++* Add documentation to Reader to explain the situation ++* Add a TODO for the rewrite version + + ## Release v0.6 + +diff --git a/vendor/github.com/ulikunitz/xz/lzma/header.go b/vendor/github.com/ulikunitz/xz/lzma/header.go +index cda3946..096f9a4 100644 +--- a/vendor/github.com/ulikunitz/xz/lzma/header.go ++++ b/vendor/github.com/ulikunitz/xz/lzma/header.go +@@ -60,36 +60,36 @@ const noHeaderSize uint64 = 1<<64 - 1 + // HeaderLen provides the length of the LZMA file header. + const HeaderLen = 13 + +-// header represents the header of an LZMA file. +-type header struct { +- properties Properties +- dictCap int +- // uncompressed size; negative value if no size is given +- size int64 ++// Header represents the Header of an LZMA file. ++type Header struct { ++ Properties Properties ++ DictSize uint32 ++ // uncompressed Size; negative value if no Size is given ++ Size int64 + } + + // marshalBinary marshals the header. +-func (h *header) marshalBinary() (data []byte, err error) { +- if err = h.properties.verify(); err != nil { ++func (h *Header) marshalBinary() (data []byte, err error) { ++ if err = h.Properties.verify(); err != nil { + return nil, err + } +- if !(0 <= h.dictCap && int64(h.dictCap) <= MaxDictCap) { ++ if !(h.DictSize <= MaxDictCap) { + return nil, fmt.Errorf("lzma: DictCap %d out of range", +- h.dictCap) ++ h.DictSize) + } + + data = make([]byte, 13) + + // property byte +- data[0] = h.properties.Code() ++ data[0] = h.Properties.Code() + + // dictionary capacity +- putUint32LE(data[1:5], uint32(h.dictCap)) ++ putUint32LE(data[1:5], uint32(h.DictSize)) + + // uncompressed size + var s uint64 +- if h.size > 0 { +- s = uint64(h.size) ++ if h.Size > 0 { ++ s = uint64(h.Size) + } else { + s = noHeaderSize + } +@@ -99,20 +99,20 @@ func (h *header) marshalBinary() (data []byte, err error) { + } + + // unmarshalBinary unmarshals the header. +-func (h *header) unmarshalBinary(data []byte) error { ++func (h *Header) unmarshalBinary(data []byte) error { + if len(data) != HeaderLen { + return errors.New("lzma.unmarshalBinary: data has wrong length") + } + + // properties + var err error +- if h.properties, err = PropertiesForCode(data[0]); err != nil { ++ if h.Properties, err = PropertiesForCode(data[0]); err != nil { + return err + } + + // dictionary capacity +- h.dictCap = int(uint32LE(data[1:])) +- if h.dictCap < 0 { ++ h.DictSize = uint32LE(data[1:]) ++ if int(h.DictSize) < 0 { + return errors.New( + "LZMA header: dictionary capacity exceeds maximum " + + "integer") +@@ -121,10 +121,10 @@ func (h *header) unmarshalBinary(data []byte) error { + // uncompressed size + s := uint64LE(data[5:]) + if s == noHeaderSize { +- h.size = -1 ++ h.Size = -1 + } else { +- h.size = int64(s) +- if h.size < 0 { ++ h.Size = int64(s) ++ if h.Size < 0 { + return errors.New( + "LZMA header: uncompressed size " + + "out of int64 range") +@@ -134,9 +134,9 @@ func (h *header) unmarshalBinary(data []byte) error { + return nil + } + +-// validDictCap checks whether the dictionary capacity is correct. This ++// validDictSize checks whether the dictionary capacity is correct. This + // is used to weed out wrong file headers. +-func validDictCap(dictcap int) bool { ++func validDictSize(dictcap int) bool { + if int64(dictcap) == MaxDictCap { + return true + } +@@ -155,13 +155,16 @@ func validDictCap(dictcap int) bool { + // dictionary sizes of 2^n or 2^n+2^(n-1) with n >= 10 or 2^32-1. If + // there is an explicit size it must not exceed 256 GiB. The length of + // the data argument must be HeaderLen. ++// ++// This function should be disregarded because there is no guarantee that LZMA ++// files follow the constraints. + func ValidHeader(data []byte) bool { +- var h header ++ var h Header + if err := h.unmarshalBinary(data); err != nil { + return false + } +- if !validDictCap(h.dictCap) { ++ if !validDictSize(int(h.DictSize)) { + return false + } +- return h.size < 0 || h.size <= 1<<38 ++ return h.Size < 0 || h.Size <= 1<<38 + } +diff --git a/vendor/github.com/ulikunitz/xz/lzma/reader.go b/vendor/github.com/ulikunitz/xz/lzma/reader.go +index 2c3d073..4d2178b 100644 +--- a/vendor/github.com/ulikunitz/xz/lzma/reader.go ++++ b/vendor/github.com/ulikunitz/xz/lzma/reader.go +@@ -6,25 +6,32 @@ + // Reader and Writer support the classic LZMA format. Reader2 and + // Writer2 support the decoding and encoding of LZMA2 streams. + // +-// The package is written completely in Go and doesn't rely on any external ++// The package is written completely in Go and does not rely on any external + // library. + package lzma + + import ( + "errors" ++ "fmt" + "io" + ) + + // ReaderConfig stores the parameters for the reader of the classic LZMA + // format. + type ReaderConfig struct { ++ // Since v0.5.14 this parameter sets an upper limit for a .lzma file's ++ // dictionary size. This helps to mitigate problems with mangled ++ // headers. + DictCap int + } + + // fill converts the zero values of the configuration to the default values. + func (c *ReaderConfig) fill() { + if c.DictCap == 0 { +- c.DictCap = 8 * 1024 * 1024 ++ // set an upper limit of 2 GB for dictionary capacity to address ++ // the zero prefix security issue. ++ c.DictCap = 1 << 31 ++ // original: c.DictCap = 8 * 1024 * 1024 + } + } + +@@ -39,10 +46,33 @@ func (c *ReaderConfig) Verify() error { + } + + // Reader provides a reader for LZMA files or streams. ++// ++// # Security concerns ++// ++// Note that LZMA format doesn't support a magic marker in the header. So ++// [NewReader] cannot determine whether it reads the actual header. For instance ++// the LZMA stream might have a zero byte in front of the reader, leading to ++// larger dictionary sizes and file sizes. The code will detect later that there ++// are problems with the stream, but the dictionary has already been allocated ++// and this might consume a lot of memory. ++// ++// Version 0.5.14 introduces built-in mitigations: ++// ++// - The [ReaderConfig] DictCap field is now interpreted as a limit for the ++// dictionary size. ++// - The default is 2 Gigabytes (2^31 bytes). ++// - Users can check with the [Reader.Header] method what the actual values are in ++// their LZMA files and set a smaller limit using [ReaderConfig]. ++// - The dictionary size doesn't exceed the larger of the file size and ++// the minimum dictionary size. This is another measure to prevent huge ++// memory allocations for the dictionary. ++// - The code supports stream sizes only up to a pebibyte (1024^5). + type Reader struct { +- lzma io.Reader +- h header +- d *decoder ++ lzma io.Reader ++ header Header ++ // headerOrig stores the original header read from the stream. ++ headerOrig Header ++ d *decoder + } + + // NewReader creates a new reader for an LZMA stream using the classic +@@ -51,8 +81,37 @@ func NewReader(lzma io.Reader) (r *Reader, err error) { + return ReaderConfig{}.NewReader(lzma) + } + ++// ErrDictSize reports about an error of the dictionary size. ++type ErrDictSize struct { ++ ConfigDictCap int ++ HeaderDictSize uint32 ++ Message string ++} ++ ++// Error returns the error message. ++func (e *ErrDictSize) Error() string { ++ return e.Message ++} ++ ++func newErrDictSize(messageformat string, ++ configDictCap int, headerDictSize uint32, ++ args ...interface{}) *ErrDictSize { ++ newArgs := make([]interface{}, len(args)+2) ++ newArgs[0] = configDictCap ++ newArgs[1] = headerDictSize ++ copy(newArgs[2:], args) ++ return &ErrDictSize{ ++ ConfigDictCap: configDictCap, ++ HeaderDictSize: headerDictSize, ++ Message: fmt.Sprintf(messageformat, newArgs...), ++ } ++} ++ ++// We support only files not larger than 1 << 50 bytes (a pebibyte, 1024^5). ++const maxStreamSize = 1 << 50 ++ + // NewReader creates a new reader for an LZMA stream in the classic +-// format. The function reads and verifies the the header of the LZMA ++// format. The function reads and verifies the header of the LZMA + // stream. + func (c ReaderConfig) NewReader(lzma io.Reader) (r *Reader, err error) { + if err = c.Verify(); err != nil { +@@ -66,29 +125,63 @@ func (c ReaderConfig) NewReader(lzma io.Reader) (r *Reader, err error) { + return nil, err + } + r = &Reader{lzma: lzma} +- if err = r.h.unmarshalBinary(data); err != nil { ++ if err = r.header.unmarshalBinary(data); err != nil { + return nil, err + } +- if r.h.dictCap < MinDictCap { +- r.h.dictCap = MinDictCap ++ r.headerOrig = r.header ++ dictSize := int64(r.header.DictSize) ++ if int64(c.DictCap) < dictSize { ++ return nil, newErrDictSize( ++ "lzma: header dictionary size %[2]d exceeds configured dictionary capacity %[1]d", ++ c.DictCap, uint32(dictSize), ++ ) ++ } ++ if dictSize < MinDictCap { ++ dictSize = MinDictCap ++ } ++ // original code: disabled this because there is no point in increasing ++ // the dictionary above what is stated in the file. ++ /* ++ if int64(c.DictCap) > int64(dictSize) { ++ dictSize = int64(c.DictCap) ++ } ++ */ ++ size := r.header.Size ++ if size >= 0 && size < dictSize { ++ dictSize = size + } +- dictCap := r.h.dictCap +- if c.DictCap > dictCap { +- dictCap = c.DictCap ++ // Protect against modified or malicious headers. ++ if size > maxStreamSize { ++ return nil, fmt.Errorf( ++ "lzma: stream size %d exceeds a pebibyte (1024^5)", ++ size) + } ++ if dictSize < MinDictCap { ++ dictSize = MinDictCap ++ } ++ ++ r.header.DictSize = uint32(dictSize) + +- state := newState(r.h.properties) +- dict, err := newDecoderDict(dictCap) ++ state := newState(r.header.Properties) ++ dict, err := newDecoderDict(int(dictSize)) + if err != nil { + return nil, err + } +- r.d, err = newDecoder(ByteReader(lzma), state, dict, r.h.size) ++ r.d, err = newDecoder(ByteReader(lzma), state, dict, r.header.Size) + if err != nil { + return nil, err + } + return r, nil + } + ++// Header returns the header as read from the LZMA stream. It is intended to ++// allow the user to understand what parameters are typically provided in the ++// headers of the LZMA files and set the DictCap field in [ReaderConfig] ++// accordingly. ++func (r *Reader) Header() (h Header, ok bool) { ++ return r.headerOrig, r.d != nil ++} ++ + // EOSMarker indicates that an EOS marker has been encountered. + func (r *Reader) EOSMarker() bool { + return r.d.eosMarker +diff --git a/vendor/github.com/ulikunitz/xz/lzma/writer.go b/vendor/github.com/ulikunitz/xz/lzma/writer.go +index 5803ecc..2a13203 100644 +--- a/vendor/github.com/ulikunitz/xz/lzma/writer.go ++++ b/vendor/github.com/ulikunitz/xz/lzma/writer.go +@@ -13,7 +13,7 @@ import ( + // MinDictCap and MaxDictCap provide the range of supported dictionary + // capacities. + const ( +- MinDictCap = 1 << 12 ++ MinDictCap = 1 << 12 + MaxDictCap = 1<<32 - 1 + ) + +@@ -96,21 +96,21 @@ func (c *WriterConfig) Verify() error { + } + + // header returns the header structure for this configuration. +-func (c *WriterConfig) header() header { +- h := header{ +- properties: *c.Properties, +- dictCap: c.DictCap, +- size: -1, ++func (c *WriterConfig) header() Header { ++ h := Header{ ++ Properties: *c.Properties, ++ DictSize: uint32(c.DictCap), ++ Size: -1, + } + if c.SizeInHeader { +- h.size = c.Size ++ h.Size = c.Size + } + return h + } + + // Writer writes an LZMA stream in the classic format. + type Writer struct { +- h header ++ h Header + bw io.ByteWriter + buf *bufio.Writer + e *encoder +@@ -130,12 +130,12 @@ func (c WriterConfig) NewWriter(lzma io.Writer) (w *Writer, err error) { + w.buf = bufio.NewWriter(lzma) + w.bw = w.buf + } +- state := newState(w.h.properties) +- m, err := c.Matcher.new(w.h.dictCap) ++ state := newState(w.h.Properties) ++ m, err := c.Matcher.new(int(w.h.DictSize)) + if err != nil { + return nil, err + } +- dict, err := newEncoderDict(w.h.dictCap, c.BufSize, m) ++ dict, err := newEncoderDict(int(w.h.DictSize), c.BufSize, m) + if err != nil { + return nil, err + } +@@ -171,8 +171,8 @@ func (w *Writer) writeHeader() error { + + // Write puts data into the Writer. + func (w *Writer) Write(p []byte) (n int, err error) { +- if w.h.size >= 0 { +- m := w.h.size ++ if w.h.Size >= 0 { ++ m := w.h.Size + m -= w.e.Compressed() + int64(w.e.dict.Buffered()) + if m < 0 { + m = 0 +@@ -192,9 +192,9 @@ func (w *Writer) Write(p []byte) (n int, err error) { + // Close closes the writer stream. It ensures that all data from the + // buffer will be compressed and the LZMA stream will be finished. + func (w *Writer) Close() error { +- if w.h.size >= 0 { ++ if w.h.Size >= 0 { + n := w.e.Compressed() + int64(w.e.dict.Buffered()) +- if n != w.h.size { ++ if n != w.h.Size { + return errSize + } + } +-- +2.45.4 + diff --git a/SPECS/jx/jx.spec b/SPECS/jx/jx.spec index fd935795b9b..149801b9c7f 100644 --- a/SPECS/jx/jx.spec +++ b/SPECS/jx/jx.spec @@ -1,7 +1,7 @@ Summary: Command line tool for working with Jenkins X. Name: jx Version: 3.10.182 -Release: 1%{?dist} +Release: 2%{?dist} License: Apache-2.0 Vendor: Microsoft Corporation Distribution: Azure Linux @@ -27,6 +27,7 @@ Source0: https://github.com/jenkins-x/jx/archive/v%{version}.tar.gz#/%{na # See: https://reproducible-builds.org/docs/archives/ # - For the value of "--mtime" use the date "2021-04-26 00:00Z" to simplify future updates. Source1: %{name}-%{version}-vendor.tar.gz +Patch0: CVE-2025-58058.patch BuildRequires: golang >= 1.17.1 %global debug_package %{nil} @@ -62,6 +63,9 @@ install -p -m 755 -t %{buildroot}%{_bindir} ./build/jx %{_bindir}/jx %changelog +* Wed Sep 03 2025 Azure Linux Security Servicing Account - 3.10.182-2 +- Patch for CVE-2025-58058 + * Thu Feb 13 2025 CBL-Mariner Servicing Account - 3.10.182-1 - Auto-upgrade to 3.10.182 - Fix CVE-2023-39325 and CVE-2023-44487 in jx