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

  1. Write or copy the artifact into TEMP_DIR before serving it remotely
  2. Use a local command (not GET /file) to read project files — remote serving is temp-only by design
  3. Confirm the path is under the system temp directory (os.tmpdir()), not cwd
  4. 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

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


AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12). Data as JSON: /api/errors/eb6070edf8013304. Report an issue: GitHub.