{"record":{"id":"f6c18d852c847833","repo":"affaan-m/ECC","slug":"nasiko-installation-requires-explicit-yes-consen","errorCode":null,"errorMessage":"Nasiko installation requires explicit --yes consent.","messagePattern":"Nasiko installation requires explicit --yes consent\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/nasiko-release.js","lineNumber":387,"sourceCode":"  try {\n    return createLifecycleLock(lockPath, fileSystem);\n  } catch (error) {\n    if (error.code === 'EEXIST') {\n      throw new Error(`Another Nasiko lifecycle operation won stale-lock recovery: ${lockPath}.`);\n    }\n    throw error;\n  }\n}\n\nasync function installNasiko(options = {}, dependencies = {}) {\n  const version = options.version || 'v0.1.0';\n  const base = getQualifiedRelease(version, dependencies.platform || process.platform, dependencies.arch || process.arch);\n  const release = dependencies.releaseOverride ? { ...base, ...dependencies.releaseOverride } : base;\n  const installDirectory = validateInstallDirectory(options.installDir || defaultInstallDirectory(release, dependencies.environment || process.env, dependencies.homeDirectory || os.homedir()));\n  const destination = path.join(installDirectory, release.binaryName);\n  const plan = { dryRun: Boolean(options.dryRun), version, platform: release.os, architecture: release.arch, manifestDigest: release.manifestDigest, binaryDigest: release.binaryDigest, registryOrigin: REGISTRY_ORIGIN, destination, license: release.license, sourceUrl: release.sourceUrl };\n  if (options.dryRun) return plan;\n  if (!options.yes) throw new Error('Nasiko installation requires explicit --yes consent.');\n  fs.mkdirSync(installDirectory, { recursive: true, mode: 0o755 });\n  assertPrivateInstallDirectory(installDirectory);\n  const releaseLock = acquireLifecycleLock(installDirectory);\n  const metadataPath = metadataPathFor(destination);\n  let destinationOwned = false;\n  let metadataOwned = false;\n  try {\n    const existing = inspectInstalledNasiko(destination);\n    if (existing.installed) {\n      if (existing.qualified && existing.version === version) return { ...plan, dryRun: false, installed: true, reused: true };\n      throw new Error('An unqualified or incompatible Nasiko executable or receipt already exists at the destination.');\n    }\n    const retrieve = dependencies.fetchBytes || fetchBytes;\n    const manifestBytes = await retrieve(`${REGISTRY_ORIGIN}/v2/${REPOSITORY}/manifests/${release.manifestDigest}`, { accept: 'application/vnd.oci.image.manifest.v1+json', maxBytes: MAX_MANIFEST_BYTES });\n    assertDigest(manifestBytes, release.manifestDigest, 'Nasiko manifest');\n    const layer = validateManifest(manifestBytes);\n    const archiveBytes = await retrieve(`${REGISTRY_ORIGIN}/v2/${REPOSITORY}/blobs/${layer.digest}`, { maxBytes: MAX_ARCHIVE_BYTES });\n    if (archiveBytes.length !== layer.size) throw new Error('Nasiko archive size mismatch.');","sourceCodeStart":369,"sourceCodeEnd":405,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/nasiko-release.js#L369-L405","documentation":"installNasiko performs no filesystem changes during a dry run, but a real installation mutates the install directory and downloads a binary. To prevent accidental installs, the library requires options.yes to be explicitly truthy; otherwise it throws this consent error before creating anything.","triggerScenarios":"Calling installNasiko({ version, installDir, dryRun: false }) — or omitting dryRun — without options.yes: true; CLI paths that failed to forward the --yes flag into the options object.","commonSituations":"Scripting the library API after testing with dryRun (dryRun returns the plan and never reaches the consent check); forgetting to pass --yes through a wrapper script; interactive prompts skipped in CI.","solutions":["Pass yes: true in the options object when you intend a real install.","On the CLI, add the --yes flag to the install command.","Keep dryRun true while previewing; flip it off together with adding yes: true.","Audit wrapper scripts to ensure the consent flag is forwarded to installNasiko."],"exampleFix":"// before\nawait installNasiko({ version: 'v0.1.0', installDir: '/usr/local/bin' }) // throws\n// after\nawait installNasiko({ version: 'v0.1.0', installDir: '/usr/local/bin', yes: true })","handlingStrategy":"validation","validationCode":"function assertInstallConsent(options) {\n  if (!options.dryRun && !options.yes) {\n    throw new Error('real install requires { yes: true } or the --yes CLI flag')\n  }\n}\nassertInstallConsent(myOptions)\nawait installNasiko(myOptions)","typeGuard":"const hasInstallConsent = (o) => Boolean(o && o.yes) || Boolean(o && o.dryRun)","tryCatchPattern":"try {\n  await installNasiko(opts)\n} catch (e) {\n  if (e.message.includes('explicit --yes consent')) {\n    return installNasiko({ ...opts, yes: await promptForConfirmation() })\n  }\n  throw e\n}","preventionTips":["Always pass yes: true (or --yes) whenever dryRun is false.","Never rely on defaults: installNasiko defaults to a real install when dryRun is omitted.","Wrap installNasiko in a helper that validates consent before calling.","In CI, set the consent flag explicitly rather than relying on interactive prompts."],"tags":["consent","cli","install"],"backgroundTag":"missing-required-flag","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"}