Back to Blog
high SEVERITY7 min read

How an Infinite Loop Vulnerability Happens in Go's Text Normalization and How to Fix It

CVE-2026-56852 is a high-severity denial-of-service vulnerability in `golang.org/x/text` where a `norm.Iter` iterator can enter an infinite loop when processing specially crafted Unicode input, hanging the process indefinitely. The `fe-tool` module was pinned to `v0.27.0`, which contains the flaw, and was upgraded to `v0.39.0` to eliminate the risk. Because `fe-tool` handles file-format parsing (7-Zip archives and Electron ASAR bundles), any user-supplied filename or archive content could have t

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published August 26, 2026•Reviewed August 26, 2026

Answer Summary

CVE-2026-56852 is a high-severity Denial-of-Service (DoS) vulnerability (CWE-835: Loop with Unreachable Exit Condition) in the `golang.org/x/text` Go package. In versions prior to `v0.39.0`, the `norm.Iter` type — used for Unicode normalization — can enter an infinite loop when it encounters certain malformed or adversarially crafted Unicode sequences, causing the host process to hang indefinitely. The fix is to upgrade `golang.org/x/text` to `v0.39.0` in `go.mod`, which corrects the iterator's loop-exit logic so it always terminates on any input.

Vulnerability at a Glance

cweCWE-835 (Loop with Unreachable Exit Condition)
fixUpgrade golang.org/x/text from v0.27.0 to v0.39.0 in fe-tool/go.mod
riskAn attacker supplying crafted Unicode input can hang the process indefinitely, causing a denial of service
languageGo
root causenorm.Iter in golang.org/x/text ≤v0.27.0 has a loop-exit condition that is never reached for certain Unicode byte sequences
vulnerabilityInfinite Loop / Denial of Service in Unicode normalization

How an Infinite Loop Vulnerability Happens in Go's Text Normalization and How to Fix It


The fe-tool module was quietly sitting on a time-bomb dependency

The fe-tool/go.mod file lists the dependencies for a Go utility that parses 7-Zip archives (via github.com/bodgit/sevenzip) and Electron ASAR bundles (via github.com/dcboy/go-asar). Both of those libraries lean on golang.org/x/text for Unicode normalization — the process of converting text into a canonical form before comparing, storing, or displaying it. That normalization step is completely invisible to most developers, which is exactly what makes this class of vulnerability so dangerous: the attack surface is buried several layers deep in the dependency tree.

Trivy's dependency scanner flagged golang.org/x/text v0.27.0 in fe-tool/go.mod as matching CVE-2026-56852, a high-severity Denial-of-Service vulnerability. The fix — upgrading to v0.39.0 — is a single line change, but understanding why it matters requires a closer look at what norm.Iter does and how it can be made to spin forever.


The Vulnerability Explained

What is norm.Iter and why does it loop?

golang.org/x/text/unicode/norm provides iterators for walking through Unicode text one normalization segment at a time. The norm.Iter type is designed to be called repeatedly in a loop like this:

// Typical usage pattern (simplified)
var iter norm.Iter
iter.InitString(norm.NFC, inputString)

for !iter.Done() {
    segment := iter.Next() // advance to next normalized segment
    process(segment)
}

The contract is simple: each call to iter.Next() advances an internal byte offset, and iter.Done() returns true when the offset reaches the end of the input. In affected versions (≤ v0.27.0), a specific class of Unicode byte sequences — containing certain combining characters or malformed code-point boundaries — causes iter.Next() to return without advancing the internal offset. The offset stays at the same position on the next iteration, iter.Done() never becomes true, and the loop runs forever.

The vulnerable dependency line

Before the fix, fe-tool/go.mod pinned:

// fe-tool/go.mod (before fix)
golang.org/x/text v0.27.0 // indirect

Because this is an indirect dependency (pulled in by bodgit/sevenzip and dcboy/go-asar), it would not normally appear on a developer's radar during a routine code review. Yet it is exercised whenever either of those libraries normalizes a filename or string extracted from an archive.

How an attacker could exploit this

fe-tool processes user-supplied archive files. Consider this attack path:

  1. An attacker crafts a 7-Zip or ASAR archive whose internal filenames contain a malformed Unicode sequence — for example, a combining diacritic that is encoded in a way that prevents norm.Iter from advancing past it.
  2. The victim runs fe-tool against the malicious archive (or a service wraps fe-tool and processes user-uploaded archives automatically).
  3. bodgit/sevenzip calls into golang.org/x/text to normalize the filename for path comparison or output.
  4. norm.Iter.Next() stalls at the malformed byte offset, the loop never exits, and the fe-tool process hangs at 100% CPU until it is killed.

In an automated pipeline — a CI artifact processor, a package registry scanner, a game-mod distribution platform — this single malicious archive could take down the worker indefinitely, constituting a full denial of service.


The Fix

What changed in fe-tool/go.mod

The patch upgrades golang.org/x/text from the vulnerable v0.27.0 to the patched v0.39.0:

-   golang.org/x/text v0.27.0 // indirect
+   golang.org/x/text v0.39.0 // indirect

v0.39.0 corrects the loop-exit logic inside norm.Iter so that Next() always advances the byte offset by at least one position, guaranteeing termination regardless of the input content.

Additional changes in the same PR

The diff also promotes github.com/bodgit/sevenzip and github.com/dcboy/go-asar from indirect to direct dependencies:

+require (
+   github.com/bodgit/sevenzip v1.6.1
+   github.com/dcboy/go-asar v0.1.0
+)

 require (
    github.com/andybalholm/brotli v1.2.0 // indirect
    github.com/bodgit/plumbing v1.3.0 // indirect
-   github.com/bodgit/sevenzip v1.6.1 // indirect
    github.com/bodgit/windows v1.0.1 // indirect
-   github.com/dcboy/go-asar v0.1.0 // indirect

This is a best-practice cleanup: if fe-tool's own code imports these packages directly, they belong in the top-level require block rather than the indirect block. Accurate dependency classification makes it easier for tools like Trivy, govulncheck, and Dependabot to reason about the actual attack surface.

The Go toolchain version was also bumped from go 1.24.5 to go 1.25.0, and fe-tool/go.sum was updated with the new hashes for golang.org/x/text v0.39.0 and the reclassified direct dependencies.

Before vs. after at a glance

Before After
golang.org/x/text version v0.27.0 v0.39.0
norm.Iter infinite-loop risk ✅ Present ❌ Fixed
sevenzip / go-asar classification indirect direct
Go toolchain 1.24.5 1.25.0

Key Takeaways

  • norm.Iter in golang.org/x/text ≤ v0.27.0 is not safe to use with untrusted input — archive filenames, user-uploaded text, or any externally sourced string can trigger the infinite loop.
  • Indirect dependencies in go.mod carry real CVEs. The // indirect comment is a classification hint for the Go toolchain, not a security boundary.
  • Upgrading from v0.27.0 to v0.39.0 is the only reliable fix — input validation alone cannot paper over a loop-exit bug inside the library.
  • Promoting bodgit/sevenzip and go-asar to direct dependencies makes the dependency graph more transparent and ensures future scanners correctly attribute reachability to fe-tool's own code.
  • A single-line go.mod change can eliminate a high-severity DoS vector that would otherwise be invisible during normal code review.

How Orbis AppSec Detected This

  • Source: User-supplied archive files (7-Zip or ASAR format) processed by fe-tool, whose internal filenames or string fields may contain arbitrary Unicode byte sequences.
  • Sink: The norm.Iter.Next() call path inside golang.org/x/text v0.27.0, reached transitively through github.com/bodgit/sevenzip and github.com/dcboy/go-asar when they normalize strings from archive metadata.
  • Missing control: No upper bound on the version of golang.org/x/text was enforced, allowing the vulnerable v0.27.0 to remain pinned long after the patch was available; no automated dependency-vulnerability gate was present in the pipeline.
  • CWE: CWE-835 — Loop with Unreachable Exit Condition
  • Fix: golang.org/x/text was upgraded from v0.27.0 to v0.39.0 in fe-tool/go.mod, replacing the defective norm.Iter loop-exit logic with a version that always advances the byte offset.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

CVE-2026-56852 is a textbook example of how a deeply buried, "invisible" dependency can introduce a high-severity vulnerability into an application that never explicitly calls the affected API. The fe-tool module processes real-world archive files — a classic source of adversarially crafted input — and its transitive dependency on a flawed version of golang.org/x/text meant that any malicious Unicode sequence in an archive filename could hang the process indefinitely.

The fix is surgical and low-risk: a single version bump in go.mod from v0.27.0 to v0.39.0, plus a dependency-graph cleanup that makes the true attack surface explicit. The broader lesson is that dependency hygiene — automated scanning, accurate direct/indirect classification, and CI gates on known CVEs — is not optional overhead. It is the layer of defense that catches the vulnerabilities your code review will never see.


Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #120

Related Articles

critical

CVE-2026-59873: node-tar 7.5.11 DoS via Crafted Gzip Bomb

node-tar versions 7.5.11 through 7.5.18 are vulnerable to a denial-of-service attack through maliciously crafted gzip archives that decompress to disproportionately large sizes. An attacker can exploit this to exhaust memory and CPU resources by submitting a small, highly compressed archive that expands beyond configured limits during extraction.

high

MapManager.get() Race Condition Duplicates API Requests

The MapManager's `get(mapUid, cache)` method used a check-then-act pattern that permitted multiple concurrent requests to pass the cache miss check simultaneously, triggering redundant API calls and risking cache corruption. The fix introduces a `_pending` promise map to deduplicate in-flight fetches for identical map UIDs.

critical

Lampa Desktop Auto-Update Heuristic Bypass: Execution of Unverified

Lampa Desktop's auto-update mechanism downloaded JavaScript and CSS from `raw.githubusercontent.com` using only heuristic validation—file size thresholds and string pattern matching—that attackers could trivially satisfy. The fix introduces cryptographic integrity verification by cross-referencing Git blob hashes from the GitHub Contents API, ensuring downloaded code matches the repository's authoritative state before execution.

high

adm-zip 0.6.0 Preserves SUID Bits From ZIPs: CVE-2026-102282

The `adm-zip` dependency resolved to 0.6.0 in this project's dependency tree, a version affected by CVE-2026-102282: during extraction it applies the Unix permission bits stored in each ZIP entry's external file attributes verbatim, including the setuid (`04000`), setgid (`02000`), and sticky bits. An attacker who controls an archive passed to `extractAllTo()` or `extractEntryTo()` can therefore have the extractor create a setuid binary owned by whatever user the extraction process runs as. The

high

requestInput() Type Confusion: NaN and Object Bypass in JavaScript

The `requestInput()` utility function lacked validation on its `type` parameter and failed to handle `NaN` results from float conversions, creating a type confusion weakness. An attacker could supply malformed inputs that propagate unhandled `NaN` values or unexpected object types through the type system. The fix adds explicit guards against `NaN` type parameters and rejects non-primitive type values.

critical

No Rate Limit on /api/uploads/presign Enables DoS

The `/api/uploads/presign` endpoint accepted unlimited concurrent requests to generate storage presigned URLs, giving an attacker a free lever to exhaust storage-provider quotas and server resources. The fix adds an `express-rate-limit` middleware capping each client to 30 requests per minute on that route.