CVE-2026-63175
Improper state isolation between concurrent captures can leak sensitive data.
- CVSS 7.1
- CWE-613
- Design Defects
- Remote
PlaywrightCapture stored capture-specific configuration and runtime data as mutable class-level variables rather than instance-level variables. Consequently, multiple Capture objects running within the same Python process could share state, including HTTP headers, cookies, browser storage, HTTP credentials, proxy configuration, user-agent settings, geolocation information, and captured request data. In a multi-user or concurrent deployment, information supplied during one capture could therefore persist and be reused by a subsequent or parallel capture. This could result in the disclosure of authentication cookies, credentials, browser storage, or captured request data belonging to another user. It could also cause requests to be performed with another capture's authentication context, headers, or proxy configuration, potentially enabling unauthorized access to remote resources or interference with other capture operations. The vulnerability is resolved by initializing all capture-specific settings and request data as instance variables in the Capture constructor, ensuring that state is isolated between capture operations.
- CWE
- CWE-613
- CVSS base score
- 7.1
- Published
- 2026-07-15
- OWASP
- A01 Broken Access Control
- Orthogonal defect classification
- Assignment
- Code defect classification
- Incorrect Assignment
- Category
- Design Defects
- Subcategory
- Insecure Handling of Sensitive Data
- Accessibility scope
- Remote
- Impact
- Information Disclosure
- Affected component
- PlaywrightCa
- Fixed by upgrading
- Yes
Solution
Upgrade scrapy-playwright to version 0.0.34 or later.
Vulnerable code sample
import threading
import time
# This code is a conceptual representation of the vulnerability described in
# CVE-2026-63175, demonstrating the use of mutable class-level variables
# for instance-specific state.
class VulnerableCapture:
# Vulnerability: State is stored in class variables, not instance variables.
# This state will be shared across all instances of VulnerableCapture.
headers = {}
captured_data = []
def __init__(self, user_id):
self.user_id = user_id
# The constructor does not initialize state, so it defaults to the shared class variables.
def set_auth_header(self, token):
"""Sets an auth token for a capture, but incorrectly modifies the class variable."""
print(f"[{self.user_id}] Setting auth token: {token[:10]}...")
self.headers['Authorization'] = f"Bearer {token}"
def capture_page(self, url):
"""Simulates a page capture, using the shared class-level state."""
print(f"[{self.user_id}] Capturing '{url}' with headers: {self.headers}")
# All instances append to the same shared list.
self.captured_data.append({
'captured_by': self.user_id,
'url': url,
'headers_used': self.headers.copy()
})
time.sleep(0.1)
def get_results(self):
"""Returns the shared data list."""
return self.captured_data
# --- Demonstration of the vulnerability in a concurrent context ---
def user_one_session():
"""Simulates a session for User 1 with sensitive data."""
user1_capture = VulnerableCapture(user_id="user_1")
user1_capture.set_auth_header("user_1_secret_api_key_xxxxxxxx")
user1_capture.capture_page("https://api.private.com/data")
def user_two_session():
"""Simulates a session for User 2, which should be isolated."""
# This user starts their capture slightly after user 1 sets their header.
time.sleep(0.05)
user2_capture = VulnerableCapture(user_id="user_2")
# User 2 makes a request without setting any headers.
# They expect an anonymous request.
user2_capture.capture_page("https://api.public.com/data")
if __name__ == "__main__":
print("--- Starting concurrent captures ---")
thread1 = threading.Thread(target=user_one_session)
thread2 = threading.Thread(target=user_two_session)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print("\n--- Vulnerability Demonstration ---")
# We create a final object just to access the final state of the shared class variables.
final_state_accessor = VulnerableCapture(user_id="inspector")
all_captured_data = final_state_accessor.get_results()
print("\nFinal captured data from the shared state:")
for item in all_captured_data:
print(item)
print("\nAnalysis:")
print("1. User 2's capture incorrectly used User 1's 'Authorization' header.")
print("2. The final list contains data from both users, as it was a shared resource.")Patched code sample
import uuid
class Capture:
"""
A corrected class where capture-specific data is properly encapsulated
within each instance, preventing state leakage between objects.
"""
def __init__(self, user_agent=None, proxy=None):
"""
Initializes all capture-specific settings and data as instance variables.
This ensures that each Capture object has its own isolated state.
"""
# Instance-specific configuration and runtime data
self.headers = {}
self.cookies = {}
self.storage = {}
self.credentials = None
self.geolocation = None
self.captured_requests = []
# Initialize settings from arguments or with defaults for this instance
self.user_agent = user_agent if user_agent else "default-python-capture-agent/1.0"
self.proxy_config = proxy
def set_auth_token(self, token):
"""Sets an authentication token in the instance's headers."""
self.headers['Authorization'] = f'Bearer {token}'
def add_cookie(self, name, value):
"""Adds a cookie to the instance's cookie jar."""
self.cookies[name] = value
def capture_request(self, request_data):
"""Adds captured request data to the instance's list."""
self.captured_requests.append(request_data)
# Example demonstrating the fix (for illustrative purposes):
# In a real application, these objects might be created in different threads
# or for different users concurrently.
# --- Capture for User 1 ---
# A new instance is created, so a new, isolated set of variables is initialized.
capture_user1 = Capture(user_agent="user1-browser/91.0")
capture_user1.set_auth_token(f"user1_token_{uuid.uuid4()}")
capture_user1.add_cookie("sessionid", "abc12345")
capture_user1.capture_request({"url": "/api/user1/profile", "status": 200})
# --- Capture for User 2 ---
# This second instance gets its own, completely separate set of variables.
# It does NOT inherit or see any data from capture_user1.
capture_user2 = Capture(proxy={"http": "http://proxy.example.com:8080"})
capture_user2.set_auth_token(f"user2_token_{uuid.uuid4()}")
capture_user2.add_cookie("sessionid", "xyz67890")
capture_user2.capture_request({"url": "/api/user2/dashboard", "status": 200})
# Verification: The state of one instance does not affect the other.
# assert capture_user1.headers != capture_user2.headers
# assert capture_user1.cookies != capture_user2.cookies
# assert capture_user1.proxy_config is None
# assert capture_user2.proxy_config is not None
# assert len(capture_user1.captured_requests) == 1
# assert len(capture_user2.captured_requests) == 1
# assert "user1_token" in capture_user1.headers['Authorization']
# assert "user2_token" in capture_user2.headers['Authorization']Cite this entry
@misc{vaitp:cve202663175,
title = {{Improper state isolation between concurrent captures can leak sensitive data.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-63175},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-63175/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
