can1357/oh-my-pi · error · ArchiveError

Archive path cannot contain '..'

Error message

Archive path cannot contain '..'

What it means

Path-safety guard in `listDirectory`: `normalizeArchiveLookupPath(subPath)` returned undefined because the supplied path contains a `..` component, which this library forbids to prevent path-traversal-style lookups. Thrown as ArchiveError before any directory resolution happens (packages/utils/src/ar/reader.ts:76).

Source

Thrown at packages/utils/src/ar/reader.ts:76

		if (resolvedPath === "") {
			return { path: normalizedPath, isDirectory: true, size: 0 };
		}
		const entry = this.#entries.get(resolvedPath);
		if (!entry) return undefined;
		return {
			path: normalizedPath,
			isDirectory: entry.isDirectory,
			size: entry.size,
			mtimeMs: entry.mtimeMs,
			mode: entry.mode,
		};
	}

	/** List one directory's children, sorted case-insensitively by name. */
	listDirectory(subPath?: string): ArchiveDirectoryEntry[] {
		const normalizedPath = normalizeArchiveLookupPath(subPath);
		if (normalizedPath === undefined) {
			throw new ArchiveError("Archive path cannot contain '..'");
		}

		const resolvedPath = normalizedPath
			? resolveArchiveLinkPath(this.#entries, normalizedPath, this.limits.maxLinkDepth)
			: "";
		if (normalizedPath && resolvedPath !== "") {
			const entry = this.#entries.get(resolvedPath);
			if (!entry) {
				throw new ArchiveError(`Archive path '${normalizedPath}' not found`);
			}
			if (!entry.isDirectory) {
				throw new ArchiveError(`Archive path '${normalizedPath}' is not a directory`);
			}
		}

		const sourcePrefix = resolvedPath ? `${resolvedPath}/` : "";
		const children = new Map<string, ArchiveDirectoryEntry>();

View on GitHub (pinned to 9690622007)

Solutions

  1. Remove or reject '..' segments before calling: normalize the requested path (e.g. path.posix.normalize and refuse if it escapes the root).
  2. Call listDirectory with undefined or a rooted relative path ('sub/dir') — top-level listing uses undefined, not '..' or '/'.
  3. Sanitize untrusted input at the boundary: strip leading slashes and collapse '.' segments, then re-check for '..'.
  4. Catch this ArchiveError and show the user that upward traversal is not allowed inside archives.

Example fix

// before: user input passed straight through
reader.listDirectory(userPath); // '../etc' -> throws

// after: normalize and reject traversal first
const clean = path.posix.normalize(userPath.replace(/^\/+/, ''));
if (clean.split('/').includes('..')) {
  throw new Error('Directory traversal is not allowed');
}
reader.listDirectory(clean === '.' ? undefined : clean);
Defensive patterns

Strategy: validation

Validate before calling

// Sanitize user-supplied subPath before calling listDirectory
function safeArchivePath(input: string): string {
  const clean = path.posix.normalize(input.replace(/^\/+/, ''));
  if (clean === '.' || clean === '..' || clean.split('/').includes('..')) {
    throw new Error(`Path escapes the archive root: ${input}`);
  }
  return clean;
}
reader.listDirectory(safeArchivePath(userPath));

Type guard

function isTraversalFree(p: string): boolean {
  return !p.split('/').includes('..');
}

Try / catch

try {
  return reader.listDirectory(userPath);
} catch (err) {
  if (err instanceof ArchiveError && err.message === "Archive path cannot contain '..'") {
    throw new Error(`Invalid directory request (traversal blocked): ${userPath}`);
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling `reader.listDirectory('../sibling')`, `'a/../../b'`, or passing any subPath containing '..' segments; also occurs when user-supplied input with '..' flows into listDirectory (directly or via allEntries).

Common situations: Building UI paths from user input without sanitizing; joining a user-chosen folder onto a base path such that '..' appears; code that treats archive paths like filesystem-relative paths and walks upward.

Related errors


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