{"record":{"id":"3be2bde8a100d7ee","repo":"Hmbown/CodeWhale","slug":"rust-public-key-byte-out-of-range","errorCode":null,"errorMessage":"Rust public key byte out of range","messagePattern":"Rust public key byte out of range","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/scripts/check-cloud-facts.mjs","lineNumber":22,"sourceCode":"import { fileURLToPath } from \"node:url\";\nimport { validateSource, verifyEnvelope, parseTsKeys, validateTrustedKeys, readBoundedFile } from \"./facts-publish.mjs\";\n\nconst WEB_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), \"..\");\nconst REPO_ROOT = resolve(WEB_ROOT, \"..\");\nexport { parseTsKeys };\n\nexport function parseRustKeys(text) {\n  const source = text.replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\").replace(/^\\s*\\/\\/.*$/gm, \"\");\n  const tables = [...source.matchAll(/^\\s*pub\\s+const\\s+TRUSTED_KEYS\\s*:\\s*&\\s*\\[TrustedKey\\]\\s*=\\s*&\\s*\\[([\\s\\S]*?)\\]\\s*;/gm)];\n  if (tables.length !== 1) throw new Error(\"cannot parse exactly one Rust TRUSTED_KEYS table\");\n  const table = tables[0];\n  const body = table[1].replace(/^\\s*\\/\\/.*$/gm, \"\");\n  const keys = [];\n  const remainder = body.replace(/TrustedKey\\s*\\{\\s*key_id:\\s*\"([^\"]+)\",\\s*public_key:\\s*\\[([^\\]]*)\\],\\s*status:\\s*KeyStatus::(Active|Retired)\\s*,?\\s*\\}/g, (_, keyId, encoded, status) => {\n    const pieces = encoded.split(\",\").map((piece) => piece.trim()).filter(Boolean);\n    if (pieces.length !== 32 || pieces.some((piece) => !/^(?:\\d+|0x[0-9a-fA-F]+)$/.test(piece))) throw new Error(\"Rust public key must contain 32 literal bytes\");\n    const bytes = pieces.map(Number);\n    if (bytes.some((byte) => !Number.isInteger(byte) || byte < 0 || byte > 255)) throw new Error(\"Rust public key byte out of range\");\n    keys.push({ keyId, publicKey: Buffer.from(bytes).toString(\"base64\"), status: status.toLowerCase() });\n    return \"\";\n  });\n  if (remainder.replace(/[\\s,]/g, \"\")) throw new Error(\"unparsed Rust TRUSTED_KEYS entry\");\n  return validateTrustedKeys(keys);\n}\n\nfunction text(path) { return readBoundedFile(path).toString(\"utf8\"); }\nfunction json(path) { return JSON.parse(text(path)); }\n\nexport function checkCloudFacts() {\n  const failures = [];\n  const source = json(resolve(REPO_ROOT, \"docs/cloud-facts/stable.json\"));\n  for (const error of validateSource(source)) failures.push(`stable.json: ${error}`);\n  if (source.channel !== \"stable\") failures.push(\"stable.json: channel must be stable\");\n  const latest = json(resolve(WEB_ROOT, \"data/latest-published-release.json\"));\n  if (source.release?.latest !== latest.version) failures.push(\"stable.json release.latest differs from latest-published-release.json\");\n  if (source.release?.release_url && source.release.release_url !== latest.url) failures.push(\"stable.json release.release_url differs from latest-published-release.json\");","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/web/scripts/check-cloud-facts.mjs#L4-L40","documentation":"After splitting the public_key literal into byte pieces and converting with Number(), each byte must be an integer in 0–255. This throws when a piece converts to a non-integer or out-of-range value — typically a value >255 (bad hex/decimal conversion) or a negative number inside the Rust array.","triggerScenarios":"A byte literal in the Rust public_key array is negative, greater than 255, or a value like 0x1FF that exceeds one byte.","commonSituations":"Hand-converting a base64 key to bytes and making an arithmetic mistake; concatenating bytes wrong; copy/paste from a different key length encoding.","solutions":["Find the offending byte (each must satisfy 0 ≤ b ≤ 255 and be integral).","Regenerate the 32-byte array programmatically from the canonical key (e.g. base64 → bytes → Rust literal) instead of by hand.","Verify against the TypeScript source-of-truth key used by check-cloud-facts."],"exampleFix":"// before (Rust)\npublic_key: [256, 12, ...],\n// after\npublic_key: [0x1_00 → fix: 0x00, 12, ...], // every byte 0..=255","handlingStrategy":"validation","validationCode":"const bytes = parts.map(Number); if (bytes.some(b => !Number.isInteger(b) || b < 0 || b > 255)) throw new Error(\"byte out of range before calling parser\");","typeGuard":"const isByte = (n) => Number.isInteger(n) && n >= 0 && n <= 255;","tryCatchPattern":"try { parseRustKeys(src); } catch (e) { if (e.message.includes(\"out of range\")) logOffendingBytes(src); throw e; }","preventionTips":["Clamp/validate bytes when generating the literal.","Derive literals programmatically from the canonical key.","Cross-check against the TypeScript source-of-truth key.","Reject hex values > 0xFF at generation time."],"tags":["parsing","rust","validation","crypto-keys"],"backgroundTag":"value-out-of-range","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}