diff --git a/content/posts/python-31022-31117/index.md b/content/posts/python-31022-31117/index.md index 59d8d62..9fb01d6 100644 --- a/content/posts/python-31022-31117/index.md +++ b/content/posts/python-31022-31117/index.md @@ -30,29 +30,29 @@ Python 3.12.15 is also a source-only security release, with security support con ## Security content in all five releases -* [gh-158446](https://github.com/python/cpython/issues/158446): Fix crashes or incorrect output when formatting float or complex values with precision close to INT_MAX. -* [CVE-2026-19553](https://www.cve.org/CVERecord?id=CVE-2026-19553) — [gh-156793](https://github.com/python/cpython/issues/156793): `ssl.SSLContext.wrap_bio()` now validates its `server_side`, `server_hostname`, and `session` arguments. `asyncio` also validates TLS `server_hostname` arguments. On Python 3.10, 3.11, and 3.12, missing hostnames with `check_hostname` enabled emit `DeprecationWarning` for compatibility; they raise `ValueError` on Python 3.13 and later. -* [CVE-2026-82049](https://www.cve.org/CVERecord?id=CVE-2026-82049) — [gh-157190](https://github.com/python/cpython/issues/157190): Fix a tarfile extraction-filter vulnerability involving hard links to symbolic links that could expose files outside the destination and change their permissions or modification times. -* [gh-157953](https://github.com/python/cpython/issues/157953): Update bundled libexpat to version 2.8.5. -* [CVE-2026-15310](https://www.cve.org/CVERecord?id=CVE-2026-15310) — [gh-156002](https://github.com/python/cpython/issues/156002): Bound `zipfile` decompression per read for bzip2 and LZMA members, and for Zstandard members on Python 3.14, preventing unbounded allocations from small compressed members. Third-party decompressors supplied by monkey-patching `_get_decompressor()` that lack `needs_input` and two-argument `decompress()` remain vulnerable. -* [CVE-2026-19672](https://www.cve.org/CVERecord?id=CVE-2026-19672) — [gh-155999](https://github.com/python/cpython/issues/155999): Prevent tarfile extraction filters from creating directories outside the destination for paths that leave it and then return. -* [CVE-2026-19445](https://www.cve.org/CVERecord?id=CVE-2026-19445) — [gh-156293](https://github.com/python/cpython/issues/156293): Fix an ssl crash when an SNI callback switches contexts and the original callback context is no longer referenced. -* [CVE-2026-17084](https://www.cve.org/CVERecord?id=CVE-2026-17084) — [gh-155292](https://github.com/python/cpython/issues/155292): Restrict stringprep and the IDNA codec to Unicode codepoint attributes defined by RFC 3454. -* [CVE-2026-15806](https://www.cve.org/CVERecord?id=CVE-2026-15806) — [gh-155694](https://github.com/python/cpython/issues/155694): Scope urllib.request HTTPPasswordMgr credentials by URL scheme to prevent HTTPS credentials from being used for matching HTTP URLs. +* gh-158446: Fix crashes or incorrect output when formatting float or complex values with precision close to INT_MAX. +* CVE-2026-19553 — gh-156793: `ssl.SSLContext.wrap_bio()` now validates its `server_side`, `server_hostname`, and `session` arguments. `asyncio` also validates TLS `server_hostname` arguments. On Python 3.10, 3.11, and 3.12, missing hostnames with `check_hostname` enabled emit `DeprecationWarning` for compatibility; they raise `ValueError` on Python 3.13 and later. +* CVE-2026-82049 — gh-157190: Fix a tarfile extraction-filter vulnerability involving hard links to symbolic links that could expose files outside the destination and change their permissions or modification times. +* gh-157953: Update bundled libexpat to version 2.8.5. +* CVE-2026-15310 — gh-156002: Bound `zipfile` decompression per read for bzip2 and LZMA members, and for Zstandard members on Python 3.14, preventing unbounded allocations from small compressed members. Third-party decompressors supplied by monkey-patching `_get_decompressor()` that lack `needs_input` and two-argument `decompress()` remain vulnerable. +* CVE-2026-19672 — gh-155999: Prevent tarfile extraction filters from creating directories outside the destination for paths that leave it and then return. +* CVE-2026-19445 — gh-156293: Fix an ssl crash when an SNI callback switches contexts and the original callback context is no longer referenced. +* CVE-2026-17084 — gh-155292: Restrict stringprep and the IDNA codec to Unicode codepoint attributes defined by RFC 3454. +* CVE-2026-15806 — gh-155694: Scope urllib.request HTTPPasswordMgr credentials by URL scheme to prevent HTTPS credentials from being used for matching HTTP URLs. ## Additional security content by version ### Python 3.10.22, 3.11.17, 3.12.15 and 3.13.16 -* [CVE-2026-87910](https://www.cve.org/CVERecord?id=CVE-2026-87910) — [gh-157265](https://github.com/python/cpython/issues/157265): Apply tarfile extraction filters when a link falls back to extracting an archive member, skipping members rejected by the filter. +* CVE-2026-87910 — gh-157265: Apply tarfile extraction filters when a link falls back to extracting an archive member, skipping members rejected by the filter. ### Python 3.13.16 and 3.14.8 -* [gh-158010](https://github.com/python/cpython/issues/158010): Update bundled OpenSSL to [3.5.9](https://openssl-library.org/news/secadv/20260929.txt). In Python 3.13.16 this applies to Windows, macOS and Android, moving from OpenSSL 3.0.21 to the 3.5 LTS series. In Python 3.14.8 it applies to Windows, macOS, Android and iOS. The source-only Python 3.10–3.12 releases do not bundle OpenSSL. +* gh-158010: Update bundled OpenSSL to [3.5.9](https://openssl-library.org/news/secadv/20260929.txt). In Python 3.13.16 this applies to Windows, macOS and Android, moving from OpenSSL 3.0.21 to the 3.5 LTS series. In Python 3.14.8 it applies to Windows, macOS, Android and iOS. The source-only Python 3.10–3.12 releases do not bundle OpenSSL. ### Python 3.10.22 -* [gh-149018](https://github.com/python/cpython/issues/149018): Improve protection against XML hash-flooding attacks in `xml.parsers.expat` and `xml.etree.ElementTree` when compiled with libexpat 2.8.0 or later. +* gh-149018: Improve protection against XML hash-flooding attacks in `xml.parsers.expat` and `xml.etree.ElementTree` when compiled with libexpat 2.8.0 or later. This XML hash-flooding protection was already included in Python 3.11.16. diff --git a/src/layouts/BlogPostLayout.astro b/src/layouts/BlogPostLayout.astro index a6ed15c..b5d2272 100644 --- a/src/layouts/BlogPostLayout.astro +++ b/src/layouts/BlogPostLayout.astro @@ -37,6 +37,7 @@ const groupMeta: Record = { "gh-repo": { label: "Repositories", order: 2 }, "gh-user": { label: "People", order: 3 }, "pypi": { label: "Packages", order: 4 }, + "cve": { label: "Security", order: 5 }, }; const sortedGroups = [...refGroups.entries()] @@ -195,6 +196,7 @@ const sortedGroups = [...refGroups.entries()] type === "gh-repo" && "bg-zinc-100 text-zinc-700 hover:bg-zinc-200 dark:bg-zinc-800 dark:text-zinc-300 dark:hover:bg-zinc-700", type === "gh-user" && "bg-zinc-100 text-zinc-700 hover:bg-zinc-200 dark:bg-zinc-800 dark:text-zinc-300 dark:hover:bg-zinc-700", type === "pypi" && "bg-emerald-50 text-emerald-700 hover:bg-emerald-100 dark:bg-emerald-900/20 dark:text-emerald-300 dark:hover:bg-emerald-900/40", + type === "cve" && "bg-rose-50 text-rose-700 hover:bg-rose-100 dark:bg-rose-900/20 dark:text-rose-300 dark:hover:bg-rose-900/40", ]} target="_blank" rel="noopener noreferrer" diff --git a/src/plugins/remark-python-refs.ts b/src/plugins/remark-python-refs.ts index de7713a..04351e7 100644 --- a/src/plugins/remark-python-refs.ts +++ b/src/plugins/remark-python-refs.ts @@ -13,9 +13,12 @@ * - GitHub users/orgs (github.com/NAME — exactly 1 segment, not reserved) * - CVE references (nvd.nist.gov/vuln/detail/CVE-YYYY-NNNNN) * - Python releases (python.org/downloads/release/python-XXXX/) + * + * Bare "gh-NNNN" and "CVE-YYYY-NNNN" text (not already inside a link + * or heading) is autolinked and rendered as a badge. */ import type { Root, Link, Paragraph, PhrasingContent } from "mdast"; -import { visit } from "unist-util-visit"; +import { SKIP, visit } from "unist-util-visit"; import { pythonIcon, docsIcon, @@ -35,6 +38,14 @@ const DOCS = /^https?:\/\/docs\.python\.org\//i; const PYPI = /^https?:\/\/pypi\.org\/project\/([^/]+)\/?/i; const GH_ISSUE = /^https?:\/\/github\.com\/([\w.-]+)\/([\w.-]+)\/(issues|pull)\/(\d+)\/?/i; const CVE = /^https?:\/\/nvd\.nist\.gov\/vuln\/detail\/(CVE-[\d-]+)\/?/i; + +/** + * Bare references in plain text that get autolinked (outside links, + * headings and code): + * - "gh-156293" → python/cpython issue + * - "CVE-2026-19445" → cve.org record + */ +const BARE_REF = /\b(?:(CVE-\d{4}-\d{4,})|gh-(\d+))\b/g; const PY_RELEASE = /^https?:\/\/(?:www\.)?python\.org\/downloads\/release\/(python-[\w.]+)\/?/i; const GITHUB = /^https?:\/\/github\.com\/([\w.-]+)(?:\/([\w.-]+))?\/?$/i; @@ -330,6 +341,48 @@ export default function remarkPythonRefs() { } }); + // Pass 3: Autolink bare gh-NNNN issue refs and CVE IDs in text → badges + visit(tree, (node: any, index, parent: any) => { + // Don't touch text that is already a link (or a reference definition), + // or headings — Astro builds heading ids from text nodes only, so + // injecting HTML there would change the anchor slug. + if ( + node.type === "link" || + node.type === "linkReference" || + node.type === "definition" || + node.type === "heading" + ) { + return SKIP; + } + if (node.type !== "text" || index == null || !parent) return; + + const value: string = node.value; + const parts: any[] = []; + let lastIndex = 0; + BARE_REF.lastIndex = 0; + let m: RegExpExecArray | null; + while ((m = BARE_REF.exec(value)) !== null) { + if (m.index > lastIndex) { + parts.push({ type: "text", value: value.slice(lastIndex, m.index) }); + } + const [label, cve, ghNum] = m; + const match: Match = cve + ? { type: "cve", icon: shieldIcon, label, url: `https://www.cve.org/CVERecord?id=${cve}` } + : { type: "gh-issue", icon: issueIcon, label, url: `https://github.com/python/cpython/issues/${ghNum}` }; + collectRef(match.type, match.label, match.url); + parts.push({ type: "html", value: buildBadgeHtml(match) }); + lastIndex = m.index + m[0].length; + } + if (parts.length === 0) return; + if (lastIndex < value.length) { + parts.push({ type: "text", value: value.slice(lastIndex) }); + } + + parent.children.splice(index, 1, ...parts); + // Continue after the nodes we just inserted + return index + parts.length; + }); + // Expose collected references via remarkPluginFrontmatter if (!file.data.astro) file.data.astro = {}; if (!file.data.astro.frontmatter) file.data.astro.frontmatter = {};