@@ -609,7 +609,7 @@ fn split_sandbox_path(path: &str) -> (&str, &str) {
609609/// checks on sandbox-side source paths in download flows.
610610const SANDBOX_WORKSPACE_ROOT : & str = "/sandbox" ;
611611
612- /// Lexically normalise a POSIX-style absolute path by resolving `.` and `..`
612+ /// Lexically clean a POSIX-style absolute path by resolving `.` and `..`
613613/// components, collapsing repeated separators, and stripping any trailing
614614/// slash. Returns `None` if the input is empty or relative — the caller is
615615/// expected to reject those before reaching this helper.
@@ -619,7 +619,7 @@ const SANDBOX_WORKSPACE_ROOT: &str = "/sandbox";
619619/// client-side to refuse obvious path-traversal attempts before issuing the
620620/// SSH command. Symlink-based escapes inside the sandbox must be addressed
621621/// server-side.
622- fn lexical_normalise_absolute_path ( path : & str ) -> Option < String > {
622+ fn lexical_clean_absolute_path ( path : & str ) -> Option < String > {
623623 if !path. starts_with ( '/' ) {
624624 return None ;
625625 }
@@ -647,26 +647,26 @@ fn lexical_normalise_absolute_path(path: &str) -> Option<String> {
647647/// Validate that a sandbox-side source path passed to `sandbox download`
648648/// resolves under the sandbox writable root.
649649///
650- /// Returns the normalised , traversal-resolved path on success. Refuses any
650+ /// Returns the cleaned , traversal-resolved path on success. Refuses any
651651/// path that lexically escapes `/sandbox` (e.g. `/etc/passwd`,
652652/// `/sandbox/../etc/passwd`) with a user-facing error.
653653///
654654/// This is a lexical guard only — it does not follow symlinks. Call
655- /// `canonicalize_sandbox_source_path ` after this on any path that will be
656- /// passed to a subsequent SSH I/O operation, so a symlink such as
655+ /// `resolve_sandbox_source_path ` after this on any path that will be passed
656+ /// to a subsequent SSH I/O operation, so a symlink such as
657657/// `/sandbox/etc-link -> /etc` cannot leak files outside the workspace.
658658fn validate_sandbox_source_path ( path : & str ) -> Result < String > {
659659 if path. is_empty ( ) {
660660 return Err ( miette:: miette!( "sandbox source path is empty" ) ) ;
661661 }
662- let normalised = lexical_normalise_absolute_path ( path)
662+ let cleaned = lexical_clean_absolute_path ( path)
663663 . ok_or_else ( || miette:: miette!( "sandbox source path must be absolute (got '{path}')" ) ) ?;
664- if !is_under_sandbox_workspace ( & normalised ) {
664+ if !is_under_sandbox_workspace ( & cleaned ) {
665665 return Err ( miette:: miette!(
666666 "sandbox source path '{path}' is outside the sandbox workspace ({SANDBOX_WORKSPACE_ROOT})"
667667 ) ) ;
668668 }
669- Ok ( normalised )
669+ Ok ( cleaned )
670670}
671671
672672/// Pure helper: is `path` equal to `/sandbox` or a descendant of it?
@@ -680,28 +680,28 @@ fn is_under_sandbox_workspace(path: &str) -> bool {
680680/// The lexical guard in `validate_sandbox_source_path` cannot see symlinks; a
681681/// path such as `/sandbox/etc-link/passwd` (where `etc-link -> /etc`) clears
682682/// the lexical check but would still leak `/etc/passwd` once `tar -C` follows
683- /// the link. Canonicalising on the remote side and re-validating closes that
684- /// gap. The returned canonical path is what the caller should hand to probe
685- /// and tar invocations.
686- async fn canonicalize_sandbox_source_path (
683+ /// the link. Resolving symlinks on the remote side and re-validating closes
684+ /// that gap. The returned fully-resolved path is what the caller should hand
685+ /// to probe and tar invocations.
686+ async fn resolve_sandbox_source_path (
687687 session : & SshSessionConfig ,
688688 sandbox_path : & str ,
689689) -> Result < String > {
690- let canonical_cmd = format ! ( "realpath -e -- {path}" , path = shell_escape( sandbox_path) ) ;
691- let canonical = ssh_run_capture_stdout ( session, & canonical_cmd )
690+ let resolve_cmd = format ! ( "realpath -e -- {path}" , path = shell_escape( sandbox_path) ) ;
691+ let resolved = ssh_run_capture_stdout ( session, & resolve_cmd )
692692 . await
693- . wrap_err_with ( || format ! ( "failed to canonicalise sandbox source path '{sandbox_path}'" ) ) ?;
694- if canonical . is_empty ( ) {
693+ . wrap_err_with ( || format ! ( "failed to resolve sandbox source path '{sandbox_path}'" ) ) ?;
694+ if resolved . is_empty ( ) {
695695 return Err ( miette:: miette!(
696696 "sandbox source path '{sandbox_path}' does not exist"
697697 ) ) ;
698698 }
699- if !is_under_sandbox_workspace ( & canonical ) {
699+ if !is_under_sandbox_workspace ( & resolved ) {
700700 return Err ( miette:: miette!(
701- "sandbox source path '{sandbox_path}' resolves to '{canonical }', outside the sandbox workspace ({SANDBOX_WORKSPACE_ROOT})"
701+ "sandbox source path '{sandbox_path}' resolves to '{resolved }', outside the sandbox workspace ({SANDBOX_WORKSPACE_ROOT})"
702702 ) ) ;
703703 }
704- Ok ( canonical )
704+ Ok ( resolved )
705705}
706706
707707/// Resolve the host-side target path for a downloaded *file*, following
@@ -934,7 +934,7 @@ pub async fn sandbox_sync_down(
934934) -> Result < ( ) > {
935935 let sandbox_path = validate_sandbox_source_path ( sandbox_path) ?;
936936 let session = ssh_session_config ( server, name, tls) . await ?;
937- let sandbox_path = canonicalize_sandbox_source_path ( & session, & sandbox_path) . await ?;
937+ let sandbox_path = resolve_sandbox_source_path ( & session, & sandbox_path) . await ?;
938938 let kind = probe_sandbox_source_kind ( & session, & sandbox_path) . await ?;
939939
940940 match kind {
@@ -1540,31 +1540,31 @@ mod tests {
15401540 }
15411541
15421542 #[ test]
1543- fn lexical_normalise_resolves_dot_and_dotdot_segments ( ) {
1543+ fn lexical_clean_resolves_dot_and_dotdot_segments ( ) {
15441544 assert_eq ! (
1545- lexical_normalise_absolute_path ( "/sandbox/./a" ) ,
1545+ lexical_clean_absolute_path ( "/sandbox/./a" ) ,
15461546 Some ( "/sandbox/a" . to_string( ) )
15471547 ) ;
15481548 assert_eq ! (
1549- lexical_normalise_absolute_path ( "/sandbox/sub/../a" ) ,
1549+ lexical_clean_absolute_path ( "/sandbox/sub/../a" ) ,
15501550 Some ( "/sandbox/a" . to_string( ) )
15511551 ) ;
15521552 assert_eq ! (
1553- lexical_normalise_absolute_path ( "/sandbox/../etc/passwd" ) ,
1553+ lexical_clean_absolute_path ( "/sandbox/../etc/passwd" ) ,
15541554 Some ( "/etc/passwd" . to_string( ) )
15551555 ) ;
15561556 assert_eq ! (
1557- lexical_normalise_absolute_path ( "//sandbox///foo//" ) ,
1557+ lexical_clean_absolute_path ( "//sandbox///foo//" ) ,
15581558 Some ( "/sandbox/foo" . to_string( ) )
15591559 ) ;
1560- assert_eq ! ( lexical_normalise_absolute_path ( "/" ) , Some ( "/" . to_string( ) ) ) ;
1560+ assert_eq ! ( lexical_clean_absolute_path ( "/" ) , Some ( "/" . to_string( ) ) ) ;
15611561 }
15621562
15631563 #[ test]
1564- fn lexical_normalise_refuses_relative_paths ( ) {
1565- assert_eq ! ( lexical_normalise_absolute_path ( "" ) , None ) ;
1566- assert_eq ! ( lexical_normalise_absolute_path ( "sandbox/a" ) , None ) ;
1567- assert_eq ! ( lexical_normalise_absolute_path ( "./a" ) , None ) ;
1564+ fn lexical_clean_refuses_relative_paths ( ) {
1565+ assert_eq ! ( lexical_clean_absolute_path ( "" ) , None ) ;
1566+ assert_eq ! ( lexical_clean_absolute_path ( "sandbox/a" ) , None ) ;
1567+ assert_eq ! ( lexical_clean_absolute_path ( "./a" ) , None ) ;
15681568 }
15691569
15701570 #[ test]
0 commit comments