{"record":{"id":"84a9ac2515f9c1d2","repo":"nexu-io/open-design","slug":"resource-root-env-must-be-under-the-workspace-r","errorCode":null,"errorMessage":"${RESOURCE_ROOT_ENV} must be under the workspace root or app resources path","messagePattern":"(.+?) must be under the workspace root or app resources path","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/daemon-paths.ts","lineNumber":83,"sourceCode":"\nexport interface ResolveDaemonResourceRootOptions {\n  configured?: string;\n  safeBases?: Array<string | null | undefined>;\n}\n\nexport function resolveDaemonResourceRoot({\n  configured = process.env[RESOURCE_ROOT_ENV],\n  safeBases,\n}: ResolveDaemonResourceRootOptions = {}): string | null {\n  if (!configured || configured.length === 0) return null;\n\n  const resolved = path.resolve(configured);\n  const normalizedSafeBases = (safeBases ?? [])\n    .filter((base): base is string => typeof base === 'string' && base.length > 0)\n    .map((base) => path.resolve(base));\n\n  if (!normalizedSafeBases.some((base) => isPathWithin(base, resolved))) {\n    throw new Error(\n      `${RESOURCE_ROOT_ENV} must be under the workspace root or app resources path`,\n    );\n  }\n\n  return resolved;\n}\n\nexport function resolveDaemonResourceDir(\n  resourceRoot: string | null,\n  segment: string,\n  fallback: string,\n): string {\n  return resourceRoot ? path.join(resourceRoot, segment) : fallback;\n}\n\nexport interface ResolveDaemonPluginPreviewsDirOptions {\n  env?: NodeJS.ProcessEnv;\n  resourceRoot: string | null | undefined;","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/daemon-paths.ts#L65-L101","documentation":"Thrown by resolveDaemonResourceRoot() when OD_RESOURCE_ROOT is set but the resolved path is not inside any of the supplied safeBases (workspace root or app resources path). It is a path-traversal / escape guard: the daemon only serves resources from sanctioned roots, so a configured resource root outside them is rejected hard. Returns null when the env var is unset (the legitimate 'no override' case).","triggerScenarios":"Setting OD_RESOURCE_ROOT to an absolute path outside the workspace root or the packaged app resources path; using a relative path that resolves outside the safe bases via '..'; pointing at a symlink whose target escapes the bases.","commonSituations":"Operator pointed OD_RESOURCE_ROOT at /etc or an arbitrary absolute dir; a packaged run inherited a dev-time OD_RESOURCE_ROOT pointing outside Contents/Resources; a symlinked resources dir resolves outside the safe base.","solutions":["Set OD_RESOURCE_ROOT to a directory inside the workspace root or the app resources path.","Unset OD_RESOURCE_ROOT to let the daemon derive resources from the workspace / packaged resources path.","If a symlink is involved, resolve it to its real target and ensure that target is inside a safe base."],"exampleFix":"# before: pointing outside the workspace\nexport OD_RESOURCE_ROOT=/opt/my-resources\n\n# after: keep it inside the workspace (or unset)\nexport OD_RESOURCE_ROOT=$PWD/resources\n# or simply: unset OD_RESOURCE_ROOT","handlingStrategy":"validation","validationCode":"function assertResourceRootInSafeBase(configured: string | undefined, safeBases: string[]) {\n  if (!configured) return null;\n  const resolved = path.resolve(configured);\n  if (!safeBases.some(b => isPathWithin(b, resolved))) {\n    throw new Error('OD_RESOURCE_ROOT must be under workspace or app resources path');\n  }\n  return resolved;\n}","typeGuard":"function isWithinAnyBase(target: string, bases: string[]): boolean {\n  return bases.some(b => isPathWithin(b, target));\n}","tryCatchPattern":"try { resolveDaemonResourceRoot({ safeBases }); }\ncatch (e) {\n  if (e instanceof Error && /OD_RESOURCE_ROOT/.test(e.message)) {\n    // unset OD_RESOURCE_ROOT and fall back to derived resources path\n  } else throw e;\n}","preventionTips":["Only set OD_RESOURCE_ROOT to a dir inside the workspace root or packaged resources path.","Resolve symlinks before configuring OD_RESOURCE_ROOT.","Leave it unset in dev unless you need a specific resources override."],"tags":["daemon","paths","security","env-vars","path-traversal"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}