@@ -146,7 +146,7 @@ def _is_valid_event(self, run_name: str) -> bool:
146146 return tf .io .gfile .isdir (self ._profile_dir (run_name ))
147147
148148 def _profile_dir (self , run_name : str ) -> str :
149- """Converts run name to full profile path.
149+ """Converts run name to a full, validated profile path under `logdir` .
150150
151151 Args:
152152 run_name (str):
@@ -155,9 +155,23 @@ def _profile_dir(self, run_name: str) -> str:
155155 Returns:
156156 Full path for run name.
157157 """
158+ logger .debug ("_profile_dir run_name: %s" , run_name )
159+ base = os .path .realpath (self ._logdir )
158160 if run_name is None or run_name == uploader_utils .DEFAULT_PROFILE_RUN_NAME :
159- return os .path .join (self ._logdir , self .PROFILE_PATH )
160- return os .path .join (self ._logdir , run_name , self .PROFILE_PATH )
161+ candidate = os .path .join (base , self .PROFILE_PATH )
162+ else :
163+ # Normalize and forbid absolute/traversal components in `run_name`.
164+ if os .path .isabs (run_name ):
165+ raise ValueError ("run_name must be a relative name" )
166+ # Collapse dot segments then strip leading separators
167+ safe_name = os .path .normpath (run_name ).lstrip ("/\\ " )
168+ if safe_name .startswith (".." ) or (".." + os .sep ) in safe_name :
169+ raise ValueError ("run_name must not contain path traversal" )
170+ candidate = os .path .join (base , safe_name , self .PROFILE_PATH )
171+ resolved = os .path .realpath (candidate )
172+ if os .path .commonpath ([base , resolved ]) != base :
173+ raise ValueError ("profile path escapes logdir" )
174+ return resolved
161175
162176 def send_request (self , run_name : str ):
163177 """Accepts run_name and sends an RPC request if an event is detected.
0 commit comments