alt-python39 (3.9.23-31) stable; urgency=medium

  * SECURITY UPDATE: tarfile ignored a filter's None ("skip this member")
    answer in the hard-link extraction fallback (CWE-59, link following). The
    guard CVE-2026-11940 added to TarFile.makelink_with_filter() re-runs the
    filter on the referenced member re-rooted at the link's own, shallower
    name before the fallback extracts it there, but it only reacted to the
    exceptions that extra call could raise and discarded its return value. A
    PEP 706 filter is also allowed to return None to mean "skip this member",
    and that answer was ignored: the second call, made with the member's own
    name, still succeeded and the member was written at the link's path
    anyway. A custom filter that downgrades FilterError to None - the
    documented way to skip unsafe members instead of aborting the whole
    extraction - therefore did not stop the escape CVE-2026-11940 was meant
    to close. Reproduced on the patched 3.9.23 tree with a two-member archive
    ('a/b/s' symlink -> '../escape', 'q' hard link -> 'a/b/s') extracted with
    a filter returning None on FilterError: before the fix dest/q is
    materialised as a symlink to '../escape', whose body points outside the
    destination; after it, 'q' is skipped and only a/b/s is extracted.
    - debian/patches/CVE-2026-87910.patch: backport of cpython
      9c17bace90f88dfba6d0e2fe23c8e7ae35f83955 (gh-157265, GH-157266,
      GH-157308, squashing the test follow-up GH-157334), which keeps the
      first filter call's result and returns early when it is None, plus its
      regression test test_extract_filters_target_none in
      Lib/test/test_tarfile.py. Applied after CVE-2026-11940.patch, whose
      guard it fixes; the two must not be separated or reordered.
    - CVE-2026-87910

  * SECURITY UPDATE: tarfile 'tar'/'data' extraction filters created
    directories outside the destination for a member whose name leaves the
    destination and comes back (CWE-22, path traversal). _get_filtered_attrs()
    checks containment by resolving the member name against the destination
    and comparing with os.path.commonpath(); a name such as
    '../evil/../dest/sub/file' resolves back inside and passes the check, but
    the intermediate directories are created from the name as given and walk
    out of the destination on the way. Extracting an untrusted archive into a
    scratch directory therefore left attacker-chosen directories next to it,
    under the very filters meant to prevent that. Reproduced on the patched
    3.9.23 tree with a one-member archive named
    '../escaped.evil/../dest/sub/file' extracted into outerdir/dest: before
    the fix outerdir/escaped.evil is created under both filter='data' and
    filter='tar'; after it nothing is created outside the destination and
    dest/sub/file still holds its content. POSIX only.
    - debian/patches/CVE-2026-19672.patch: backport of cpython
      97688346ada2df3e5b9c279348862c3d64ab0823 (gh-155999, GH-156000), which
      normalizes a member name containing a '..' component before the
      containment check so the directories actually created match the path
      that was validated, plus its regression test test_parent_dir_out_and_back
      in Lib/test/test_tarfile.py. Applied after CVE-2026-87910.patch.
    - Behaviour change: the 'tar' and 'data' filters now rewrite such a member
      name with os.path.normpath(), which removes internal '..' components and
      so may change the meaning of a name that traverses symbolic links.
    - CVE-2026-19672

 -- Juan Carlos Garcia <jgarcia@cloudlinux.com>  Tue, 22 Sep 2026 12:00:00 +0000

alt-python39 (3.9.23-30) stable; urgency=medium

  * SECURITY UPDATE: tarfile 'data'/'tar' extraction filter bypass via a hard
    link to a symbolic link (CWE-59, link following). TarFile.makelink_with_filter()
    passed tarinfo._link_target straight to os.link(); link(2) does not follow
    symbolic links, so an archive storing a hard link whose target is an
    archived symlink got the *same symlink inode* materialised one directory
    shallower than the symlink the filter had validated. Its relative body then
    re-based outside the destination directory, and the chmod()/utime() that
    extraction performs on the newly created name followed the link onto the
    outside file, changing its permissions and modification time; its contents
    also became readable through the in-tree path. Reproduced on the patched
    3.9.23 tree with a four-member archive ('x' regular decoy, 'sub/'
    directory, 'sub/link' -> '../x' symlink, 'escape' hard link to 'sub/link'
    with mode 0777 and mtime 946684800) extracted with filter='data': a file
    outside the destination went 0600 -> 0755, its mtime was rewritten to
    946684800 and its contents were readable through dest/escape. After the
    fix the outside file is untouched and dest/escape is a genuine in-tree hard
    link to the extracted decoy.
    - debian/patches/CVE-2026-82049.patch: backport of cpython
      b8f23e307097552eaea2604383a12ab280520d0d (gh-157190, GH-157191,
      GH-157192), which resolves the hard-link source with os.path.realpath()
      before calling os.link(), plus its regression test
      test_sneaky_hardlink_relocation in Lib/test/test_tarfile.py. This
      one-liner is sufficient only because CVE-2026-11940 is already applied:
      it closes the variant where the archive carries a decoy at the symlink's
      in-destination target so os.path.exists() is true and os.link() runs,
      while the no-decoy variant skips os.link() entirely and is closed by the
      guard CVE-2026-11940.patch added to makelink_with_filter(). The two must
      not be separated.
    - CVE-2026-82049

 -- Juan Carlos Garcia <jgarcia@cloudlinux.com>  Fri, 18 Sep 2026 12:00:00 +0000

alt-python39 (3.9.23-29) stable; urgency=medium

  * SECURITY UPDATE: complete the CVE-2026-18503 fix. 3.9.23-28 backported
    only cpython fd78b565 (gh-98820), which fixes the quoted-field probe in
    csv.Sniffer._guess_quote_and_delimiter(). The second super-linear regex
    in that function, the doubled-quote probe dq_regexp, was left untouched:
    it is unanchored, rescans leading whitespace and runs even when no
    delimiter was found, which is exponential on a sample carrying a run of
    quote characters. With 3.9.23-28 installed, csv.Sniffer().sniff() on
    '"",'*100 + '"'*100 + '0' + '"'*100 + '0', on '"a"\n' + ' '*100000 with
    delimiters=',;', and on '"a" "b"\n' + ' '*100000 each still runs
    unbounded inside a single C-level re call, so the stall does not even
    take signals.
    - debian/patches/CVE-2026-18503.patch: additionally backport cpython
      19e5024518 (gh-109638, GH-154868) in its upstream 3.10-branch form.
      Both commits are part of Python 3.10.21, the release the CVE record
      names as fixing the "0 to < 3.10.21" range. The doubled-quote probe is
      now anchored on whole fields, matches the field body as an unrolled
      loop that cannot backtrack, is skipped entirely when no delimiter was
      found, and does not rescan spaces when the delimiter is a space;
      Sniffer.sniff() normalises CRLF and CR to LF first so the probe's "$"
      means end of record. The upstream Misc/NEWS.d fragment and the five
      upstream regression tests are included.
    - CVE-2026-18503

 -- Juan Carlos Garcia <jgarcia@cloudlinux.com>  Mon, 31 Aug 2026 12:15:00 +0000

alt-python39 (3.9.23-28) stable; urgency=medium

  * SECURITY UPDATE: csv.Sniffer._guess_quote_and_delimiter() probed the
    sample with four dialect regexes whose quoted-field body was a lazy
    ".*?" compiled under re.DOTALL | re.MULTILINE. Because ".*?" is not
    anchored to the first undoubled quote, every candidate start position
    rescanned the sample to its end, so csv.Sniffer.sniff() on
    attacker-supplied CSV - classically a single column of quoted values -
    ran in quadratic time and could consume excessive CPU (CWE-1333,
    inefficient regular expression complexity).
    - debian/patches/CVE-2026-18503.patch: backport of cpython fd78b565
      (gh-98820). The quoted-field body is rebuilt as an unrolled loop,
      which is unambiguous and therefore does not backtrack. The 3.10
      variant is used because the 3.11+ form relies on a possessive
      quantifier that re in 3.9 cannot parse.
    - CVE-2026-18503

 -- Juan Carlos Garcia <jgarcia@cloudlinux.com>  Thu, 27 Aug 2026 06:50:00 +0000

alt-python39 (3.9.23-27) stable; urgency=medium

  * SECURITY UPDATE: CPython's SourcelessFileLoader read legacy .pyc files
    with a plain _io.FileIO instead of io.open_code(), because
    FileLoader.get_data() only routed SourceLoader and ExtensionFileLoader
    subclasses through open_code(). A PEP 578 open_code hook installed to vet
    code before it is executed was therefore not consulted when a sourceless
    .pyc was imported, letting code loaded that way escape such validation
    (CWE-668).
    - debian/patches/CVE-2026-2297.patch: backport of cpython a51b1b51
      (gh-145506). SourcelessFileLoader is added to the open_code() branch
      of FileLoader.get_data(). As in the upstream 3.10 backport 876858c9,
      the frozen Python/importlib_external.h is regenerated as well - the
      interpreter executes the frozen blob, not the .py file, and the
      Debian recipe does not run "make regen-all".
    - CVE-2026-2297
  * SECURITY UPDATE: xml.etree.ElementTree.Element.findall(), a fully
    consumed Element.iterfind(), and Element.find() when the first match
    sits near the end of the sibling list had O(n**2) time complexity for
    XPath index predicates ([1], [last()], [last()-N]), because the
    predicate re-ran parent.findall(tag) for every candidate sibling.
    Parsing an attacker-supplied XML document with many same-tag siblings
    could therefore consume excessive CPU time (CWE-407, inefficient
    algorithmic complexity).
    - debian/patches/CVE-2026-6879.patch: backport of cpython 2ffab083
      (gh-152674). The (parent, tag) lookup is cached so each sibling group
      is scanned once.
    - CVE-2026-6879

 -- Viktor Borzov <vborzov@cloudlinux.com>  Tue, 11 Aug 2026 02:48:09 -0400

alt-python39 (3.9.23-26) stable; urgency=medium

  * SECURITY UPDATE: TarFile.extract() did not forward the caller's filter to
    _extract_one(), so on the code path where a hardlink is extracted rather
    than linked the filter was silently dropped. An archive extracted with
    filter='data' could therefore end up creating files with an
    attacker-chosen uid/gid instead of the values the filter would have
    enforced (incorrect enforcement of an extraction filter).
    - debian/patches/CVE-2026-4360.patch: backport of cpython 7ccdbaba
      (gh-151987). extract() now passes filter_function through to
      _extract_one().
    - CVE-2026-4360

 -- Arseniy Zolotarev <azolotarev@cloudlinux.com>  Mon, 27 Jul 2026 13:58:00 +0000

alt-python39 (3.9.23-25) stable; urgency=medium

  * SECURITY UPDATE: tarfile.extractall() with the 'data' or 'tar' filter
    could be bypassed by a crafted archive where a hardlink references a
    symlink stored at a deeper name than the hardlink itself. The
    hardlink-extraction fallback validated the symlink at its archived
    location but recreated it at the hardlink's shallower path, so a relative
    target the filter judged contained could escape the destination
    directory, enabling out-of-destination file reads or writes (CWE-22: path
    traversal). This was an incomplete fix of CVE-2025-4330.
    - debian/patches/CVE-2026-11940.patch: backport of cpython be13e86f
      (gh-151558). makelink_with_filter() now also runs the filter against
      the link renamed to the hardlink's own path before creating it.
    - CVE-2026-11940
  * SECURITY UPDATE: writing configuration files with configparser where a
    multi-line value contained a carriage return (\r) emitted that CR
    verbatim, so an attacker controlling the value could inject unexpected
    keys and values into the resulting file (CWE-93: CRLF injection).
    - debian/patches/CVE-2026-0864.patch: backport of cpython 5858e42c
      (gh-143927). _write_section() now normalises CRLF and CR to LF before
      applying the existing newline indentation.
    - CVE-2026-0864
  * SECURITY UPDATE: reading a tar archive opened in streaming mode
    (mode="r|") did not handle EOF in _Stream.seek(), which kept reading the
    number of blocks declared in the member header even after the stream was
    exhausted, so a small archive declaring a huge member size made parsing
    take disproportionately long (CWE-400: uncontrolled resource
    consumption).
    - debian/patches/CVE-2026-11972.patch: backport of cpython f50bf135
      (gh-151981). The skip loop now breaks on an empty read.
    - CVE-2026-11972

 -- Arseniy Zolotarev <azolotarev@cloudlinux.com>  Mon, 27 Jul 2026 12:45:03 +0000

alt-python39 (3.9.23-24) stable; urgency=medium

  * SECURITY UPDATE: ftplib.ftpcp() passed the source server's self-reported
    PASV IPv4 address straight to the target server's PORT command, so a
    malicious source server could redirect the target's data connection to
    an arbitrary host and port (CWE-918: server-side request forgery).
    - debian/patches/CVE-2026-8328.patch: backport of cpython eac4fe3b
      (gh-87451). ftpcp() now uses the source socket's actual peer address,
      honouring the existing trust_server_pasv_ipv4_address opt-out the same
      way FTP.makepasv() already did after CVE-2021-4189.
    - CVE-2026-8328
  * SECURITY UPDATE: tarfile's data filter could be bypassed with crafted
    link members - symlinks whose name is empty or ends in a separator, and
    links resolving to the destination directory itself - letting a
    malicious archive redirect later members and make extractall() write
    outside the destination directory (CWE-22: path traversal).
    - debian/patches/CVE-2026-7774.patch: backport of cpython c063191c
      (gh-149486). The filter now rejects a link that resolves to the
      destination directory, and computes the link target from the
      normalised linkname and the separator stripped parent directory.
    - CVE-2026-7774
  * SECURITY UPDATE: unicodedata.normalize() sorted runs of combining
    characters with an O(n^2) insertion sort, so input with long runs of
    alternating canonical combining classes consumed excessive CPU in every
    normalization form (CWE-407: inefficient algorithmic complexity).
    - debian/patches/CVE-2026-3276.patch: backport of cpython 991224b1
      (gh-149079) and its follow-up 90748760. Canonical ordering now uses
      insertion sort for short runs and counting sort for long ones, and
      skips runs that are already ordered.
    - CVE-2026-3276

 -- Arseniy Zolotarev <azolotarev@cloudlinux.com>  Mon, 27 Jul 2026 08:10:17 -0400

alt-python39 (3.9.23-23) stable; urgency=medium

  * SECURITY UPDATE: base64.b64decode() and urlsafe_b64decode() always
    accepted the standard-alphabet '+' and '/' characters even when an
    alternative alphabet excluding them was specified via altchars, so
    malformed input could bypass strict-alphabet validation filters
    (CWE-704: incorrect type conversion or cast, per NVD).
    - debian/patches/CVE-2025-12781.patch: backport of cpython 9060b4ab
      (gh-125346, PR gh-141128; adapted from the reviewed alt-python37
      backport). Emits DeprecationWarning/FutureWarning when '+' or '/'
      appear outside the alternative alphabet; decoded output unchanged.
    - CVE-2025-12781

 -- Viktor Borzov <vborzov@cloudlinux.com>  Fri, 24 Jul 2026 08:10:15 -0400

alt-python39 (3.9.23-22) stable; urgency=medium

  * SECURITY UPDATE: html.parser.HTMLParser incremental parsing could be
    driven into quadratic-time CPU consumption. Each feed() concatenated new
    data onto the unparsed buffer and re-scanned it from the start, so feeding
    a long unterminated construct (tag, comment, declaration, PI, CDATA) in
    many small chunks was quadratic in the input size — a denial of service
    (CWE-407).
    - debian/patches/CVE-2026-15308.patch: backport of cpython bcf98ddbc4
      (gh-153030, PR gh-153031; adapted from the 3.13 backport 7933f4bf71).
      feed() now accumulates incoming data in a list and only joins and
      parses it once the buffer has doubled, and close() flushes any pending
      data before the final scan.
    - CVE-2026-15308

 -- Juan Carlos Garcia <jgarcia@cloudlinux.com>  Mon, 20 Jul 2026 12:00:00 +0200

alt-python39 (3.9.23-21) stable; urgency=medium

  * Rebuild only: bump the Debian release so amd64 and arm64 are rebuilt
    and released together as a single build. No source changes.

 -- Lizi Ekseulidze <lekseulidze@cloudlinux.com>  Thu, 16 Jul 2026 13:15:10 +0400

alt-python39 (3.9.23-20) stable; urgency=medium

  * Fix test_xml_etree/test_xml_etree_c build-time test failures on
    Ubuntu 22.04 (jammy): its system libexpat 2.4.7-1ubuntu0.7 backports
    expat 2.6 reparse deferral without bumping pyexpat.version_info, so the
    version-gated tests take the pre-2.6 branch and assert non-deferred
    parsing, which no longer holds.
    - debian/rules: on Ubuntu 22.04 (jammy) only, apply
      debian/patches/06002-skip_test_flush_reparse_deferral_disabled.patch
      so the jammy deb build skips test_simple_xml_chunk_1,
      test_simple_xml_chunk_5 and test_flush_reparse_deferral_disabled (and
      the test_sax/test_pyexpat equivalents). bionic/noble are unaffected.

 -- Lizi Ekseulidze <lekseulidze@cloudlinux.com>  Tue, 07 Jul 2026 01:44:30 +0400

alt-python39 (3.9.23-19) stable; urgency=medium

  * SECURITY UPDATE: bz2.BZ2Decompressor could be reused after a
    decompression error. libbz2 leaves the bz_stream in an inconsistent
    state after reporting an error, so re-entering BZ2_bzDecompress() on
    that stream could write past the output buffer, a stack buffer
    overflow (CWE-121).
    - debian/patches/CVE-2026-9669.patch: backport of cpython 5755d0f083
      (gh-150599). Record the libbz2 error code in a new BZ2Decompressor
      bzerror field, clear needs_input on error, and raise
      ValueError("Decompressor is unusable after a previous error") on any
      subsequent decompress() call.
    - CVE-2026-9669

 -- Juan Carlos Garcia <jgarcia@cloudlinux.com>  Mon, 23 Jun 2026 12:00:00 +0200

alt-python39 (3.9.23-18) stable; urgency=medium

  * SECURITY UPDATE: imaplib.IMAP4._command() concatenated command
    arguments without rejecting control characters, allowing IMAP command
    injection via CR/LF in a user-controlled argument.
    - debian/patches/CVE-2025-15366.patch: backport of cpython 6262704b13
      (gh-143921). Add the _control_chars guard and raise ValueError on any
      argument byte in [\x00-\x1F\x7F].
    - CVE-2025-15366
  * SECURITY UPDATE: poplib.POP3._putcmd() wrote command lines without
    rejecting control characters, allowing POP3 command injection via CR/LF
    in a user-controlled argument.
    - debian/patches/CVE-2025-15367.patch: backport of cpython b234a2b675
      (gh-143923). Reject any line byte in [\x00-\x1F\x7F] with ValueError.
    - CVE-2025-15367

 -- Juan Carlos Garcia <jgarcia@cloudlinux.com>  Sun, 08 Jun 2026 12:00:00 +0200

alt-python39 (3.9.23-17) stable; urgency=medium

  * SECURITY UPDATE: tarfile normalized AREGTYPE blocks to DIRTYPE while
    processing GNU long name/link follow-up headers, allowing a crafted tar
    archive to be misinterpreted.
    - debian/patches/CVE-2025-13462.patch: backport of cpython 42d754e34c
      (gh-141707). Skip DIRTYPE normalization on follow-up headers via a
      dircheck flag.
    - CVE-2025-13462
  * SECURITY UPDATE: wsgiref.headers.Headers accepted C0 control characters
    in header names, values and parameters, enabling response splitting.
    - debian/patches/CVE-2026-0865.patch: backport of cpython f7fceed79c
      (gh-143916) plus the HTAB follow-up. Reject control characters; HTAB
      remains allowed in values but not names.
    - CVE-2026-0865
  * SECURITY UPDATE: http.client did not reject CR/LF in HTTP tunnel
    (CONNECT) request headers set via HTTPConnection.set_tunnel().
    - debian/patches/CVE-2026-1502.patch: backport of cpython 05ed7ce7ae
      (gh-146211). Validate the tunnel host and per-header name/value.
    - CVE-2026-1502
  * SECURITY UPDATE: http.cookies Morsel.js_output() emitted cookie values
    into a document.cookie assignment using only quote-escaping, allowing
    a </script> breakout / JavaScript injection.
    - debian/patches/CVE-2026-6019.patch: backport of cpython 76b3923d68
      (gh-90309). Base64-encode the value and wrap it in atob().
    - CVE-2026-6019

 -- Juan Carlos Garcia <jgarcia@cloudlinux.com>  Thu, 28 May 2026 11:15:00 +0200

alt-python39 (3.9.23-16) stable; urgency=medium

  * SECURITY UPDATE: email address-list folding encoded the separating
    comma when it ended up on a folded, unicode-encoded line, letting some
    mail servers misinterpret the address header.
    - debian/patches/CVE-2025-1795.patch: backport of cpython 09fab93c3d
      (gh-100884). Mark the shared ListSeparator terminal as_ew_allowed=False
      and reuse it in get_address_list().
    - CVE-2025-1795
  * SECURITY UPDATE: quadratic complexity in html.parser.HTMLParser allowed
    a denial of service via specially crafted incomplete markup.
    - debian/patches/CVE-2025-6069.patch: backport of cpython 6eb6c5dbfb
      (gh-135462). Rewrite goahead() EOF handling for tags, comments,
      declarations and processing instructions.
    - CVE-2025-6069
  * SECURITY UPDATE: zipfile did not validate the ZIP64 end-of-central-
    directory record and locator, so a crafted archive could be
    misinterpreted.
    - debian/patches/CVE-2025-8291.patch: backport of cpython 162997bb70
      (gh-139700). Validate the zip64 EOCD record/locator consistency.
    - CVE-2025-8291
  * SECURITY UPDATE: header injection via folded comments in the email
    module.
    - debian/patches/CVE-2025-11468.patch: backport of cpython 17d1490aa9
      (gh-143935). Escape parentheses/backslashes in folded comments and
      fix WhiteSpaceTerminal.startswith_fws().
    - CVE-2025-11468

 -- Juan Carlos Garcia <jgarcia@cloudlinux.com>  Thu, 28 May 2026 11:05:00 +0200

alt-python39 (3.9.23-15) stable; urgency=medium

  * SECURITY UPDATE: xml.parsers.expat / xml.etree.ElementTree used
    insufficient entropy for libexpat hash-flooding protection, allowing a
    crafted XML document to trigger hash collisions. Mitigation requires
    both libexpat 2.8.0+ (or a distro-backported equivalent that exports
    XML_SetHashSalt16Bytes) and a Python-side patch to seed the parser
    with the new 16-byte salt API.
    - debian/patches/CVE-2026-7210.patch: backport of cpython
      24b8f12544 (gh-149018, Stan Ulbrych). pyexpat and _elementtree call
      XML_SetHashSalt16Bytes with _Py_HashSecret.expat.hashsalt16 (16
      bytes of entropy); legacy XML_SetHashSalt remains as the fallback
      when the loaded libexpat does not export the new symbol. The
      symbol is declared __attribute__((weak)) in Modules/pyexpat.c so
      the same source path works whether the build links against bundled
      libexpat 2.8.0+ or a distro libexpat 2.5/2.7 that backports the
      entropy fix without bumping XML_COMBINED_VERSION (Debian, Ubuntu,
      RHEL/CL, Alpine). Extends the PyExpat CAPI with a nullable
      SetHashSalt16Bytes slot populated from the weak reference.
    - CVE-2026-7210

 -- Juan Carlos Garcia <jgarcia@cloudlinux.com>  Thu, 28 May 2026 10:36:38 +0200

alt-python39 (3.9.23-14) stable; urgency=medium

  * SECURITY UPDATE: urllib.request.DataHandler accepted data: URLs whose
    mediatype contained control characters, allowing newline-based HTTP
    header injection downstream.
    - debian/patches/CVE-2025-15282.patch: backport of cpython
      f25509e78e (gh-143925, Seth Larson). Adds a [\\x00-\\x1F\\x7F]
      regex check in data_open() and a matching test_invalid_mediatype.
    - CVE-2025-15282

  * SECURITY UPDATE: http.cookies.Morsel did not reject control characters
    in keys / values / coded_value, allowing cookie injection via
    __setitem__, setdefault, set, and BaseCookie.output.
    - debian/patches/CVE-2026-0672.patch: backport of cpython
      95746b3a13 (gh-143919, Seth Larson). Adds _has_control_character
      helper and inserts validation in __setitem__, setdefault, set,
      plus a wrap of BaseCookie.OutputString / output.
    - CVE-2026-0672

  * SECURITY UPDATE: the CVE-2026-0672 fix was incomplete; control
    characters could still bypass via Morsel.update(), |=, __setstate__
    (pickle), and BaseCookie.js_output().
    - debian/patches/CVE-2026-3644.patch: backport of cpython
      57e88c1cf9 (gh-145599, Stan Ulbrych + Victor Stinner). Adds
      validation to Morsel.update(), defines explicit Morsel.__ior__
      (was inherited from dict and bypassed validation), validates
      __setstate__ before assigning attributes, and re-validates the
      assembled output string in js_output().
    - CVE-2026-3644

  * SECURITY UPDATE: Modules/pyexpat.c conv_content_model could overflow
    the C stack when an Expat parser with a registered ElementDeclHandler
    parsed a deeply nested DTD content model, causing a denial-of-service.
    - debian/patches/CVE-2026-4224.patch: backport of cpython
      eb0e8be3a7 (gh-145986, Stan Ulbrych + Bénédikt Tran). Wraps
      conv_content_model with Py_EnterRecursiveCall / Py_LeaveRecursiveCall
      so deep nesting raises RecursionError instead of crashing.
    - CVE-2026-4224

 -- Juan Carlos Garcia <jgarcia@cloudlinux.com>  Mon, 11 May 2026 08:22:00 +0200

alt-python39 (3.9.23-13) stable; urgency=medium

  * SECURITY UPDATE: binascii.a2b_base64 / base64.b64decode stop decoding
    after the first padded quad, silently dropping any excess data. The
    behaviour can lead to data being accepted that other implementations
    process differently.
    - debian/patches/CVE-2026-3446.patch: backport of upstream commits
      4561f6418a (main), e31c55121620 (3.14), 1f9958f909c1 (3.13). Treats
      the pad character as non-alphabet data per RFC 4648 section 3.3:
      the loop in binascii_a2b_base64_impl no longer breaks out on a pad
      sequence; a `pads` counter tracks them so post-loop validation
      still raises "Incorrect padding" / "Invalid base64-encoded string"
      for inputs that do not satisfy `quad_pos + pads == 4`. The unused
      `done:` label is removed.
    - CVE-2026-3446

 -- Juan Carlos Garcia <jgarcia@cloudlinux.com>  Mon, 04 May 2026 09:30:00 +0200

alt-python39 (3.9.23-12) stable; urgency=medium

  * SECURITY UPDATE: email.BytesGenerator did not quote newlines in
    serialized headers, allowing header injection when a custom header
    class (e.g. LiteralHeader) bypasses the email folding rules. This is
    a bypass of CVE-2024-6923, which only added the validation to the
    text Generator class.
    - debian/patches/CVE-2026-1299.patch: mirror the
      verify_generated_headers / NEWLINE_WITHOUT_FWSP check from
      Generator._write_headers into BytesGenerator._write_headers in
      Lib/email/generator.py, raising HeaderWriteError on unsafely
      folded or delimited headers; extend test_email tests to cover
      message.as_bytes().
    - CVE-2026-1299

 -- Juan Carlos Garcia <jgarcia@cloudlinux.com>  Wed, 29 Apr 2026 09:30:00 +0200

alt-python39 (3.9.23-11) stable; urgency=medium

  * SECURITY UPDATE: use-after-free in lzma/bz2/gzip decompressors
    - debian/patches/CVE-2026-6100.patch: null next_in at the error:
      label of decompress() in Modules/_bz2module.c and
      Modules/_lzmamodule.c so the decompressor cannot be re-used
      with a stale buffer pointer after a MemoryError.
    - CVE-2026-6100

 -- Juan Carlos Garcia <jgarcia@cloudlinux.com>  Mon, 27 Apr 2026 12:00:00 +0200

alt-python39 (3.9.23-10) stable; urgency=medium

  * SECURITY UPDATE: webbrowser.open() accepts leading dashes in URLs which
    could be interpreted as command-line options by web browsers
    - debian/patches/CVE-2026-4519.patch: reject URLs starting with dashes
      in BaseBrowser._check_url() before passing to subprocess
    - CVE-2026-4519

 -- Juan Carlos Garcia <jgarcia@cloudlinux.com>  Tue, 14 Apr 2026 11:30:00 +0200

alt-python39 (3.9.23-9) stable; urgency=medium

  * SECURITY UPDATE: defect in 'tarfile' module leads to infinite loop and
    deadlock in parsing of maliciously crafted tar archives
    - debian/patches/CVE-2025-8194.patch: Validate archives to ensure member
      offsets are non-negative
    - CVE-2025-8194

 -- Juan Carlos García <jgarcia@cloudlinux.com>  Fri, 27 Feb 2026 10:38:00 +0100

alt-python39 (3.9.23-8) stable; urgency=medium

  * SECURITY UPDATE: Quadratic complexity in os.path.expandvars()
    - debian/patches/CVE-2025-6075.patch: fix quadratic complexity in
      os.path.expandvars() by replacing the character-by-character loop
      with regex-based substitution in both posixpath and ntpath modules.
    - CVE-2025-6075

 -- Juan Carlos García <jgarcia@cloudlinux.com>  Mon, 23 Feb 2026 10:33:00 +0100

alt-python39 (3.9.23-7) stable; urgency=medium

  * Fix arm64 build: use multiarch SOABI suffix on all architectures
  * Fix hardcoded x86_64 in debug package config filename for arm64
  * Fix rm -rf __pycache__ failure under fakeroot on Debian 12
  * Use dh_missing --fail-missing instead of manual unpackaged files check

 -- Levon Matshkalyan <lmatshkalyan@cloudlinux.com>  Sun, 15 Feb 2026 15:00:00 +0400

alt-python39 (3.9.23-6) stable; urgency=medium

  * SECURITY UPDATE: Memory denial of service in plistlib
    - debian/patches/CVE-2025-13837.patch: read large data by chunks,
      therefore the upper limit of consumed memory is proportional to the
      size of the input file.
    - CVE-2025-13837

 -- Juan Carlos García <jgarcia@cloudlinux.com>  Thu, 12 Feb 2026 08:04:42 +0100

alt-python39 (3.9.23-5) stable; urgency=medium

  * ALTPYTH-586: Add libnsl-dev runtime dependency for libs on Debian 13+

 -- Levon Matshkalyan <lmatshkalyan@cloudlinux.com>  Tue, 11 Feb 2026 15:00:00 +0400

alt-python39 (3.9.23-4) stable; urgency=medium

  * SECURITY UPDATE: Quadratic complexity in xml.minidom node ID cache
    clearing
    - debian/patches/CVE-2025-12084.patch: remove quadratic behavior in
      xml.minidom node ID cache clearing
    - CVE-2025-12084

 -- Juan Carlos García <jgarcia@cloudlinux.com>  Fri, 09 Jan 2026 11:35:00 +0100

alt-python39 (3.9.23-3) stable; urgency=medium

  * ALTPYTH-581: Build for Debian 13.

 -- Eduard Chiganov <echiganov@cloudlinux.com>  Mon, 01 Dec 2025 14:20:00 +0200

alt-python39 (3.9.23-1) stable; urgency=medium

  * ALTPYTH-558: Update to 3.9.23 version

 -- CloudLinux Packaging Team <packager@cloudlinux.com>  Wed, 04 Jun 2025 00:23:28 +0000

alt-python39 (3.9.22-1) stable; urgency=medium

  * ALTPYTH-553: Update to 3.9.22 version

 -- CloudLinux Packaging Team <packager@cloudlinux.com>  Wed, 09 Apr 2025 00:24:05 +0000

alt-python39 (3.9.21-1) stable; urgency=medium

  * ALTPYTH-543: Update to 3.9.21 version

 -- CloudLinux Packaging Team <packager@cloudlinux.com>  Wed, 04 Dec 2024 00:24:04 +0000

alt-python39 (3.9.20-1) stable; urgency=medium

  * Update to 3.9.20 version

 -- CloudLinux Packaging Team <packager@cloudlinux.com>  Sat, 07 Sep 2024 00:22:57 +0000

alt-python39 (3.9.19-3) stable; urgency=medium

  * ALTPYTH-518: Resolve conflicts with idle3.appdata.xml with other alt-pythonXX-idle packages

 -- Ilya Voyskovsky <ivoyskovsky@cloudlinux.com>  Thu, 25 Jul 2024 17:03:00 +0200   
 
alt-python39 (3.9.19-2) stable; urgency=medium

  * ALTPYTH-509: Always use alt-sqlite

 -- Ilya Voyskovsky <ivoyskovsky@cloudlinux.com>  Wed, 10 Apr 2024 15:05:00 +0200  
 
alt-python39 (3.9.19-1) stable; urgency=medium

  * ALTPYTH-510: Update to 3.9.19 version

 -- CloudLinux Packaging Team <packager@cloudlinux.com>  Tue, 19 Mar 2024 18:23:03 +0000

alt-python39 (3.9.18-1) stable; urgency=medium

  * ALTPYTH-476: Build missing alt-python packages for CL ubuntu.
  * update to 3.9.18

 -- Eduard Chiganov <echiganov@cloudlinux.com>  Mon, 21 Aug 2023 14:20:00 +0200

alt-python39 (3.9.16-1) stable; urgency=medium

  * update to 3.9.16

 -- Ilya Voyskovsky <ivoyskovsky@cloudlinux.com>  Mon, 30 Jan 2023 17:00:00 +0200

alt-python39 (3.9.15-1) stable; urgency=medium

  * update to 3.9.15
  * ALTPYTH-443: CL9 compatibility
  * Stripped debug symbols

 -- Ilya Voyskovsky <ivoyskovsky@cloudlinux.com>  Fri, 21 Oct 2022 17:08:00 +0200

alt-python39 (3.9.12-1) stable; urgency=medium

  * update to 3.9.12
  * ALTPYTH-417: fix python-wheels path

 -- Ilya Voyskovsky <ivoyskovsky@cloudlinux.com>  Wed, 06 Apr 2022 10:08:00 +0200

alt-python39 (3.9.10-1) stable; urgency=medium

  * update to 3.9.10

 -- Sergey Fokin <sfokin@cloudlinux.com>  Tue, 01 Feb 2022 10:46:00 +0300

alt-python39 (3.9.9-1) stable; urgency=medium

  * ALTPYTH-397: Fix the path of _WHEEL_DIR in alt-python 3.9
  * ALTPYTH-402: update to 3.9.9

 -- Sergey Fokin <sfokin@cloudlinux.com>  Mon, 17 Jan 2022 16:43:00 +0300

alt-python39 (3.9.7-1) stable; urgency=medium

  * initial build

 -- Sergey Fokin <sfokin@cloudlinux.com>  Fri, 27 Oct 2021 11:27:00 +0300
