{"record":{"id":"eb6070edf8013304","repo":"garrytan/gstack","slug":"path-must-be-within-temp-only-join-remo","errorCode":null,"errorMessage":"Path must be within: ${TEMP_ONLY.join(', ')} (remote file serving is restricted to temp directory)","messagePattern":"Path must be within: (.+?) \\(remote file serving is restricted to temp directory\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/path-security.ts","lineNumber":115,"sourceCode":"    throw new Error(`Path must be within: ${SAFE_DIRECTORIES.join(', ')}`);\n  }\n}\n\n/** Validate a file path for remote serving (GET /file). TEMP_DIR only, not cwd. */\nexport function validateTempPath(filePath: string): void {\n  const resolved = path.resolve(filePath);\n  let realPath: string;\n  try {\n    realPath = fs.realpathSync(resolved);\n  } catch (err: any) {\n    if (err.code === 'ENOENT') {\n      throw new Error('File not found');\n    }\n    throw new Error(`Cannot resolve path: ${filePath}`);\n  }\n  const isSafe = TEMP_ONLY.some(dir => isPathWithin(realPath, dir));\n  if (!isSafe) {\n    throw new Error(`Path must be within: ${TEMP_ONLY.join(', ')} (remote file serving is restricted to temp directory)`);\n  }\n}\n\n/** Escape special regex metacharacters in a user-supplied string to prevent ReDoS. */\nexport function escapeRegExp(s: string): string {\n  return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n","sourceCodeStart":97,"sourceCodeEnd":123,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/path-security.ts#L97-L123","documentation":"Thrown by validateTempPath as its safety check: GET /file (remote serving) is restricted to TEMP_DIR ONLY — cwd is deliberately excluded to prevent project file exfiltration by remote agents. If the resolved real path is not within TEMP_ONLY, this throws. This is stricter than the local-command policy (which allows cwd) on purpose.","triggerScenarios":"A remote agent requests a file that resolves outside TEMP_DIR: a project source file, /etc/passwd, a file in cwd, or a symlink that escapes TEMP_DIR after realpath resolution.","commonSituations":"Confusion between local-allowed paths (TEMP_DIR + cwd) and remote-allowed paths (TEMP_DIR only); trying to serve a project file to a remote agent; symlink in TEMP_DIR pointing into the project.","solutions":["Write or copy the artifact into TEMP_DIR before serving it remotely","Use a local command (not GET /file) to read project files — remote serving is temp-only by design","Confirm the path is under the system temp directory (os.tmpdir()), not cwd","Remove any symlinks in TEMP_DIR that point outside it"],"exampleFix":"// before: trying to serve a project file remotely\nGET /file?path=./src/secret.env  // throws\n\n// after\ncp ./src/secret.env \"$TMPDIR/secret.env\" && GET /file?path=$TMPDIR/secret.env","handlingStrategy":"validation","validationCode":"import * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\n\nconst TEMP_ONLY = (() => { try { return fs.realpathSync(os.tmpdir()); } catch { return os.tmpdir(); } })();\n\nfunction isWithinTempOnly(p: string): boolean {\n  let real: string;\n  try { real = fs.realpathSync(path.resolve(p)); }\n  catch (e: any) {\n    if (e.code === 'ENOENT') return false; // remote serving requires the file to exist\n    return false;\n  }\n  return real === TEMP_ONLY || real.startsWith(TEMP_ONLY + path.sep);\n}\n\nif (!isWithinTempOnly(filePath)) {\n  return { status: 403, body: 'Remote serving is restricted to the temp directory' };\n}","typeGuard":"function isTempOnlyPath(p: string): boolean {\n  const real = fs.realpathSync(path.resolve(p));\n  return real === TEMP_ONLY || real.startsWith(TEMP_ONLY + path.sep);\n}","tryCatchPattern":"try {\n  return serveTempFile(filePath);\n} catch (e: any) {\n  if (/restricted to temp directory/.test(e.message)) {\n    // copy the artifact into TEMP_DIR and redirect\n    const safe = path.join(os.tmpdir(), path.basename(filePath));\n    fs.copyFileSync(filePath, safe);\n    return serveTempFile(safe);\n  }\n  throw e;\n}","preventionTips":["Stage all remotely-served artifacts in TEMP_DIR — never serve project files directly","Remember the remote policy is stricter than the local one (no cwd)","Remove symlinks in TEMP_DIR that point outside it","Validate the request path against os.tmpdir() before touching the filesystem"],"tags":["security","path-traversal","remote-serving","sandbox","exfiltration","temp-dir"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}