security: use secure tempfile paths for CDP ports config and browser user data (CWE-377)#12
Closed
sebastiondev wants to merge 1 commit into
Closed
Conversation
…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.
|
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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
LocalBrowser.initialize_async()inagb/modules/browser/eval/local_page_agent.py(and the duplicate copy underpython/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):
/tmp/chrome_cdp_ports.jsonas 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./tmp/browser_user_datais 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
tempfileprimitives:tempfile.mkstemp(prefix="chrome_cdp_ports_", suffix=".json")for the CDP ports file.mkstempopens withO_CREAT | O_EXCL | O_RDWR, so it cannot follow a pre-existing symlink. The fd is thenchmod'd to0600.tempfile.mkdtemp(prefix="browser_user_data_")for the Chromium profile directory,chmod'd to0700.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/...andpython/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
tests/unit/test_cwe377_local_browser_temp.pywhich asserts the hardcoded/tmp/...strings no longer appear in the source and thattempfileis imported. Run withPYTHONPATH=python python3 tests/unit/test_cwe377_local_browser_temp.py— all 3 tests pass.mkstempbehavior: pre-creating a symlink at the would-be path no longer affects the run, sincemkstempgenerates a fresh randomized name and usesO_EXCL.grepthat no other code references the old/tmp/chrome_cdp_ports.jsonor/tmp/browser_user_datapaths.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
/tmpis 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, andtempfile.mkstemp/mkdtempare 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