T09 · Insecure Skill Coding Practices
- Location
scripts/browser_utils.py:172- Finding
Authenticated Browser Profile Persists in an Unmanaged Temporary Directory
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill appears to do what it claims, but it needs Review because it handles persistent ChatGPT login state and browser automation with several under-scoped local security risks.
Install only if you are comfortable letting this skill reuse a logged-in ChatGPT web session and store browser/session artifacts locally. Use a dedicated low-privilege account or isolated environment, avoid sensitive prompts, review and clear data/ and any chatgpt-profile-* temporary directories, and be aware that first-run setup downloads dependencies and a browser.
scripts/browser_utils.py:172Authenticated Browser Profile Persists in an Unmanaged Temporary Directory
scripts/config.py:158Chromium Sandbox Is Disabled for Remote Web Content
scripts/setup_environment.py:42Automatic Dependency and Browser Installation Lacks Integrity Verification
scripts/run.py:31Script Runner Permits Path Traversal and Execution Outside the Approved Scripts Directory
The declared description promises substantial functionality related to controlling ChatGPT through a browser and managing conversations and session state. The supplied code chunk does not implement any of these behaviors; it is only an empty package initializer with a docstring. This is a material mismatch between the described purpose and the actual provided code.
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.
display = BrowserFactory._discover_display()
if not display:
return None
launch_env = os.environ.copy()
launch_env["DISPLAY"] = display
return launch_env
The README explicitly advertises local persistence of conversation/session metadata and optional proof screenshots, but it does not clearly warn users that these artifacts may contain sensitive prompt contents, account-linked data, or screenshots of private conversations. In a browser-automation skill that reuses a logged-in ChatGPT session, this omission increases the risk of unintentional retention and disclosure of sensitive data on disk.
The skill explicitly instructs use of local shell execution, browser automation, persistent filesystem storage, and networked interaction, but it declares no tool scope or permission boundaries. That makes the skill over-privileged by default and increases the chance that an agent or user invokes powerful capabilities without clear consent or isolation, especially around login state and local data access.
The skill advertises persistent browser login state, cookies, storage state, and conversation metadata stored locally, but it does not present a prominent privacy warning or retention notice. This is dangerous because sensitive authentication material and chat history may remain on disk and be exposed to other local processes, backups, or later users of the same environment.
The skill supports proof and debug screenshots and stores them on disk, but it does not clearly warn that screenshots may capture full conversation contents, account details, or other sensitive page data. In this context, screenshots are especially risky because they can preserve secrets outside the normal conversation store and may be shared or retained unintentionally.
This markdown file documents potentially destructive operations such as reset, close, gc, and delete but provides no warning about possible loss of session or conversation state. For markdown files, missing disclosures about behaviors that could affect user data or system integrity should be flagged when the description omits cautionary guidance.
The dependency is pinned to python-dotenv==1.0.0, and the provided advisory indicates a symlink-following issue in set_key that can lead to arbitrary file overwrite when modifying .env files. In a browser-automation skill with persistent local state and login context, unsafe dotenv file handling is more concerning because local credential or configuration files may exist alongside the skill and could be overwritten if vulnerable code paths are used.
This code automates a browser session to submit arbitrary user-provided prompts to the ChatGPT web UI, but the file contains no user-consent, disclosure, or policy guardrails before transmitting that content to a third-party service. In a skill designed for persistent logged-in browser automation and reusable sessions, this creates a real privacy and data-handling risk because sensitive prompts may be sent externally without explicit acknowledgement or scoping controls.
This code reads proxy-related environment variables, including URLs that may embed usernames and passwords, and passes those credentials into browser launch settings. The file contains no confirmation prompt, logging, or explanatory comment/docstring warning the user that environment-sourced network credentials may be consumed and used for outbound traffic.
The function loads cookies from a persistent state file on disk and injects them directly into a browser context, which can silently reuse an authenticated session. In an agent skill that automates ChatGPT web access with persistent login state, this increases the chance of unintended account access, session confusion, or use of stale/borrowed credentials if the state file is shared, replaced, or insufficiently protected.
This code deletes entire runtime and screenshot directories via shutil.rmtree, which is a destructive operation affecting user or system data. Although the script prints a result after completion, there is no confirmation prompt, pre-action warning, or explanatory comment/docstring disclosing that these targets will be fully removed and recreated.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
if not venv_dir.exists():
print("🔧 First-time setup: creating virtual environment...")
result = subprocess.run([sys.executable, str(setup_script)])
if result.returncode != 0:
print("❌ Failed to set up environment")
sys.exit(1)
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
cmd = [str(venv_python), str(script_path)] + script_args
try:
result = subprocess.run(cmd)
return result.returncode
except KeyboardInterrupt:
print("\n⚠️ Interrupted by user")
The code launches a background daemon using subprocess.Popen with stdio redirected to DEVNULL, which hides the action from the user. In this file there is no confirmation prompt, print/log message, or explanatory comment/docstring near the operation disclosing that a persistent background process will be started.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
return
self._cleanup_stale_runtime_files()
cmd = [sys.executable, str(Path(__file__).resolve()), "_serve"]
subprocess.Popen(
cmd,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
if self.requirements_file.exists():
print("📦 Installing dependencies...")
try:
subprocess.run([str(self.venv_pip), "install", "--upgrade", "pip"], check=True)
subprocess.run([str(self.venv_pip), "install", "-r", str(self.requirements_file)], check=True)
print("🌐 Installing Google Chrome for Patchright...")
try:
Installing dependencies from requirements.txt causes arbitrary package installation and execution of package build/install hooks from a file local to the skill. In the context of an agent skill, this is more sensitive because a tampered or unreviewed dependency list can execute attacker-controlled code during environment setup and compromise the host running the skill.
print("📦 Installing dependencies...")
try:
subprocess.run([str(self.venv_pip), "install", "--upgrade", "pip"], check=True)
subprocess.run([str(self.venv_pip), "install", "-r", str(self.requirements_file)], check=True)
print("🌐 Installing Google Chrome for Patchright...")
try:
subprocess.run([str(self.venv_python), "-m", "patchright", "install", "chrome"], check=True)
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
subprocess.run([str(self.venv_pip), "install", "-r", str(self.requirements_file)], check=True)
print("🌐 Installing Google Chrome for Patchright...")
try:
subprocess.run([str(self.venv_python), "-m", "patchright", "install", "chrome"], check=True)
except subprocess.CalledProcessError as error:
print(f"⚠️ Warning: failed to install Chrome automatically: {error}")
print(" Run manually if needed: python -m patchright install chrome")
This code persists authentication-related information to disk via AuthInfoStore.save() without any visible protection, warning, or minimization in this file. In the context of a browser-automation skill with persistent login state, storing auth data unencrypted in local JSON can expose session material or tokens to other local users, malware, backups, or accidental disclosure.
The example on this line uses a Chinese-language question as the only concrete prompt example, which can imply a fixed language expectation in the skill reference. Under the policy, language or locale constraints should either be optional, user-selected, or clearly documented as intentionally region-specific.
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.
manager = CleanupManager()
try:
payload = getattr(manager, args.target)()
except Exception as error:
payload = result_from_exception(error)
The file persists session and daemon metadata to storage via save_json_file, which is a file-write operation affecting local user data. This file does not provide a user-facing notice, confirmation, or explanatory comment/docstring that session details will be stored on disk.
No suspicious patterns detected.