Two issues in pythonlib/, both in code paths that exist to protect the user. Reporting together because they are one-line-ish fixes in the same package; happy to split into separate issues if you'd prefer.
1. Downloaded release assets are never verified against their digest
webdl() streams a release asset into a buffer, install_versioned() hands that buffer straight to unzip(), and the extracted tree is then chmod -R 755'd and executed.
The digest is already available. PkgManager.check_asset() reads it from the GitHub API response and stores it:
digest = asset.get('digest') or ''
if digest.startswith('sha256:'):
self.installed_sha256 = digest.split(':', 1)[1]
AvailableVersion.sha256 carries it further, into version.json. But it is never compared against the downloaded bytes. Every sha256 == comparison in the package compares metadata to metadata to identify which installed version a folder holds; hashlib appears once, in utils.py, to key a config cache.
So the archive is accepted on transport security alone. A truncated download, a bad mirror, or a substituted asset all extract silently.
Repro: point webdl() at a modified archive — installation completes normally, version.json records the expected sha256 that the installed bytes do not have.
2. public_ip() disables TLS certificate verification
camoufox/ip.py:
with _suppress_insecure_warning():
resp = requests.get(url, ..., verify=False)
_suppress_insecure_warning() filters out urllib3's InsecureRequestWarning, so the disabled verification is also silent.
These requests go through the user's proxy when one is configured — the exact position an attacker occupies. A forged response controls what public_ip() returns, and callers use that to spoof the WebRTC IP. validate_ip() constrains the result to a well-formed address, but which address is the attacker's choice, so the leak the function exists to prevent becomes attacker-selectable.
The ip-api.com lookups in sync_api.py / async_api.py have a related weakness (plain http://), but that appears to be forced by ip-api's free tier, so I have left it alone rather than break the feature.
Environment
Reproduced on main @ f1febf4, Python 3.13, Linux.
Fix
PR follows — verify=True plus a verify_sha256() call between download and extraction on both install paths, with 12 unit tests. No browser patches touched.
Two issues in
pythonlib/, both in code paths that exist to protect the user. Reporting together because they are one-line-ish fixes in the same package; happy to split into separate issues if you'd prefer.1. Downloaded release assets are never verified against their digest
webdl()streams a release asset into a buffer,install_versioned()hands that buffer straight tounzip(), and the extracted tree is thenchmod -R 755'd and executed.The digest is already available.
PkgManager.check_asset()reads it from the GitHub API response and stores it:AvailableVersion.sha256carries it further, intoversion.json. But it is never compared against the downloaded bytes. Everysha256 ==comparison in the package compares metadata to metadata to identify which installed version a folder holds;hashlibappears once, inutils.py, to key a config cache.So the archive is accepted on transport security alone. A truncated download, a bad mirror, or a substituted asset all extract silently.
Repro: point
webdl()at a modified archive — installation completes normally,version.jsonrecords the expected sha256 that the installed bytes do not have.2.
public_ip()disables TLS certificate verificationcamoufox/ip.py:_suppress_insecure_warning()filters out urllib3'sInsecureRequestWarning, so the disabled verification is also silent.These requests go through the user's proxy when one is configured — the exact position an attacker occupies. A forged response controls what
public_ip()returns, and callers use that to spoof the WebRTC IP.validate_ip()constrains the result to a well-formed address, but which address is the attacker's choice, so the leak the function exists to prevent becomes attacker-selectable.The
ip-api.comlookups insync_api.py/async_api.pyhave a related weakness (plainhttp://), but that appears to be forced by ip-api's free tier, so I have left it alone rather than break the feature.Environment
Reproduced on
main@ f1febf4, Python 3.13, Linux.Fix
PR follows —
verify=Trueplus averify_sha256()call between download and extraction on both install paths, with 12 unit tests. No browser patches touched.