At commit bd4c59c, CyberChef has 2,010 analysed functions, 242 of which fall in the critical band. Five of those sit at the intersection of high structural complexity and recent commit activity — the “fire” quadrant — meaning they are live regression risks right now, not cleanup items for a future sprint. I would start with run in ParseIPv6Address.mjs: an activity-weighted risk score of 19.46, a cyclomatic complexity of 102, and a last-modified date of 8 days ago make it the most urgent surface in the codebase. CyberChef is GCHQ’s browser-based data-transformation tool, and the breadth of operations it supports — from cryptanalysis to coordinate conversion to encoding primitives — is exactly what makes these hotspots consequential: they sit on paths that many other operations depend on.
The table below ranks functions by activity-weighted risk — a score that multiplies structural complexity by recent commit frequency. A function that is both hard to understand (high cyclomatic complexity) and actively changing is a higher priority than one that is complex but untouched. CC = cyclomatic complexity (independent execution paths); ND = max nesting depth; FO = fan-out (distinct callees).
Top 5 Hotspots
| Function | File | Risk | CC | ND | FO |
|---|---|---|---|---|---|
run | src/core/operations/ParseIPv6Address.mjs | 19.5 | 102 | 19 | 8 |
convertCoordinates | src/core/lib/ConvertCoordinates.mjs | 17.5 | 81 | 4 | 37 |
run | src/core/operations/MultipleBombe.mjs | 17.1 | 25 | 6 | 19 |
_addQPSoftLinebreaks | src/core/operations/ToQuotedPrintable.mjs | 17.1 | 23 | 8 | 7 |
fromBase64 | src/core/lib/Base64.mjs | 16.8 | 33 | 4 | 10 |
Large Repo Analysis
CyberChef is a large repository. To stay within memory constraints, this analysis used hybrid touch mode: structural complexity — CC, ND, FO — is measured precisely for every function. Git activity is tracked at the function level (via git log -L) only for files with 5 or more commits in the last 30 days; other files use a file-level approximation. Rankings therefore surface functions that are both structurally complex and in the most actively-changing parts of the codebase. Dormant code with high structural complexity will rank lower than it would under a full per-function analysis — to surface it, run hotspots analyze . --per-function-touches on a machine with sufficient memory.
Repository snapshot
2,010 functions analyzed
The quadrant distribution tells an immediate story: 609 functions are in the “fire” quadrant — high complexity and active recent commits — while only 2 sit in “debt” (complex but dormant). The overwhelming majority of CyberChef’s structural risk is live, not parked. That’s the baseline before we look at which specific functions sit at the top of the stack.
High cyclomatic complexity — many independent execution paths, each a potential bug surface and required test case.Exit Heavy×5Exit Heavy
Multiple return or throw paths dispersed through the body — each exit needs separate test coverage.Deeply Nested×3Deeply Nested
Control structures nested 4+ levels deep, making it hard to reason about the full execution state at inner branches.Long Function×3Long Function
Function body is too long to review in a single pass; likely contains multiple distinct responsibilities.God Function×3God Function
Calls an unusually large number of distinct functions (high fan-out), making it the structural centre of gravity for a subsystem.Hub Function×1Hub Function
Many other functions call this one — a change here ripples widely through callers.
Across the five hotspots, complex branching and exit-heavy patterns appear in every single one. Three are flagged as god functions, meaning they accumulate logic that would be better distributed across focused helpers. Three are also long functions, a direct invitation for extract-method refactoring. One carries the hub-function pattern — more on that below.
run — ParseIPv6Address.mjs
This function handles the full classification of an IPv6 address: it matches the input against a regex, converts it to longhand and shorthand forms, then works through a long chain of conditionals to identify reserved address types — unspecified, loopback, IPv4-mapped, IPv4-translated, discard prefix, well-known translation prefix, Teredo tunneling, and more. Each branch appends a different block of explanatory text to the output and, in the Teredo case, unpacks several bit-field flags with their own nested conditionals.
The numbers are striking. A cyclomatic complexity of 102 means there are at least 102 independent execution paths through this function — each one a required test case and a potential bug surface. The maximum nesting depth of 19 is the most extreme value in the top five; at that depth, a reader tracking the current conditional context has to hold nearly 20 layers of state in their head simultaneously. This is the complex_branching and deeply_nested pattern combination in its most severe form.
The function was touched 8 days ago — a single commit in the last 30 days that is also the file’s only commit on record. There are no bug-linked commits or reverts in the external signals, so there is no historical defect evidence to cite. But a CC of 102 with a nesting depth of 19 is a structural argument on its own: the next developer to modify this function, for any reason, will be reasoning inside one of the most complex single functions in the repository.
My recommendation: decompose by address family. Each reserved-address branch — Teredo, IPv4-mapped, IPv4-translated, discard prefix, well-known prefix — is self-contained enough to become its own helper function. Extracting five or six named helpers would halve the CC of run while making each address-type handler independently testable. The Teredo branch alone, with its bit-field unpacking and flag validation, is a strong candidate for immediate extraction.
convertCoordinates — ConvertCoordinates.mjs
This is a library-level function — not inside an operations/ file but in lib/, meaning other operations call it directly. It accepts input in one coordinate format and delimiter, converts to a common geodesy lat/lon object, then converts out to a target format and delimiter. The source excerpt shows a large switch statement over input formats, with each case delegating to a different parsing library (geohash, MGRS, OS Grid Reference, and others).
The headline number here is the fan-out of 37 — the highest in the top five by a wide margin. Fan-out counts distinct functions called, and 37 means this function directly invokes 37 different callees. In JavaScript, where dynamic property access and prototype dispatch can obscure dependencies that static analysis misses, 37 is likely a floor, not a ceiling. The god_function and long_function patterns both flag here, which is consistent: a function that orchestrates 37 callees across multiple coordinate-system libraries is doing far more than one thing.
The nesting depth of 4 is comparatively manageable — the complexity here is horizontal (breadth of coupling) rather than vertical (depth of nesting). But that makes it no less risky: a change to how any one coordinate format is parsed, or to the delimiter-detection logic shared across formats, has blast radius across all 37 callees and every operation that calls convertCoordinates.
The file has a single commit and one author in the last 90 days, with no bug-linked commits or reverts. The immediate action I would take is to introduce a format-handler registry — a map from format name to a parser function — and extract each case in the switch into its own named converter. That collapses the switch to a single dispatch call, reduces the fan-out of the top-level function significantly, and makes each format independently testable without loading the full coordinate conversion pipeline.
run — MultipleBombe.mjs
The Multiple Bombe operation simulates a brute-force search over Enigma machine rotor, reflector, and fourth-rotor combinations, looking for configurations that produce the crib (known plaintext) from a given ciphertext. The source excerpt confirms what the name implies: a set of deeply nested for loops — one per rotor position — with early-continue guards to skip duplicate rotor assignments. The comment in the source is candid: the author acknowledges a combinatorics algorithm would be cleaner but opted for the nested loops given the absence of a suitable library utility.
The patterns flagged — complex_branching, deeply_nested, exit_heavy, god_function, long_function — are all consistent with what the source excerpt shows. A nesting depth of 6 comes directly from the five nested for loops plus the conditional guards inside them. A cyclomatic complexity of 25 reflects the number of paths through rotor validation, environment detection, and the combination search. A fan-out of 19 shows the function coordinates validation, machine construction, status reporting, and output assembly all in one place.
This function was last changed 8 days ago. There are no bug-linked commits or reverts on the file. The god_function pattern is the primary concern: input validation, rotor/reflector parsing, Bombe machine construction, progress reporting, and result aggregation are all interleaved. Extracting the rotor-combination enumeration into a dedicated generator function — which yields each [rotor1, rotor2, rotor3, rotor4, reflector] tuple — would flatten the nesting immediately and make the search loop itself legible in isolation.
_addQPSoftLinebreaks — ToQuotedPrintable.mjs
This private method handles line-wrapping for Quoted-Printable MIME encoding — a format where non-ASCII bytes are percent-encoded and lines must not exceed 76 characters, with soft line breaks inserted as =\r\n. The source excerpt shows a while loop over the input string, with a cascade of regex-based conditions deciding where to truncate each line: hard CRLF, trailing newline, nearest preceding newline, nearest word boundary, incomplete encoding sequences, and multi-byte UTF-8 sequence boundaries.
The nesting depth of 8 is what makes this function stand out structurally. Each while iteration contains a chain of if/else if branches, and inside the final else branch there is a nested if guarding a second while loop that trims incomplete UTF-8 sequences — reaching 8 levels of nesting at its deepest. That inner loop tests a regex condition on each iteration and has its own break conditions, which is precisely the exit_heavy pattern: multiple paths out of nested control structures make it difficult to reason about which path was actually taken for a given input.
Quoted-Printable line-breaking is genuinely intricate — RFC 2045 has a non-trivial set of rules about padding characters, encoding boundaries, and whitespace — so some complexity is inherent. But a nesting depth of 8 means the logic for handling incomplete UTF-8 sequences is effectively invisible to anyone reading the outer loop. My recommendation is to extract the UTF-8 sequence trimming into a named helper (trimIncompleteUtf8Sequence or similar), which would reduce the nesting depth of the outer loop to roughly 5 and give the inner logic a clear, testable contract. With 23 independent paths, this function currently requires 23 distinct test cases for full coverage — extracting helpers would distribute that burden.
fromBase64 — Base64.mjs
This is a library-level Base64 decoder that supports custom alphabets, optional padding, strict-mode validation, and configurable return types (string or byte array). The source excerpt shows the function handles a large number of cases explicitly: empty input, alphabet length validation, non-alphabet character stripping via dynamically constructed regex, strict-mode length and padding checks, and then the main decode loop with per-character index lookups and range-guarded output pushes.
The hub_function pattern is the most consequential flag here. fromBase64 sits in src/core/lib/Base64.mjs — a shared library module — meaning it is likely called from many operations across the codebase. A CC of 33 in a hub function means that every caller inherits the complexity of every execution path, including strict-mode validation branches and alphabet variants that a given caller may never exercise. Fan-out of 10 includes Utils.expandAlphRange, Utils.byteArrayToUtf8, and OperationError construction, all of which are called conditionally depending on the path taken.
The external signals are worth noting here: there is 1 bug-linked commit on this file. That does not prove fromBase64 was the source of the bug, but it does mean the file has a prior quality-related event — and with 33 execution paths and hub-function coupling, this is the one function in the top five where I would prioritize adding a comprehensive test matrix before any refactoring. The strictMode branch is the most structurally isolated and would be a good candidate for extraction into a validateBase64Input helper, which would reduce the CC of the main decode loop and make the strict-mode rules independently auditable.
What else is in the picture
Beyond the top five, the context_only data shows a handful of functions worth a brief note. toHexFast in Hex.mjs, createNumArray in Arithmetic.mjs, and fromBinary in Binary.mjs are all in the “watch” quadrant — low structural complexity but recently active. They are not refactoring priorities, but their activity signals mean they are worth keeping an eye on as the codebase evolves. The two Vigenère cipher run functions (VigenèreDecode.mjs and VigenèreEncode.mjs) sit in the “debt” quadrant with zero touches in the last 30 days; they are structurally modest and currently dormant, so they sit at the bottom of any priority list.
Patterns Found
Antipatterns detected across the top functions in this snapshot:
| Pattern | Occurrences |
|---|---|
complex_branching | 5 |
exit_heavy | 5 |
deeply_nested | 3 |
long_function | 3 |
god_function | 3 |
hub_function | 1 |
These labels belong to two tiers — Tier 1 (structural): complex_branching, deeply_nested, exit_heavy, long_function, god_function. Tier 2 (relational/temporal): hub_function, cyclic_hub, middle_man, neighbor_risk, stale_complex, churn_magnet, shotgun_target, volatile_god.
Reproduce This Analysis
git clone https://github.com/gchq/CyberChef
cd CyberChef
git checkout bd4c59cf34bfa4b1ae06839db70936786c5702df
hotspots analyze . --mode snapshot --explain-patterns --force --hybrid-touches 5
To run the same analysis on your own codebase, run hotspots analyze . --mode snapshot in any local git repo — no configuration required.
I use Hotspots to highlight structural and activity risk — not “bad code.” I treat these findings as a prioritization aid, not a bug predictor. Editorial policy →