Kong/insomnia · error

"extends" target must stay within the ruleset's root…

Error message

"extends" target must stay within the ruleset's root directory: ${absolute}

What it means

Path-traversal guard in assertAllowed while bundling a Spectral ruleset: resolving a local 'extends' entry produced a path outside the directory of the originally-selected ruleset (relative path starts with '..'), so loading is refused. The escaping path is interpolated.

Solutions

  1. Keep all extends targets inside the ruleset's directory
  2. Reference remote rulesets via https URLs if cross-directory sharing is needed
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at packages/insomnia/src/main/bundle-spectral-ruleset.ts:39 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of Kong/insomnia@d9bb2b0142 (2026-08-26). Data as JSON: /api/errors/4b5a2a4f361fedfc. Report an issue: GitHub.

Appendix: source

Thrown at packages/insomnia/src/main/bundle-spectral-ruleset.ts:39

// Safety checks for local-file extends entries:
// - Depth / cycle guard against infinite recursion.
// - Extension check ensures we only load YAML files.
// - rootDir guard prevents path traversal (e.g. '../../../etc/passwd') from
//   reaching files outside the directory of the originally-selected ruleset.
function assertAllowed(absolute: string, visited: Set<string>, depth: number, rootDir: string): void {
  if (depth > MAX_EXTENDS_DEPTH) {
    throw new Error(`"extends" nested too deeply (max ${MAX_EXTENDS_DEPTH}) at ${absolute}`);
  }
  if (visited.has(absolute)) {
    throw new Error(`"extends" cycle detected at ${absolute}`);
  }
  if (!ALLOWED_EXTENSIONS.includes(path.extname(absolute).toLowerCase())) {
    throw new Error(`"extends" target must be a .yaml or .yml file: ${absolute}`);
  }
  const rel = path.relative(rootDir, absolute);
  if (rel.startsWith('..') || path.isAbsolute(rel)) {
    throw new Error(`"extends" target must stay within the ruleset's root directory: ${absolute}`);
  }
}

// Reads a local ruleset file from disk and parses it.
async function readRuleset(absolute: string): Promise<Ruleset> {
  const raw = await fs.promises.readFile(absolute, { encoding: 'utf8' });
  const parsed = YAML.parse(raw);
  if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
    throw new Error(`Ruleset at ${absolute} must be an object at the top level.`);
  }
  return parsed as Ruleset;
}

function isPlainObject(value: unknown): value is Record<string, unknown> {
  return value !== null && typeof value === 'object' && !Array.isArray(value);
}

// Shallow-merges top-level keys from source into target.

View on GitHub (pinned to d9bb2b0142)