{"record":{"id":"9532f7d6fa0ff767","repo":"affaan-m/ECC","slug":"nasiko-executable-must-be-a-regular-file-not-a-sy","errorCode":null,"errorMessage":"Nasiko executable must be a regular file, not a symlink.","messagePattern":"Nasiko executable must be a regular file, not a symlink\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/nasiko-release.js","lineNumber":238,"sourceCode":"    return bytes;\n  } finally { fs.closeSync(descriptor); }\n}\n\nfunction readMetadata(executable) {\n  try {\n    const bytes = readBoundedRegularFile(metadataPathFor(executable), MAX_METADATA_BYTES);\n    return bytes ? JSON.parse(bytes.toString('utf8')) : null;\n  }\n  catch (_error) { return null; }\n}\n\nfunction inspectInstalledNasiko(executable, resolveRelease = getQualifiedRelease) {\n  if (!executable) return { installed: false, qualified: false, version: null, executable: null };\n  let binary;\n  try { binary = readBoundedRegularFile(executable, MAX_BINARY_BYTES); }\n  catch (error) {\n    if (error.code === 'ENOENT') return { installed: false, qualified: false, version: null, executable };\n    if (error.code === 'ELOOP') throw new Error('Nasiko executable must be a regular file, not a symlink.');\n    throw error;\n  }\n  if (!binary) return { installed: true, qualified: false, version: null, executable, binaryDigest: null, metadataPath: metadataPathFor(executable) };\n  const binaryDigest = digestBytes(binary);\n  const metadata = readMetadata(executable);\n  let release = null;\n  try { if (metadata) release = resolveRelease(metadata.version, metadata.platform, metadata.architecture); } catch (_error) { release = null; }\n  const qualified = Boolean(release\n    && metadata.installedPath === executable\n    && metadata.manifestDigest === release.manifestDigest\n    && metadata.binaryDigest === release.binaryDigest\n    && binaryDigest === release.binaryDigest\n    && metadata.license === release.license\n    && metadata.sourceUrl === release.sourceUrl);\n  return { installed: true, qualified, version: qualified ? metadata.version : null, executable, binaryDigest, metadataPath: metadataPathFor(executable) };\n}\n\nfunction writeMetadataExclusive(metadataPath, metadata) {","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/nasiko-release.js#L220-L256","documentation":"inspectInstalledNasiko reads the executable with O_NOFOLLOW (and a fallback lstat identity check when O_NOFOLLOW is unavailable). If the read fails with ELOOP — meaning the path is a symlink, or the file was swapped for a symlink mid-read — it throws this error instead of silently following the link, preventing symlink-based binary swapping attacks.","triggerScenarios":"Calling inspectInstalledNasiko (directly or via existing/readStatus/tests) with an executable path that is a symbolic link; or the nasiko binary at the destination being replaced by a symlink (e.g. someone linked it to another tool, or a package manager symlinked it).","commonSituations":"Users symlink ~/bin/nasiko to a version in another directory (common version-manager pattern); a previous install was moved and a symlink left behind; package managers like Homebrew symlinking binaries into bin directories.","solutions":["Remove the symlink and restore a real binary at that path: `rm <path>` then reinstall with installNasiko({yes:true}).","Run uninstallNasiko to clear the managed path, then reinstall.","Inspect with `ls -l <path>`; if it points at a legitimate copy, pass the real file path to inspect and remove the link."],"exampleFix":"// before\nln -s /opt/nasiko-0.1.0/nasiko ~/.local/bin/nasiko\ninspectInstalledNasiko(path.join(os.homedir(), '.local/bin/nasiko')) // throws\n// after\nrm ~/.local/bin/nasiko\ninstallNasiko({ version: 'v0.1.0', yes: true })","handlingStrategy":"validation","validationCode":"const stats = fs.lstatSync(executablePath)\nif (stats.isSymbolicLink()) throw new Error(`managed path must not be a symlink: ${executablePath}`)","typeGuard":"const isRegularNonSymlinkFile = (p) => {\n  try { const s = fs.lstatSync(p); return s.isFile() && !s.isSymbolicLink() } catch { return false }\n}","tryCatchPattern":"try {\n  const status = inspectInstalledNasiko(executablePath)\n} catch (e) {\n  if (e.message.includes('regular file, not a symlink')) {\n    fs.rmSync(executablePath)\n    return installNasiko({ yes: true }) // reinstall a real binary\n  }\n  throw e\n}","preventionTips":["Do not symlink managed binaries into the install directory; use PATH entries pointing at real copies.","Uninstall before reorganizing versions instead of leaving symlinks behind.","Check `ls -l` on managed paths after using version managers.","Reinstall via the library rather than copying/linking binaries manually."],"tags":["symlink","security","inspection"],"backgroundTag":"incompatible-source-type","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}