{"record":{"id":"df867170f7895525","repo":"affaan-m/ECC","slug":"invalid-ecc-repo-root-missing-install-script-at","errorCode":null,"errorMessage":"Invalid ECC repo root: missing install script at ${installApplyPath}","messagePattern":"Invalid ECC repo root: missing install script at (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/auto-update.js","lineNumber":148,"sourceCode":"}\n\n// Recognized ECC package names. A repo root is only trusted to run its\n// install-apply.js if its package.json identifies it as ECC — otherwise a\n// cloned project that ships a nested `evil/{package.json,scripts/install-apply.js}`\n// could drive auto-update into executing attacker code (GHSA-hfpv-w6mp-5g95).\nconst ECC_PACKAGE_NAMES = new Set(['ecc-universal', 'everything-claude-code']);\n\nfunction validateRepoRoot(repoRoot) {\n  const normalized = path.resolve(repoRoot);\n  const packageJsonPath = path.join(normalized, 'package.json');\n  const installApplyPath = path.join(normalized, 'scripts', 'install-apply.js');\n\n  if (!fs.existsSync(packageJsonPath)) {\n    throw new Error(`Invalid ECC repo root: missing package.json at ${packageJsonPath}`);\n  }\n\n  if (!fs.existsSync(installApplyPath)) {\n    throw new Error(`Invalid ECC repo root: missing install script at ${installApplyPath}`);\n  }\n\n  let pkgName = null;\n  try {\n    pkgName = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')).name;\n  } catch {\n    throw new Error(`Invalid ECC repo root: unreadable package.json at ${packageJsonPath}`);\n  }\n  if (!ECC_PACKAGE_NAMES.has(pkgName)) {\n    throw new Error(`Refusing to run install from untrusted repo root ${normalized}: package.json name '${pkgName}' is not an official ECC package.`);\n  }\n\n  return normalized;\n}\n\nfunction runExternalCommand(command, args, options = {}) {\n  const result = spawnSync(command, args, {\n    cwd: options.cwd,","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/auto-update.js#L130-L166","documentation":"readRegularTextFile opens files with O_NOFOLLOW|O_NONBLOCK and then fstat's the descriptor to confirm the opened object is a regular file. If the fstat shows it is not a regular file (device, fifo, socket, directory), it throws this error naming the file via label. This guarantees reads only ever consume plain regular files and never special files that could block forever or leak data.","triggerScenarios":"Passing a path that is a FIFO, socket, device node (e.g. /dev/*), or directory to a reader that goes through readRegularTextFile (callers: existing, source, readBody). The O_NOFOLLOW open would already fail on a symlink, so reaching this check means the path itself is a non-regular file.","commonSituations":"A misconfigured path pointing at a named pipe or device; passing a directory where a file was expected (e.g. wrong config key); a test fixture accidentally creating a fifo; container mounts exposing special files at the expected path.","solutions":["Point the path at a regular file, not a directory, fifo, socket, or device node.","Check the path with `ls -la` / `file <path>` to see what kind of filesystem object it actually is.","Fix the configuration key or constant that supplies the wrong path.","If data lives in a directory, read the specific file inside it instead of the directory itself."],"exampleFix":"// before\nreadBody('/vault/project');            // directory\n\n// after\nreadBody('/vault/project/memory.md'); // regular file","handlingStrategy":"type-guard","validationCode":"const fs = require('fs');\nfunction assertRegularFile(path) {\n  const st = fs.lstatSync(path);\n  if (!st.isFile()) {\n    throw new TypeError(`Expected a regular file at ${path}, got ${st.isDirectory() ? 'directory' : 'special file'}.`);\n  }\n}","typeGuard":"const isRegularFile = (p) => { try { return fs.lstatSync(p).isFile() && !fs.lstatSync(p).isSymbolicLink(); } catch { return false; } };","tryCatchPattern":"try {\n  const text = readRegularTextFile(path, { label: 'memory document', maxBytes });\n} catch (err) {\n  if (err.message.includes('must be a regular, non-symlink file')) {\n    throw new Error(`${path} is not a regular file — check the path/config key.`);\n  }\n  throw err;\n}","preventionTips":["Verify paths with `file`/`ls -la` when configuring vault locations.","Ensure config keys point at files, not directories or pipes.","Guard call sites with an isRegularFile check before reading.","Avoid pointing the vault at special filesystem paths (devices, fifos, sockets)."],"tags":["filesystem","symlink","regular-file-check","memory-vault"],"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"}