## Description
`navigator.connection.downlink` and `navigator.connection.rtt` return unrealistic values when CloakBrowser is launched with a browser-level HTTP proxy.
With the same Docker container, same Xvfb environment, same proxy, and same test page:
- Stock Chromium reports normal values, e.g. `downlink ~= 1.7`, `rtt ~= 300`
- CloakBrowser reports `downlink = 10`, `rtt = 0`
This makes CloakBrowser distinguishable from stock Chromium on sites that collect Network Information API signals.
## Environment
- CloakBrowser version: `0.4.10`
- Cloak Chromium version: `146.0.7680.177.5`
- OS: Debian GNU/Linux 13 (trixie)
- Runtime: Docker + Xvfb
- Browser mode: headed
- Proxy type: browser-level HTTP proxy
- Proxy example: `--proxy-server=http://host.docker.internal:18081`
## Reproduction
### 1. CloakBrowser with proxy
```python
import asyncio
import tempfile
from cloakbrowser import launch_persistent_context_async
async def main():
context = await launch_persistent_context_async(
user_data_dir=tempfile.mkdtemp(prefix="cloak-nqe-"),
headless=False,
proxy="http://host.docker.internal:18081",
args=[
"--no-sandbox",
"--fingerprint=123456",
"--fingerprint-platform=windows",
],
)
page = context.pages[0] if context.pages else await context.new_page()
await page.goto("https://browserleaks.com/javascript", wait_until="domcontentloaded")
await page.wait_for_timeout(3000)
result = await page.evaluate("""
() => ({
userAgent: navigator.userAgent,
webdriver: navigator.webdriver,
connection: navigator.connection ? {
effectiveType: navigator.connection.effectiveType,
downlink: navigator.connection.downlink,
rtt: navigator.connection.rtt,
saveData: navigator.connection.saveData,
type: navigator.connection.type || null,
} : null,
})
""")
print(result)
await context.close()
asyncio.run(main())
Observed result:
{
"webdriver": false,
"connection": {
"effectiveType": "3g",
"downlink": 10,
"rtt": 0,
"saveData": false,
"type": null
}
}
2. Stock Chromium with the same proxy
In the same Docker container, using Debian Chromium:
apt-get update
apt-get install -y --no-install-recommends chromium
Then launch stock Chromium with the same proxy:
import asyncio
import json
import os
import subprocess
import tempfile
import urllib.request
from playwright.async_api import async_playwright
async def wait_http(port):
for _ in range(150):
try:
return json.load(urllib.request.urlopen(f"http://127.0.0.1:{port}/json/version", timeout=1))
except Exception:
await asyncio.sleep(0.2)
raise RuntimeError("CDP not ready")
async def main():
port = 9662
user_data = tempfile.mkdtemp(prefix="raw-chromium-")
args = [
"/usr/bin/chromium",
f"--remote-debugging-port={port}",
"--remote-allow-origins=*",
f"--user-data-dir={user_data}",
"--no-first-run",
"--no-default-browser-check",
"--no-sandbox",
"--proxy-server=http://host.docker.internal:18081",
"data:text/html,<html>probe</html>",
]
env = os.environ.copy()
env["DISPLAY"] = os.environ.get("DISPLAY", ":99")
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, env=env)
try:
await wait_http(port)
async with async_playwright() as pw:
browser = await pw.chromium.connect_over_cdp(f"http://127.0.0.1:{port}")
context = browser.contexts[0]
page = context.pages[0] if context.pages else await context.new_page()
await page.goto("https://browserleaks.com/javascript", wait_until="domcontentloaded")
await page.wait_for_timeout(3000)
result = await page.evaluate("""
() => ({
userAgent: navigator.userAgent,
webdriver: navigator.webdriver,
connection: navigator.connection ? {
effectiveType: navigator.connection.effectiveType,
downlink: navigator.connection.downlink,
rtt: navigator.connection.rtt,
} : null,
})
""")
print(result)
await browser.close()
finally:
proc.terminate()
asyncio.run(main())
Observed stock Chromium result:
{
"webdriver": false,
"connection": {
"effectiveType": "3g",
"downlink": 1.7,
"rtt": 300
}
}
Additional Notes
I also captured Chrome NetLog from CloakBrowser.
The underlying Network Quality Estimator appears to have reasonable values, for example:
{
"name": "NETWORK_QUALITY_CHANGED",
"params": {
"downstream_throughput_kbps": 1400,
"effective_connection_type": "3G",
"http_rtt_ms": 308,
"transport_rtt_ms": 125
}
}
But the JavaScript-exposed Network Information API still returns:
{
"downlink": 10,
"rtt": 0
}
So the issue seems to be in the JS-exposed navigator.connection layer, not the underlying network measurement.
Expected Behavior
CloakBrowser should expose Network Information API values that are consistent with stock Chromium under the same proxy/environment, or provide explicit fingerprint flags to control these values.
For example:
{
"effectiveType": "3g",
"downlink": 1.5,
"rtt": 250
}
instead of:
{
"effectiveType": "3g",
"downlink": 10,
"rtt": 0
}
Observed result:
{ "webdriver": false, "connection": { "effectiveType": "3g", "downlink": 10, "rtt": 0, "saveData": false, "type": null } }2. Stock Chromium with the same proxy
In the same Docker container, using Debian Chromium:
Then launch stock Chromium with the same proxy:
Observed stock Chromium result:
{ "webdriver": false, "connection": { "effectiveType": "3g", "downlink": 1.7, "rtt": 300 } }Additional Notes
I also captured Chrome NetLog from CloakBrowser.
The underlying Network Quality Estimator appears to have reasonable values, for example:
{ "name": "NETWORK_QUALITY_CHANGED", "params": { "downstream_throughput_kbps": 1400, "effective_connection_type": "3G", "http_rtt_ms": 308, "transport_rtt_ms": 125 } }But the JavaScript-exposed Network Information API still returns:
{ "downlink": 10, "rtt": 0 }So the issue seems to be in the JS-exposed
navigator.connectionlayer, not the underlying network measurement.Expected Behavior
CloakBrowser should expose Network Information API values that are consistent with stock Chromium under the same proxy/environment, or provide explicit fingerprint flags to control these values.
For example:
{ "effectiveType": "3g", "downlink": 1.5, "rtt": 250 }instead of:
{ "effectiveType": "3g", "downlink": 10, "rtt": 0 }