Fission-AI/OpenSpec · error

Path is outside the allowed directory

Error message

Path is outside the allowed directory: ${specPath}

What it means

Security guard in resolveTrustedSpecPath: the requested spec path escapes the allowed specs root when compared lexically (path.relative yields '..' or an absolute). This blocks path-traversal attempts where a delta or update names a file outside the trusted specs directory.

Solutions

  1. Ensure the spec path is relative to and stays inside the specs root
  2. Remove '../' segments or absolute paths from the spec reference
  3. Audit the change spec for injected traversal sequences if the path was not user-supplied
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at src/core/specs-apply.ts:59 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of Fission-AI/OpenSpec@6926ccb18a (2026-08-25). Data as JSON: /api/errors/1a22f74613f12778. Report an issue: GitHub.

Appendix: source

Thrown at src/core/specs-apply.ts:59

  exists: boolean;
}

function isLexicallyWithin(allowedDirectory: string, targetPath: string): boolean {
  const relative = path.relative(path.resolve(allowedDirectory), path.resolve(targetPath));
  return (
    relative === '' ||
    (relative !== '..' &&
      !relative.startsWith(`..${path.sep}`) &&
      !path.isAbsolute(relative))
  );
}

function resolveTrustedSpecPath(specsRoot: string, specPath: string): {
  root: string;
  file: string;
} {
  if (!isLexicallyWithin(specsRoot, specPath)) {
    throw new Error(`Path is outside the allowed directory: ${specPath}`);
  }

  try {
    // Preserve spec.md links that remain inside the overall specs tree.
    FileSystemUtils.assertPathWithin(specsRoot, specPath);
    const root = FileSystemUtils.canonicalizeExistingPath(specsRoot);
    return {
      root,
      // Rebase onto the canonical root so missing targets also work when the
      // project is reached through an OS path alias (for example /var on macOS).
      file: path.join(root, path.relative(path.resolve(specsRoot), path.resolve(specPath))),
    };
  } catch {
    // Direct capability directories may intentionally be monorepo symlinks.
    // Freeze their canonical location as the trust root so later swaps are
    // rejected while a nested spec.md link still cannot escape.
    const root = FileSystemUtils.canonicalizeExistingPath(path.dirname(specPath));
    const file = path.join(root, path.basename(specPath));

View on GitHub (pinned to 6926ccb18a)