Skip to content

security: use secure tempfile paths for CDP ports config and browser user data (CWE-377)#12

Closed
sebastiondev wants to merge 1 commit into
agbcloud:masterfrom
sebastiondev:fix/cwe377-local-page-agent-insecure-2441
Closed

security: use secure tempfile paths for CDP ports config and browser user data (CWE-377)#12
sebastiondev wants to merge 1 commit into
agbcloud:masterfrom
sebastiondev:fix/cwe377-local-page-agent-insecure-2441

Conversation

@sebastiondev
Copy link
Copy Markdown

Summary

LocalBrowser.initialize_async() in agb/modules/browser/eval/local_page_agent.py (and the duplicate copy under python/agb/...) writes to two hardcoded predictable paths in /tmp:

  • /tmp/chrome_cdp_ports.json — opened with mode "w", which follows symlinks and truncates the target.
  • /tmp/browser_user_data — used as the persistent Chromium profile directory.

This is a classic CWE-377: Insecure Temporary File pattern.

Data flow

initialize_async()open("/tmp/chrome_cdp_ports.json", "w")json.dump(...)
initialize_async()launch_persistent_context(user_data_dir="/tmp/browser_user_data", ...)

Both paths are fixed strings with no randomization, no O_EXCL, no permission restriction, and no symlink check.

Impact

On a shared multi-user host (CI runners, shared dev boxes, multi-tenant containers):

  1. Symlink attack on the JSON file — an unprivileged local attacker who can predict when the SDK will run can pre-create /tmp/chrome_cdp_ports.json as a symlink pointing at any file the SDK user can write (e.g., ~/.ssh/authorized_keys, a config file, a script). When the SDK runs, open(..., "w") truncates the target and writes attacker-influenceable JSON to it.
  2. Predictable browser profile directory/tmp/browser_user_data is created with default umask permissions and is reused across runs. An attacker who pre-creates the directory (or wins a race) can read cookies, session storage, and saved credentials, or plant files Chromium will load.

Severity is bounded — no remote vector, no privilege escalation beyond the SDK user, and on single-user dev machines or single-tenant containers the practical risk is low. Filing this as defense-in-depth hardening rather than a critical issue.

Fix

Replace both hardcoded paths with tempfile primitives:

  • tempfile.mkstemp(prefix="chrome_cdp_ports_", suffix=".json") for the CDP ports file. mkstemp opens with O_CREAT | O_EXCL | O_RDWR, so it cannot follow a pre-existing symlink. The fd is then chmod'd to 0600.
  • tempfile.mkdtemp(prefix="browser_user_data_") for the Chromium profile directory, chmod'd to 0700.

Paths are stored on the instance (self._cdp_ports_path, self._user_data_dir) so they remain stable for the lifetime of the browser. The same change is applied to both copies of the file (agb/... and python/agb/...) since the repo currently maintains both trees.

The diff is small and behavior-preserving — Chromium doesn't care what the user-data-dir path is, and nothing else in the codebase reads /tmp/chrome_cdp_ports.json (verified via grep).

Testing

  • Added tests/unit/test_cwe377_local_browser_temp.py which asserts the hardcoded /tmp/... strings no longer appear in the source and that tempfile is imported. Run with PYTHONPATH=python python3 tests/unit/test_cwe377_local_browser_temp.py — all 3 tests pass.
  • Manually verified mkstemp behavior: pre-creating a symlink at the would-be path no longer affects the run, since mkstemp generates a fresh randomized name and uses O_EXCL.
  • Confirmed via grep that no other code references the old /tmp/chrome_cdp_ports.json or /tmp/browser_user_data paths.

Adversarial review

Before submitting we tried to disprove this. The main question was whether the SDK is realistically ever run in a multi-user environment where /tmp is shared with untrusted users — if not, this is purely theoretical. We concluded it's worth fixing anyway: shared CI runners and multi-tenant container hosts are common deployment targets for browser-automation SDKs, the fix is tiny and has no behavioral downside, and tempfile.mkstemp/mkdtemp are the standard Python idiom for exactly this case. We also checked whether Playwright internally sandboxes the user-data-dir — it doesn't; it just passes the path through to Chromium, which happily reads/writes whatever's there.

cc @lewiswigmore

…data (CWE-377)

Replace hardcoded /tmp/chrome_cdp_ports.json with tempfile.mkstemp() and
/tmp/browser_user_data with tempfile.mkdtemp() to prevent symlink attacks
and ensure owner-only file permissions on shared systems.
@lewiswigmore
Copy link
Copy Markdown

Closing this to reduce the open-PR pile-up — we have multiple outstanding security contributions to this repo and that volume is not fair on your review queue. Keeping #11 (fix: reject symlinks in extension upload to prevent arbitrary file exfiltration ) as the primary one to focus attention on.

Happy to revisit this finding separately later if it is still relevant. Apologies for the noise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants