{"record":{"id":"03844005c88aafae","repo":"quasarframework/quasar","slug":"refusing-to-remove-a-filesystem-root-as-build-outp","errorCode":null,"errorMessage":"Refusing to remove a filesystem root as build output","messagePattern":"Refusing to remove a filesystem root as build output","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"app-vite/lib/utils/remove-build-artifacts.js","lineNumber":72,"sourceCode":"  projectDir,\n  allowOutsideProject\n}) {\n  if (typeof targetDir !== 'string' || targetDir.trim() === '') {\n    throw new Error('Build output directory must be a non-empty path')\n  }\n\n  if (typeof projectDir !== 'string' || projectDir.trim() === '') {\n    throw new Error('Project directory must be a non-empty path')\n  }\n\n  const project = resolve(projectDir)\n  const effectiveProject = fse.realpathSync(project)\n  const home = fse.realpathSync(resolve(homedir()))\n  const target = resolve(projectDir, targetDir)\n  const effectiveTarget = getEffectivePath(target)\n\n  if (isFilesystemRoot(target) || isFilesystemRoot(effectiveTarget)) {\n    throw new Error('Refusing to remove a filesystem root as build output')\n  }\n\n  if (target === home || effectiveTarget === home) {\n    throw new Error(\n      'Refusing to remove the user home directory as build output'\n    )\n  }\n\n  if (target === project || effectiveTarget === effectiveProject) {\n    throw new Error('Refusing to remove the project root as build output')\n  }\n\n  if (\n    allowOutsideProject !== true &&\n    isContainedPath(effectiveProject, effectiveTarget) === false\n  ) {\n    throw new Error(\n      'Build output directory must remain inside the project. Set build.allowOutsideProjectDistDir to true to explicitly allow an external directory.'","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/quasarframework/quasar/blob/4841521b5f635a971eb9e2710e5542efd194691b/app-vite/lib/utils/remove-build-artifacts.js#L54-L90","documentation":"As a destructive-operation guard, getBuildArtifactsCleanTarget refuses to treat a filesystem root ('/' on POSIX, drive root on Windows) as build output. It compares both the raw resolved target and its realpath-resolved effective path (catching symlinks that point at a root) against isFilesystemRoot.","triggerScenarios":"Configuring the build output directory as '/' (or 'C:/' etc.), or as a path that resolves via symlinks to a filesystem root — e.g. distDir pointing at a symlink named /dist that actually targets /.","commonSituations":"Setting distDir: '/' by mistake in quasar.config.js; a symlink farm or Docker volume mount mapping the dist dir to the container root; reading an empty environment variable that collapses the path to root after resolve.","solutions":["Set distDir to a real subdirectory like 'dist' or 'build/output'","Check for symlinks in the configured path ('ls -l') and repoint them away from the root","Never interpolate unvalidated env vars directly into distDir; default to 'dist' when empty"],"exampleFix":"// before\ndistDir: process.env.DIST_DIR || '/'\n// after\ndistDir: process.env.DIST_DIR || 'dist'","handlingStrategy":"validation","validationCode":"const path = require('node:path')\nconst { parse } = require('node:path')\nfunction isFsRoot(p) {\n  const parsed = parse(path.resolve(p))\n  return parsed.root === path.resolve(p) && (parsed.dir === parsed.root)\n}\nconst target = path.resolve(projectDir, distDir)\nif (isFsRoot(target)) throw new Error('dist dir resolves to filesystem root')","typeGuard":null,"tryCatchPattern":"try {\n  await removeBuildArtifacts(opts)\n} catch (err) {\n  if (err.message === 'Refusing to remove a filesystem root as build output') {\n    // fix distDir config; never bypass this guard\n  } else throw err\n}","preventionTips":["Never set distDir to '/', a drive root, or raw unvalidated env vars","Default to a relative 'dist' directory","Check symlinks ('readlink -f') when distDir is an absolute path","Treat this error as a config bug, not something to catch and continue"],"tags":["safety","filesystem","path-traversal"],"backgroundTag":"unsafe-delete-path","analyzedSha":"4841521b5f635a971eb9e2710e5542efd194691b","analyzedAt":"2026-08-30T01:13:14.944Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}