VAITP Dataset

← Back to the dataset

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.

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 ::