{"record":{"id":"3c9980d0e5503fcd","repo":"affaan-m/ECC","slug":"invalid-ecc-repo-root-missing-package-json-at-p","errorCode":null,"errorMessage":"Invalid ECC repo root: missing package.json at ${packageJsonPath}","messagePattern":"Invalid ECC repo root: missing package\\.json at (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/auto-update.js","lineNumber":136,"sourceCode":"    return path.dirname(record.state.target.root);\n  }\n\n  return repoRoot;\n}\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}","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/auto-update.js#L118-L154","documentation":"`changedFiles` is an optional field in the orch-review payload, but when present it must be an array of path strings — it is joined into the security-trigger haystack. This throw fires when changedFiles is supplied but is not an array (e.g. a single string, a comma-separated CSV string, or an object). Non-array values would not join correctly and would distort the security scan.","triggerScenarios":"Passing changedFiles: 'src/foo.js' (single string); changedFiles: 'a.js,b.js' (CSV); changedFiles: { path: 'a.js' } (object).","commonSituations":"Caller has a single changed file and passes it bare instead of wrapping in an array; caller joins file paths into a CSV string; a mapping step returns the wrong shape.","solutions":["Pass an array even for one file: changedFiles: ['src/foo.js'].","If you have a CSV or Set, convert first: changedFiles: Array.from(fileSet) or csv.split(',').","If you do not need file-level security scanning, omit changedFiles entirely."],"exampleFix":"// before\norchReview({ diff, changedFiles: 'src/foo.js' }); // string, not array\n\n// after\norchReview({ diff, changedFiles: ['src/foo.js'] });","handlingStrategy":"validation","validationCode":"// Normalize changedFiles to an array before calling.\nif (changedFiles != null && !Array.isArray(changedFiles)) {\n  changedFiles = [String(changedFiles)]; // or csv.split(',')\n}\norchReview({ diff, changedFiles });","typeGuard":"function isValidChangedFiles(v) {\n  return v == null || (Array.isArray(v) && v.every(x => typeof x === 'string'));\n}","tryCatchPattern":null,"preventionTips":["Always pass changedFiles as an array, even for a single file.","Convert CSV/Sets to arrays before passing.","Omit changedFiles entirely if you do not need file-level security scanning."],"tags":["validation","workflow","type","review-gate","array"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}