{"record":{"id":"c7a8c19063e68caf","repo":"gchq/CyberChef","slug":"err-message-replace-rotor-plugboard","errorCode":null,"errorMessage":"err.message.replace(\"Rotor\", \"Plugboard\")","messagePattern":"err\\.message\\.replace\\(\"Rotor\", \"Plugboard\"\\)","errorType":"exception","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/lib/Typex.mjs","lineNumber":204,"sourceCode":"        // Typex input wiring is backwards vs Enigma: that is, letters enter the rotors in a\n        // clockwise order, vs. Enigma's anticlockwise (or vice versa depending on which side\n        // you're looking at it from). I'm doing the transform here to avoid having to rewrite\n        // the Engima crypt() method in Typex as well.\n        // Note that the wiring for the reflector is the same way around as Enigma, so no\n        // transformation is necessary on that side.\n        // We're going to achieve this by mapping the plugboard settings through an additional\n        // transform that mirrors the alphabet before we pass it to the superclass.\n        if (!/^[A-Z]{26}$/.test(wiring)) {\n            throw new OperationError(\"Plugboard wiring must be 26 unique uppercase letters\");\n        }\n        const reversed = \"AZYXWVUTSRQPONMLKJIHGFEDCB\";\n        wiring = wiring.replace(/./g, x => {\n            return reversed[Enigma.a2i(x)];\n        });\n        try {\n            super(wiring, \"\", \"A\", \"A\");\n        } catch (err) {\n            throw new OperationError(err.message.replace(\"Rotor\", \"Plugboard\"));\n        }\n    }\n\n    /**\n     * Transform a character through this rotor forwards.\n     *\n     * @param {number} c - The character.\n     * @returns {number}\n     */\n    transform(c) {\n        return Utils.mod(this.map[Utils.mod(c + this.pos, 26)] - this.pos, 26);\n    }\n\n    /**\n     * Transform a character through this rotor backwards.\n     *\n     * @param {number} c - The character.\n     * @returns {number}","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/lib/Typex.mjs#L186-L222","documentation":"Thrown by the Typex Plugboard constructor in Typex.mjs in the catch block: when the mirrored wiring passes the regex but the Enigma Rotor superclass rejects it (typically because the 26 letters are not a unique permutation), the constructor rewrites the superclass error message, replacing the substring \"Rotor\" with \"Plugboard\" and re-throws as an OperationError. So the literal thrown text is dynamic — it is whatever the rotor error said, with 'Rotor' swapped for 'Plugboard' (e.g. 'Plugboard wiring must be 26 unique letters').","triggerScenarios":"Constructing `new Typex.Plugboard(wiring)` with a 26-uppercase-letter string that is NOT a permutation of A-Z — i.e. it has duplicate letters and is missing others. The regex at line 194 passes (right length and charset) but the superclass uniqueness check fails, and this catch fires.","commonSituations":"Hand-typed wiring with a repeated letter (e.g. two 'A's); wiring generated by a buggy permutation routine that can repeat; copy-paste that duplicated a letter; an intentional non-bijective map that Typex (via Enigma) does not allow.","solutions":["Ensure the wiring is a true permutation: every letter A-Z appears exactly once. Check with new Set(wiring).size === 26.","If you want a pass-through plugboard, use the identity \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\".","Generate wirings with a verified shuffle of the alphabet rather than ad-hoc string building."],"exampleFix":"// before: 'A' repeated, 'Z' missing\nnew Plugboard(\"ABCDEFGHIJKLMNOPQRSTUVAWXYZ\"); // superclass throws, rewritten\n// after: true permutation\nnew Plugboard(\"AYZXWVUTSRQPONMLKJIHGFEDCB\");","handlingStrategy":"validation","validationCode":"function isAlphabetPermutation(w) {\n    if (typeof w !== \"string\" || w.length !== 26) return false;\n    const seen = new Set();\n    for (const ch of w) {\n        if (ch < \"A\" || ch > \"Z\") return false;\n        if (seen.has(ch)) return false;\n        seen.add(ch);\n    }\n    return seen.size === 26;\n}\nif (!isAlphabetPermutation(wiring)) {\n    throw new Error(\n        `Plugboard wiring must be a permutation of A-Z (each letter exactly once). Got: '${wiring}'.`\n    );\n}\nnew Typex.Plugboard(wiring);","typeGuard":"function isAlphabetPermutation(w) {\n    if (typeof w !== \"string\" || w.length !== 26) return false;\n    const seen = new Set();\n    for (const ch of w) {\n        if (ch < \"A\" || ch > \"Z\") return false;\n        if (seen.has(ch)) return false;\n        seen.add(ch);\n    }\n    return seen.size === 26;\n}","tryCatchPattern":"try {\n    plugboard = new Typex.Plugboard(wiring);\n} catch (e) {\n    if (e instanceof OperationError && /Plugboard/i.test(e.message)) {\n        // message is the rewritten rotor error; surface a clearer one\n        return { error: `Plugboard wiring must be a permutation of A-Z (no repeats).` };\n    }\n    throw e;\n}","preventionTips":["Generate wirings with a verified Fisher-Yates shuffle of the alphabet, never by ad-hoc string building.","Check new Set(wiring).size === 26 at the config boundary to catch repeats early.","For no-plugboard behaviour, use the identity alphabet string."],"tags":["crypto","typex","plugboard","wiring","permutation-validation"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}