{"record":{"id":"56bb36da5ff11838","repo":"gchq/CyberChef","slug":"letter-elem-not-in-ls47","errorCode":null,"errorMessage":"Letter ${elem} not in LS47","messagePattern":"Letter (.+?) not in LS47","errorType":"validation","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/lib/LS47.mjs","lineNumber":105,"sourceCode":"        i = (i + 1) % 7;\n    }\n    return k;\n}\n\n/**\n * Checks the key is a valid key.\n *\n * @param {string} key\n */\nfunction checkKey(key) {\n    if (key.length !== letters.length)\n        throw new OperationError(\"Wrong key size\");\n    const counts = new Array();\n    for (let i = 0; i < letters.length; i++)\n        counts[letters.charAt(i)] = 0;\n    for (const elem of letters) {\n        if (letters.indexOf(elem) === -1)\n            throw new OperationError(\"Letter \" + elem + \" not in LS47\");\n        counts[elem]++;\n        if (counts[elem] > 1)\n            throw new OperationError(\"Letter duplicated in the key\");\n    }\n}\n\n/**\n * Finds the position of a letter in they key.\n *\n * @param {letter} key\n * @param {string} letter\n * @returns {object}\n */\nfunction findPos (key, letter) {\n    const index = key.indexOf(letter);\n    if (index >= 0 && index < 49)\n        return [Math.floor(index/7), index%7];\n    throw new OperationError(\"Letter \" + letter + \" is not in the key\");","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/lib/LS47.mjs#L87-L123","documentation":"LS47's checkKey() intends to reject keys containing a character outside its 49-character alphabet. NOTE: as written, the loop iterates `letters` (the constant alphabet) rather than `key`, so letters.indexOf(elem) is always >= 0 and this branch is effectively unreachable in the current code. The intended behaviour is to flag any key byte not in \"_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()\".","triggerScenarios":"Intent: calling checkKey (via encrypt/decrypt/encryptPad/decryptPad) with a 49-char key that includes a character outside the LS47 alphabet (e.g. '@', uppercase letters, '\\n'). In practice, with the current code the check never fires because the loop target is wrong.","commonSituations":"Hand-built key containing uppercase letters or symbols outside the alphabet; key encoded with characters from a related cipher (e.g. LS47+ which has a different alphabet); copy-paste introduced a stray character. The bug also masks these cases from users.","solutions":["Use deriveKey(password) which can only emit alphabet characters.","If you build a key manually, restrict it to the exact alphabet string and validate with a regex like /^[_a-z0-9.,\\-+*/:?!'()]+$/.","Report the upstream bug: the for-loop should iterate `key`, not `letters`, so this guard actually fires."],"exampleFix":"// Bug in src/core/lib/LS47.mjs: the loop iterates `letters` not `key`.\n// before\nfor (const elem of letters) {\n    if (letters.indexOf(elem) === -1) throw ...; // always false\n}\n\n// after (intended)\nfor (const elem of key) {\n    if (letters.indexOf(elem) === -1)\n        throw new OperationError(\"Letter \" + elem + \" not in LS47\");\n}","handlingStrategy":"validation","validationCode":"const LS47_ALPHABET = \"_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()\";\n\nfunction keyUsesOnlyAlphabet(key) {\n  return [...key].every(c => LS47_ALPHABET.includes(c));\n}","typeGuard":"function isAllLS47Chars(x): x is string {\n  const A = \"_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()\";\n  return typeof x === 'string' && [...x].every(c => A.includes(c));\n}","tryCatchPattern":null,"preventionTips":["Use deriveKey() which only emits alphabet characters.","Validate manually with a regex/charset check before calling encrypt/decrypt since the in-library guard is currently a no-op (upstream bug).","Lowercase and substitute unsupported characters before constructing a key by hand.","Report the upstream bug so the library check actually iterates the key."],"tags":["cryptography","ls47","validation","upstream-bug"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}