garrytan/gstack · error · Error
Path must be within: ${TEMP_ONLY.join(', ')} (remote file se
Error message
Path must be within: ${TEMP_ONLY.join(', ')} (remote file serving is restricted to temp directory) What it means
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.
Source
Thrown at browse/src/path-security.ts:115
throw new Error(`Path must be within: ${SAFE_DIRECTORIES.join(', ')}`);
}
}
/** Validate a file path for remote serving (GET /file). TEMP_DIR only, not cwd. */
export function validateTempPath(filePath: string): void {
const resolved = path.resolve(filePath);
let realPath: string;
try {
realPath = fs.realpathSync(resolved);
} catch (err: any) {
if (err.code === 'ENOENT') {
throw new Error('File not found');
}
throw new Error(`Cannot resolve path: ${filePath}`);
}
const isSafe = TEMP_ONLY.some(dir => isPathWithin(realPath, dir));
if (!isSafe) {
throw new Error(`Path must be within: ${TEMP_ONLY.join(', ')} (remote file serving is restricted to temp directory)`);
}
}
/** Escape special regex metacharacters in a user-supplied string to prevent ReDoS. */
export function escapeRegExp(s: string): string {
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
View on GitHub (pinned to 94993f7401)
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
Example fix
// before: trying to serve a project file remotely GET /file?path=./src/secret.env // throws // after cp ./src/secret.env "$TMPDIR/secret.env" && GET /file?path=$TMPDIR/secret.env
Defensive patterns
Strategy: validation
Validate before calling
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
const TEMP_ONLY = (() => { try { return fs.realpathSync(os.tmpdir()); } catch { return os.tmpdir(); } })();
function isWithinTempOnly(p: string): boolean {
let real: string;
try { real = fs.realpathSync(path.resolve(p)); }
catch (e: any) {
if (e.code === 'ENOENT') return false; // remote serving requires the file to exist
return false;
}
return real === TEMP_ONLY || real.startsWith(TEMP_ONLY + path.sep);
}
if (!isWithinTempOnly(filePath)) {
return { status: 403, body: 'Remote serving is restricted to the temp directory' };
} Type guard
function isTempOnlyPath(p: string): boolean {
const real = fs.realpathSync(path.resolve(p));
return real === TEMP_ONLY || real.startsWith(TEMP_ONLY + path.sep);
} Try / catch
try {
return serveTempFile(filePath);
} catch (e: any) {
if (/restricted to temp directory/.test(e.message)) {
// copy the artifact into TEMP_DIR and redirect
const safe = path.join(os.tmpdir(), path.basename(filePath));
fs.copyFileSync(filePath, safe);
return serveTempFile(safe);
}
throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Path must be within: ${SAFE_DIRECTORIES.join(', ')}
- Path must be within: ${safeDirs.join(', ')}
- State name must be alphanumeric (a-z, 0-9, _, -)
- File not found
- Cannot resolve path: ${filePath}
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/eb6070edf8013304.
Report an issue: GitHub.