fix(DurableExecution): resolve symlinks at every path component in FileSystemSerializer containment check - #2563
Open
GarrettBeatty wants to merge 1 commit into
Conversation
…leSystemSerializer containment check IsWithinBase resolved the base path through ResolveReal, which only followed a symlink on the leaf and its immediate parent. When the configured base path itself is a symlink (e.g. an EFS mount exposed as /mnt/link -> /mnt/real), the base resolved to /mnt/real while candidate paths built under /mnt/link kept the unresolved symlink component (it sits above their leaf/parent). The resulting StartsWith prefix check failed, so every offloaded read and write was rejected. Resolve symlinks at every existing component of the path so the base and the candidates are canonicalized identically regardless of where a symlink sits. Add a test with a symlinked base leaf; it fails before this change.
GarrettBeatty
marked this pull request as ready for review
September 4, 2026 16:57
jnunn-aws
approved these changes
Sep 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Addresses @jnunn-aws's review comment on #2561: #2561 (comment)
Problem
FileSystemSerializer.IsWithinBaseruns both the base and the candidate throughResolveReal, butResolveRealonly followed a symlink on the leaf and its immediate parent. When the configuredbasePathis itself a symlink — e.g. an EFS mount exposed as/mnt/link -> /mnt/real— the two sides canonicalize asymmetrically:/mnt/link— its leaf is the symlink, so it resolves tofullBase = /mnt/real/./mnt/link/<func>/<exec>/<inv>/<entity>.bin— the/mnt/linksymlink sits several components above the leaf/parent, whichResolveRealnever touched, so it stayed/mnt/link/....fullPath.StartsWith(fullBase)is thenfalse, soValidateWriteDirWithinBase/ValidatePathWithinBasereject every offloaded write and read.The macOS
/var -> /private/varcase in the code comment is safe only because that symlink sits above the base and is left unresolved symmetrically on both sides; a symlinked base leaf is not.Fix
ResolveRealnow follows a symlink at every existing component of the path (walking root → leaf, callingResolveLinkTargetper segment). Base and candidate are canonicalized identically no matter where the symlink sits, so a symlinked mount root used as the base resolves the same way the candidate paths built under it do. Non-existent trailing components (a per-execution directory validated before it is created) can't be symlinks and are appended lexically. This also strengthens escape detection: a symlink planted at any interior component that points outside the base is now caught, not just one on the leaf/parent.Test
Added
SymlinkedBasePath_RoundTripsAndStaysContained: createslink -> real, constructs the serializer with the symlink as the base, and asserts a value round-trips and the payload physically lands under the real target. The test fails before this change (containment guard rejects the write) and passes after. It soft-skips where symlink creation requires elevation (Windows without Developer Mode).All existing
FileSystemSerializerTestspass (net10.0).