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
- Rewrite the artifact uris to be genuinely repository-relative with no ".." escapes
- Re-run the scan inside the repository so paths resolve within the root
- 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
- Run the scanner with the repository root as its working directory so relative URIs stay inside
- Reject uris containing ".." segments in a pre-import pass
- Check symlink targets: realpath must stay within the repository
- Treat out-of-repo artifact URIs from untrusted SARIF as a security signal, not a formatting nit
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
- Destination paths cannot contain parent traversal or NUL byt
- Shared-folder destination escapes its configured root
- Refusing to download outside the workspace: ${downloadPath}
- Absolute paths are not allowed in ${scheme}:// URLs: ${rawPa
- Path traversal (..) is not allowed in ${scheme}:// URLs: ${r
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/badee92b8e3fdcc9.
Report an issue: GitHub.