{"record":{"id":"b9d8c3be4d32a9bb","repo":"pbakaus/impeccable","slug":"svelte-component-source-file-escapes-project-root","errorCode":null,"errorMessage":"Svelte-component source file escapes project root","messagePattern":"Svelte-component source file escapes project root","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"plugin/skills/impeccable/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/plugin/skills/impeccable/scripts/live/svelte-component.mjs#L431-L467","documentation":"Thrown by resolveSourceFile after resolving the relative path: the result, when expressed relative to cwd, starts with '..' or is absolute, meaning the file escapes the project root. The guard prevents path-traversal writes outside the project during svelte-component patching.","triggerScenarios":"sourceFile like '../../etc/passwd' or a symlink-adjacent relative path whose resolved location sits above cwd. The first guard (absolute) is not tripped, but this escape check is.","commonSituations":"User-supplied input with traversal segments, or a cwd that does not match the project root the path was computed against.","solutions":["Resolve against the correct project root: pass cwd matching where the path is relative to.","Sanitize the input to reject '..' segments before calling resolveSourceFile.","If traversal is intentional for a monorepo, run from a cwd that legitimately contains the file."],"exampleFix":"// before\nresolveSourceFile('../../shared/Comp.svelte', cwd);\n\n// after: run from a cwd that contains the target\nresolveSourceFile('packages/shared/Comp.svelte', projectRoot);","handlingStrategy":"validation","validationCode":"function isWithinRoot(sourceFile, cwd) {\n  const full = path.resolve(cwd, sourceFile);\n  const rel = path.relative(cwd, full);\n  return !!rel && !rel.startsWith('..') && !path.isAbsolute(rel);\n}","typeGuard":"function isContainedPath(sourceFile, cwd) {\n  if (!sourceFile || path.isAbsolute(sourceFile)) return false;\n  const rel = path.relative(cwd, path.resolve(cwd, sourceFile));\n  return !!rel && !rel.startsWith('..') && !path.isAbsolute(rel);\n}","tryCatchPattern":"try {\n  resolveSourceFile(sourceFile, cwd);\n} catch (err) {\n  if (err.message.includes('escapes project root')) {\n    throw new Error(`refusing path outside project: ${sourceFile}`);\n  } else throw err;\n}","preventionTips":["Sanitize user-supplied paths: reject any segment equal to '..'.","Resolve symlinks before the containment check if symlinks are allowed.","Lock cwd to the project root at process start."],"tags":["path","security","svelte","validation"],"backgroundTag":null,"analyzedSha":"d14711ae3d1a1dd62dee61a358d27f107c51ccd0","analyzedAt":"2026-08-13T00:52:25.771Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}