@@ -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,22 @@ def _profile_dir(self, run_name: str) -> str:
155155 Returns:
156156 Full path for run name.
157157 """
158+ base = os .path .realpath (self ._logdir )
158159 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 )
160+ candidate = os .path .join (base , self .PROFILE_PATH )
161+ else :
162+ # Normalize and forbid absolute/traversal components in `run_name`.
163+ if os .path .isabs (run_name ):
164+ raise ValueError ("run_name must be a relative name" )
165+ # Collapse dot segments then strip leading separators
166+ safe_name = os .path .normpath (run_name ).lstrip ("/\\ " )
167+ if safe_name .startswith (".." ) or (".." + os .sep ) in safe_name :
168+ raise ValueError ("run_name must not contain path traversal" )
169+ candidate = os .path .join (base , safe_name , self .PROFILE_PATH )
170+ resolved = os .path .realpath (candidate )
171+ if os .path .commonpath ([base , resolved ]) != base :
172+ raise ValueError ("profile path escapes logdir" )
173+ return resolved
161174
162175 def send_request (self , run_name : str ):
163176 """Accepts run_name and sends an RPC request if an event is detected.
0 commit comments