can1357/oh-my-pi · error · Error

SARIF artifact resolves outside the repository: ${uri}

Error message

SARIF artifact resolves outside the repository: ${uri}

What it means

Even when the URI resolves to a file URL, the importer verifies the resulting absolute path (and its realpath) stays within the repository root. This prevents SARIF findings from escaping the repository via ".." segments or symlinks, and throws when the resolved or canonical path lies outside.

Source

Thrown at packages/coding-agent/src/security/importers/sarif.ts:121

): Promise<string> {
	const uri = artifact.uri;
	if (!uri) throw new Error("SARIF artifact location is missing its URI");
	const rootUrl = pathToFileURL(`${repositoryRoot}${path.sep}`);
	let baseUrl = rootUrl;
	if (artifact.uriBaseId) {
		const declaredBase = run.originalUriBaseIds?.[artifact.uriBaseId]?.uri;
		if (!declaredBase && artifact.uriBaseId !== "%SRCROOT%") {
			throw new Error(`SARIF artifact uses an unknown URI base: ${artifact.uriBaseId}`);
		}
		baseUrl = declaredBase ? new URL(declaredBase, rootUrl) : rootUrl;
	}
	const resolvedUrl = new URL(uri.replaceAll("\\", "/"), baseUrl);
	if (resolvedUrl.protocol !== "file:") {
		throw new Error(`SARIF artifact URI must resolve to a repository file: ${uri}`);
	}
	const absolute = path.resolve(fileURLToPath(resolvedUrl));
	if (!pathIsWithin(absolute, repositoryRoot)) {
		throw new Error(`SARIF artifact resolves outside the repository: ${uri}`);
	}
	const canonical = await fs.realpath(absolute).catch(error => {
		if (error instanceof Error && "code" in error && error.code === "ENOENT") return absolute;
		throw error;
	});
	if (!pathIsWithin(canonical, repositoryRoot)) {
		throw new Error(`SARIF artifact resolves outside the repository through a symbolic link: ${uri}`);
	}
	return path.relative(repositoryRoot, canonical).replaceAll(path.sep, "/");
}

async function normalizeSarifLocations(
	result: SarifResult,
	run: SarifRun,
	repositoryRoot: string,
): Promise<SecurityLocation[]> {
	const locations: SecurityLocation[] = [];
	for (const item of result.locations ?? []) {

View on GitHub (pinned to 9690622007)

Solutions

  1. Rewrite the artifact uris to be genuinely repository-relative with no ".." escapes
  2. Re-run the scan inside the repository so paths resolve within the root
  3. Remove or retarget symlinks whose realpath leaves the repository, or exclude those artifacts

Example fix

// before
"uri": "../../shared/lib/util.ts"
// after: copy/link within repo and reference
"uri": "packages/shared/lib/util.ts"
Defensive patterns

Strategy: validation

Validate before calling

import * as path from "node:path";
function pathIsWithin(candidate: string, root: string): boolean {
  const rel = path.relative(root, candidate);
  return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
}
const abs = path.resolve(repoRoot, uri);
if (!pathIsWithin(abs, repoRoot)) throw new Error(`URI escapes repository: ${uri}`);

Type guard

function withinRepo(absolutePath: string, repoRoot: string): boolean {
  const rel = path.relative(repoRoot, absolutePath);
  return rel !== "" && !rel.startsWith("..") && !path.isAbsolute(rel);
}

Try / catch

try {
  const bundle = await importSarif(sarifDir, repoRoot);
} catch (err) {
  if (err instanceof Error && err.message.startsWith("SARIF artifact resolves outside the repository")) {
    console.error(err.message + " — rewrite the URI or re-run the scan inside the repository");
  } else throw err;
}

Prevention

When it happens

Trigger: resolveSarifArtifactPath computes an absolute path outside repositoryRoot (e.g. uri "../../etc/passwd") or fs.realpath resolves a symlink to a target outside the repo.

Common situations: SARIF generated on a machine with the repo nested deeper, leaving ../..-style relative URIs; symlinked source files pointing into other directories; malicious or buggy scanner URIs.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/badee92b8e3fdcc9. Report an issue: GitHub.