MCPcopy
hub / github.com/21st-dev/1code / assertDanglingSymlinkSafe

Function assertDanglingSymlinkSafe

src/main/lib/git/security/secure-fs.ts:224–273  ·  view source on GitHub ↗

* Handle the ENOENT case: check if fullPath is a dangling symlink pointing outside * the worktree, or if it truly doesn't exist (in which case validate parent chain). * * Attack scenario this prevents: * - Repo contains `docs/config.yml` → symlink to `~/.ssh/some_new_file` (doesn't exist) * - r

(
	worktreePath: string,
	fullPath: string,
)

Source from the content-addressed store, hash-verified

222 * @throws PathValidationError if symlink escapes worktree
223 */
224async function assertDanglingSymlinkSafe(
225 worktreePath: string,
226 fullPath: string,
227): Promise<void> {
228 const worktreeReal = await realpath(worktreePath);
229
230 try {
231 // Check if the path itself exists (as a symlink or otherwise)
232 const stats = await lstat(fullPath);
233
234 if (stats.isSymbolicLink()) {
235 // It's a dangling symlink - validate where it points
236 const linkTarget = await readlink(fullPath);
237 const resolvedTarget = isAbsolute(linkTarget)
238 ? linkTarget
239 : resolve(dirname(fullPath), linkTarget);
240
241 // Check if the resolved target would be within worktree
242 // For dangling symlinks, we can't use realpath on the target,
243 // so we check the literal resolved path
244 const targetRelative = relative(worktreeReal, resolvedTarget);
245 if (
246 targetRelative === ".." ||
247 targetRelative.startsWith(`..${sep}`) ||
248 isAbsolute(targetRelative)
249 ) {
250 throw new PathValidationError(
251 "Dangling symlink points outside the worktree",
252 "SYMLINK_ESCAPE",
253 );
254 }
255 // Dangling symlink points within worktree - allow the operation
256 return;
257 }
258
259 // Not a symlink but lstat succeeded - weird state, but validate parent chain
260 await assertParentInWorktree(worktreePath, fullPath);
261 } catch (error) {
262 if (error instanceof PathValidationError) {
263 throw error;
264 }
265 if (error instanceof Error && "code" in error && error.code === "ENOENT") {
266 // Path truly doesn't exist (not even as a symlink) - validate parent chain
267 await assertParentInWorktree(worktreePath, fullPath);
268 return;
269 }
270 // Other errors - fail closed
271 throw new PathValidationError("Cannot validate path", "SYMLINK_ESCAPE");
272 }
273}
274export const secureFs = {
275 /**
276 * Read a file within a worktree.

Callers 1

assertRealpathInWorktreeFunction · 0.85

Calls 2

lstatFunction · 0.85
assertParentInWorktreeFunction · 0.85

Tested by

no test coverage detected