ruvnet/RuView · error · Error

Refusing CLI access: repository marker escapes the trusted…

Error message

Refusing CLI access: repository marker escapes the trusted root

What it means

Homecore's repo-trust module realpaths the README.md marker and verifies the resolved path stays inside the trusted root before reading it. If README.md is a symlink whose target resolves outside the root, this security error aborts CLI access, preventing the trust check from reading arbitrary files outside the checkout.

Solutions

  1. Replace the README.md symlink with a real file inside the repository (copy the content in)
  2. If linking is required, link to a target still inside the checkout and re-run
  3. Confirm where it resolves: readlink -f README.md

Example fix

# before
README.md -> /home/me/shared/README.md   (symlink escaping the repo)

# after
$ rm README.md && cp /home/me/shared/README.md ./README.md   (regular file inside the repo)
Defensive patterns

Strategy: validation

Validate before calling

import { realpathSync } from 'node:fs';
import { isAbsolute, join, relative } from 'node:path';
function readmeStaysInRoot(root) {
  try {
    const realRoot = realpathSync(root);
    const real = realpathSync(join(root, 'README.md'));
    const rel = relative(realRoot, real);
    return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
  } catch {
    return false;
  }
}

Prevention

When it happens

Trigger: README.md in the checkout root is a symlink to a file outside the repository (shared docs folder, home directory, another checkout). readContainedPrefix realpaths it, isWithin(root, real) fails, and assertTrustedHomecoreRepo throws during trust verification.

Common situations: Repos linking README.md to an absolute external path, dotfile-managed checkouts, monorepos sharing a README by symlink, test fixtures built with symlinks.

Related errors


AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16). Data as JSON: /api/errors/55fc6c77e6f2f261. Report an issue: GitHub.

Appendix: source

Thrown at harness/homecore/src/repo-trust.js:30

const REQUIRED_MARKERS = Object.freeze([
  '.git',
  'README.md',
  'v2/Cargo.toml',
  'v2/crates/homecore/Cargo.toml',
  'v2/crates/homecore-server/Cargo.toml',
  'docs/adr/ADR-126-ruview-native-ha-port-master.md',
]);

function isWithin(parent, child) {
  const rel = relative(parent, child);
  return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
}

function readContainedPrefix(root, path, maxBytes) {
  const real = realpathSync(path);
  if (!isWithin(root, real)) {
    throw new Error('Refusing CLI access: repository marker escapes the trusted root');
  }
  const stat = statSync(real);
  if (!stat.isFile()) {
    throw new Error('Refusing CLI access: README marker is not a regular file');
  }
  const buffer = Buffer.alloc(Math.min(stat.size, maxBytes));
  const descriptor = openSync(real, 'r');
  try {
    const bytes = readSync(descriptor, buffer, 0, buffer.length, 0);
    return buffer.subarray(0, bytes).toString('utf8');
  } finally {
    closeSync(descriptor);
  }
}

export function looksLikeHomecoreRepo(path) {
  if (!path || !existsSync(path)) return false;
  return REQUIRED_MARKERS.every((marker) => existsSync(join(path, marker)));

View on GitHub (pinned to 4685618388)