{"record":{"id":"b04546192c62cd6e","repo":"pbakaus/impeccable","slug":"svelte-component-source-file-escapes-project-root-b04546","errorCode":null,"errorMessage":"Svelte-component source file escapes project root","messagePattern":"Svelte-component source file escapes project root","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"skill/scripts/live/svelte-component.mjs","lineNumber":449,"sourceCode":"  return null;\n}\n\nexport function readManifest(manifestPath) {\n  const data = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));\n  return {\n    ...data,\n    manifestPath,\n  };\n}\n\nexport function resolveSourceFile(sourceFile, cwd = process.cwd()) {\n  if (!sourceFile || path.isAbsolute(sourceFile)) {\n    throw new Error('Invalid svelte-component source file');\n  }\n  const full = path.resolve(cwd, sourceFile);\n  const rel = path.relative(cwd, full);\n  if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) {\n    throw new Error('Svelte-component source file escapes project root');\n  }\n  if (!fs.existsSync(full)) {\n    throw new Error('Svelte-component source file not found: ' + sourceFile);\n  }\n  return full;\n}\n\nfunction appendCssToSvelteStyle(lines, cssLines) {\n  const closeIdx = findLastStyleCloseLine(lines);\n  const prepared = ['', ...cssLines.map((line) => (line.trim() === '' ? '' : '  ' + line.trimStart()))];\n  if (closeIdx === -1) {\n    return [...lines, '', '<style>', ...prepared.slice(1), '</style>'];\n  }\n  return [\n    ...lines.slice(0, closeIdx),\n    ...prepared,\n    ...lines.slice(closeIdx),\n  ];","sourceCodeStart":431,"sourceCodeEnd":467,"githubUrl":"https://github.com/pbakaus/impeccable/blob/d14711ae3d1a1dd62dee61a358d27f107c51ccd0/skill/scripts/live/svelte-component.mjs#L431-L467","documentation":"Thrown by resolveSourceFile() in live/svelte-component.mjs when the resolved relative path escapes the project root (starts with '..') or is absolute after path.relative(). This is the path-traversal sandbox guard: it prevents a sourceFile like '../../etc/passwd' or a symlink-anchored absolute resolution from reading outside cwd. It runs after the emptiness/absolute check and before the existence check.","triggerScenarios":"resolveSourceFile(sourceFile, cwd) is called with a relative path containing '../' segments that resolve above cwd, e.g. '../shared/Foo.svelte' when cwd is /proj/app and the target lands in /proj/shared (outside /proj/app). Also triggered by absolute rel results on edge-case roots.","commonSituations":"Monorepo component lives in a sibling package and the caller passes a workspace-relative path instead of one relative to the configured cwd; cwd was set incorrectly (too narrow); user attempts to import a component from outside the project on purpose (not supported — the adapter scopes to the project).","solutions":["Point sourceFile at a path inside the configured project root (cwd).","If the component genuinely lives in a sibling package, set cwd to the workspace root that contains both, or copy/symlink the component into the project.","Remove leading '../' segments and confirm the resolved file sits under cwd."],"exampleFix":"// before (cwd = /proj/app)\nresolveSourceFile('../shared/Foo.svelte', cwd)\n// after (cwd = /proj)\nresolveSourceFile('shared/Foo.svelte', cwd)","handlingStrategy":"validation","validationCode":"const full = path.resolve(cwd, sourceFile);\nconst rel = path.relative(cwd, full);\nif (!rel || rel.startsWith('..') || path.isAbsolute(rel)) {\n  throw new Error('sourceFile escapes project root: ' + sourceFile);\n}","typeGuard":"function isWithinRoot(p: string, root: string): boolean {\n  const rel = path.relative(root, path.resolve(root, p));\n  return !!rel && !rel.startsWith('..') && !path.isAbsolute(rel);\n}","tryCatchPattern":null,"preventionTips":["Configure cwd as the workspace root that contains every component you reference.","Reject '../' in user-supplied component paths at the input boundary.","Document that the adapter is project-scoped by design."],"tags":["svelte","path","security","traversal","live-adapter"],"backgroundTag":null,"analyzedSha":"d14711ae3d1a1dd62dee61a358d27f107c51ccd0","analyzedAt":"2026-08-13T00:52:25.771Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}