@@ -212,40 +212,6 @@ def _precreate_data_dirs(service_id: str):
212212 logger .warning ("Failed to pre-create %s: %s" , dir_path , e )
213213
214214
215- def _resolve_setup_hook (ext_dir : Path ) -> Path | None :
216- """Read manifest to find setup_hook path. Returns None if no hook defined."""
217- manifest_path = None
218- for name in ("manifest.yaml" , "manifest.yml" ):
219- candidate = ext_dir / name
220- if candidate .exists ():
221- manifest_path = candidate
222- break
223- if manifest_path is None :
224- return None
225- try :
226- import yaml
227- manifest = yaml .safe_load (manifest_path .read_text (encoding = "utf-8" ))
228- except (ImportError , OSError ):
229- return None
230- if not isinstance (manifest , dict ):
231- return None
232- service_def = manifest .get ("service" , {})
233- if not isinstance (service_def , dict ):
234- return None
235- setup_hook = service_def .get ("setup_hook" , "" )
236- if not isinstance (setup_hook , str ) or not setup_hook :
237- return None
238- hook_path = (ext_dir / setup_hook ).resolve ()
239- try :
240- hook_path .relative_to (ext_dir .resolve ())
241- except ValueError :
242- logger .warning ("Path traversal attempt in setup_hook for %s: %s" , ext_dir .name , setup_hook )
243- return None
244- if not hook_path .is_file ():
245- return None
246- return hook_path
247-
248-
249215def docker_compose_action (service_id : str , action : str ) -> tuple :
250216 flags = resolve_compose_flags ()
251217 if action == "start" :
@@ -958,11 +924,27 @@ def _run_install():
958924 if run_setup_hook :
959925 _write_progress (service_id , "setup_hook" , "Running setup..." )
960926 ext_dir = USER_EXTENSIONS_DIR / service_id
961- hook_path = _resolve_setup_hook (ext_dir )
927+ hook_path = _resolve_hook (ext_dir , "post_install" )
962928 if hook_path :
929+ # Minimal allowlist env — mirror _execute_hook (L856-866)
930+ # to prevent leaking host-agent secrets to extension scripts.
931+ manifest = _read_manifest (ext_dir )
932+ service_def = manifest .get ("service" , {}) if manifest else {}
933+ if not isinstance (service_def , dict ):
934+ service_def = {}
935+ hook_env = {
936+ "PATH" : os .environ .get ("PATH" , "/usr/bin:/bin" ),
937+ "HOME" : os .environ .get ("HOME" , "" ),
938+ "SERVICE_ID" : service_id ,
939+ "SERVICE_PORT" : str (service_def .get ("port" , 0 )),
940+ "SERVICE_DATA_DIR" : str (DATA_DIR / service_id ),
941+ "DREAM_VERSION" : DREAM_VERSION ,
942+ "GPU_BACKEND" : GPU_BACKEND ,
943+ "HOOK_NAME" : "post_install" ,
944+ }
963945 result = subprocess .run (
964946 ["bash" , str (hook_path ), str (INSTALL_DIR ), GPU_BACKEND ],
965- cwd = str (ext_dir ),
947+ cwd = str (ext_dir ), env = hook_env ,
966948 capture_output = True , text = True ,
967949 timeout = SUBPROCESS_TIMEOUT_START ,
968950 )
0 commit comments