{"record":{"id":"35d8499891aaeabc","repo":"abhigyanpatwari/GitNexus","slug":"trusted-cache-directory-env-must-be-outside-the","errorCode":null,"errorMessage":"${TRUSTED_CACHE_DIRECTORY_ENV} must be outside the analyzer package and build roots","messagePattern":"(.+?) must be outside the analyzer package and build roots","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/analyzer-identity.ts","lineNumber":2114,"sourceCode":"): string | null {\n  // An explicit location is a trusted operator/test override and therefore\n  // remains authoritative, including when the secure default is unavailable.\n  if (options.cacheDirectory) {\n    const explicit = path.resolve(options.cacheDirectory);\n    try {\n      // Create it before any build/dependency directory guards are captured.\n      // A cache nested immediately under a package root then changes that\n      // parent's directory state once, not after we persist the first entry.\n      mkdirSync(explicit, { recursive: true, mode: 0o700 });\n    } catch {\n      /* persistence remains optional and will fail closed */\n    }\n    return explicit;\n  }\n  const configured = trustedEnvironmentCacheDirectory();\n  if (configured) {\n    if (isInside(packageRoot, configured) || isInside(buildRoot, configured)) {\n      throw new Error(\n        `${TRUSTED_CACHE_DIRECTORY_ENV} must be outside the analyzer package and build roots`,\n      );\n    }\n    return configured;\n  }\n  return defaultCacheDirectory();\n}\n\nfunction hasTrustedCacheOverride(options: AnalyzerIdentityResolveOptions): boolean {\n  return (\n    options.cacheDirectory !== undefined || process.env[TRUSTED_CACHE_DIRECTORY_ENV] !== undefined\n  );\n}\n\nfunction identityCacheKey(\n  packageRoot: string,\n  buildRoot: string,\n  runtimeVariant: RuntimeVariant,","sourceCodeStart":2096,"sourceCodeEnd":2132,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/52924ef12c2290ceee4612526a828ec4cdf2047f/gitnexus/src/core/analyzer-identity.ts#L2096-L2132","documentation":"Even a valid, real, absolute GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR is rejected if it lies inside the analyzer package root or build root. A cache nested inside the trees being hashed would create a feedback loop: persisting a cache entry changes the hashed inputs, invalidating the identity it just computed and defeating the tamper guards.","triggerScenarios":"cacheDirectory() computing isInside(packageRoot, configured) || isInside(buildRoot, configured) as true — e.g. the env var set to <packageRoot>/.identity-cache or <packageRoot>/node_modules/... while resolveAnalyzerRunnerIdentity() runs against that same package.","commonSituations":"Users tucking the cache 'neatly' inside the install dir, Docker images copying a cached directory into the package, or CI caching configurations that write into node_modules/gitnexus.","solutions":["Move the cache outside the installation: export GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR=/var/cache/gitnexus-identity (create it first).","In CI, cache an external directory and mount/restore it at a path outside the package and build roots.","If you do not need a custom location, unset the variable and use the default cache directory.","Note options.cacheDirectory (programmatic) is exempt — only the env-var path enforces this rule."],"exampleFix":"# before\nexport GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR=/usr/lib/node_modules/gitnexus/.id-cache\n# ...must be outside the analyzer package and build roots\n\n# after\nsudo mkdir -p /var/cache/gitnexus-identity && sudo chown \"$USER\" /var/cache/gitnexus-identity\nexport GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR=/var/cache/gitnexus-identity","handlingStrategy":"validation","validationCode":"// Ensure the configured cache dir is outside the install tree:\nimport { relative, isAbsolute } from 'node:path';\nfunction cacheDirOutsideRoots(cacheDir: string, packageRoot: string, buildRoot: string): boolean {\n  const outside = (root: string) => {\n    const rel = relative(resolve(root), resolve(cacheDir));\n    return rel === '' || rel.startsWith('..') || isAbsolute(rel) ? !(rel === '') : false;\n  };\n  return outside(packageRoot) && outside(buildRoot);\n}","typeGuard":null,"tryCatchPattern":"try {\n  spawnSync('gitnexus', ['analyze']);\n} catch (err) {\n  if (String((err as Error).message).includes('must be outside the analyzer package and build roots')) {\n    delete process.env.GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR; // fall back to the default cache\n    return spawnSync('gitnexus', ['analyze']);\n  }\n  throw err;\n}","preventionTips":["Choose OS-level cache locations (/var/cache, $HOME/.cache) for the identity cache, never inside node_modules.","In CI, restore cached identity data to paths outside the checkout/install.","Remember only the env var is constrained; programmatic options.cacheDirectory is the deliberate override route.","Audit Dockerfiles that COPY cached state into the package directory."],"tags":["analyzer-identity","cache","environment-variable","misconfiguration"],"backgroundTag":"invalid-cache-location","analyzedSha":"52924ef12c2290ceee4612526a828ec4cdf2047f","analyzedAt":"2026-08-20T23:29:22.980Z","contentChangedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}