{"record":{"id":"5e9fa5caa305c585","repo":"affaan-m/ECC","slug":"nasiko-installation-requires-a-pinned-version-such","errorCode":null,"errorMessage":"Nasiko installation requires a pinned version such as v0.1.0; latest is not allowed.","messagePattern":"Nasiko installation requires a pinned version such as v0\\.1\\.0; latest is not allowed\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/nasiko-release.js","lineNumber":42,"sourceCode":"    'linux/arm64': Object.freeze({ manifestDigest: 'sha256:655021a129c7df4621a80d16ea4eab38018530bfe9a20da646641d9f4ac5c249', binaryDigest: 'sha256:85f9fa5cfbed6c276fce6df2d71e9d0c66d7d8e8d46a40297464833d38db7a7e' }),\n    'darwin/amd64': Object.freeze({ manifestDigest: 'sha256:b4188482621efd7da5a2ab630f653665ab5f80b9aceae448b6bb5fc93e003f06', binaryDigest: 'sha256:ed6232e0bb96a2dcfd86d3c25f86021091600d52f09250403a54528bfe8100a3' }),\n    'darwin/arm64': Object.freeze({ manifestDigest: 'sha256:ce7e54fa19f989a5d125c4409b3587ca9503bb5a07bc5ff223c60e0fbad437f0', binaryDigest: 'sha256:3c60f862b04eea1b9a633593b39f1a443d9ca2123cf3edfd150d313e95f3894b' }),\n    'windows/amd64': Object.freeze({ manifestDigest: 'sha256:0760fe1fc98e8fedb66796aaf891a1de9268af1338c5e88b949656fda5d9f045', binaryDigest: 'sha256:0f57672d24fc3c70e4cbbf22864e65b35b9978ddaae3793803841536e682929b' }),\n  }),\n});\n\nfunction normalizePlatform(platform = process.platform, architecture = process.arch) {\n  const osName = platform === 'win32' ? 'windows' : platform;\n  if (!['linux', 'darwin', 'windows'].includes(osName)) throw new Error(`Unsupported platform: ${platform}`);\n  const arch = architecture === 'x64' ? 'amd64' : architecture;\n  if (!['amd64', 'arm64'].includes(arch)) throw new Error(`Unsupported architecture: ${architecture}`);\n  if (osName === 'windows' && arch !== 'amd64') throw new Error(`Unsupported architecture for Windows: ${architecture}`);\n  return { os: osName, arch, binaryName: osName === 'windows' ? 'nasiko.exe' : 'nasiko' };\n}\n\nfunction getQualifiedRelease(version, platform = process.platform, architecture = process.arch) {\n  if (!/^v\\d+\\.\\d+\\.\\d+$/.test(String(version || ''))) {\n    throw new Error('Nasiko installation requires a pinned version such as v0.1.0; latest is not allowed.');\n  }\n  const normalized = normalizePlatform(platform, architecture);\n  const qualification = QUALIFIED_RELEASES[version]?.[`${normalized.os}/${normalized.arch}`];\n  if (!qualification) throw new Error(`Nasiko ${version} is not qualified for ${normalized.os}/${normalized.arch}.`);\n  return { version, ...normalized, ...qualification, license: LICENSE, sourceUrl: SOURCE_URL };\n}\n\nfunction digestBytes(bytes) {\n  return `sha256:${crypto.createHash('sha256').update(bytes).digest('hex')}`;\n}\n\nfunction assertDigest(bytes, expectedDigest, label) {\n  if (!SHA256_PATTERN.test(expectedDigest)) throw new Error(`${label} has an invalid expected digest.`);\n  const actual = digestBytes(bytes);\n  if (actual !== expectedDigest) throw new Error(`${label} digest mismatch: expected ${expectedDigest}, got ${actual}.`);\n}\n\nfunction validateManifest(bytes) {","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/affaan-m/ECC/blob/06c5e118c4d3e6c3b7f9445f973a2194c82de193/scripts/lib/nasiko-release.js#L24-L60","documentation":"getQualifiedRelease in scripts/lib/nasiko-release.js requires an exact 'vMAJOR.MINOR.PATCH' version because every release's manifest and binary sha256 digests are pinned in QUALIFIED_RELEASES. The regex /^v\\d+\\.\\d+\\.\\d+$/ rejects anything else before any platform work happens. This makes installs reproducible and immune to registry tag drift.","triggerScenarios":"Calling getQualifiedRelease (or the installer) with version 'latest', '' or undefined, '0.1.0' (missing the leading v), 'v0.1' or 'v1' (too few segments), or 'v0.1.0-rc.1' (prerelease suffixes fail the strict regex).","commonSituations":"Copy-pasting 'latest' from other tools' docs into config; scripts that compute version strings without the v prefix; feeds that supply semver ranges like '^1.0.0' or npm-style tags; a CI variable left empty by default.","solutions":["Pin an exact released version that exists in QUALIFIED_RELEASES, currently 'v0.1.0'","If the version comes from a variable, normalize it: `const v = raw.startsWith('v') ? raw : 'v' + raw` and strip prerelease/build suffixes","Reject empty or range-style versions in your config schema before invoking the installer"],"exampleFix":"// before\ninstallNasiko({ version: 'latest' });\n// after\ninstallNasiko({ version: 'v0.1.0' });","handlingStrategy":"validation","validationCode":"const PINNED_VERSION = 'v0.1.0';\nif (!/^v\\d+\\.\\d+\\.\\d+$/.test(PINNED_VERSION)) {\n  throw new Error(`Version must be pinned as vX.Y.Z, got: ${PINNED_VERSION}`);\n}\nawait installNasiko({ version: PINNED_VERSION });","typeGuard":"const isPinnedVersion = (v: unknown): v is `v${number}.${number}.${number}` =>\n  typeof v === 'string' && /^v\\d+\\.\\d+\\.\\d+$/.test(v);","tryCatchPattern":"try {\n  await installNasiko({ version: requestedVersion });\n} catch (error) {\n  if (/requires a pinned version/.test(String(error.message))) {\n    // Replace 'latest'/semver ranges with the current pinned tag before retrying.\n  }\n  throw error;\n}","preventionTips":["Treat the Nasiko version as a lockfile entry: one constant, exact format, never a range","Reject 'latest', '^x.y.z', and prerelease strings in config validation upstream","Add a unit test asserting the configured version matches the pinned regex"],"tags":["version","validation","supply-chain"],"backgroundTag":"invalid-version-format","analyzedSha":"06c5e118c4d3e6c3b7f9445f973a2194c82de193","analyzedAt":"2026-08-18T11:27:13.915Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}