pythonlib: Add more leak warnings 0.2.8

Warns the user for when the passed parameters are likely to cause a leak.
This commit is contained in:
daijro 2024-10-06 21:53:54 -05:00
parent 4a1cd7ec64
commit 351e99ed18
8 changed files with 171 additions and 36 deletions

30
.gitignore vendored
View file

@ -1,20 +1,32 @@
# Local builds
/camoufox-* /camoufox-*
/firefox-* /firefox-*
/mozilla-unified /mozilla-unified
/extra-docs
/.vscode
_old/
dist/ dist/
bin/ bin/
venv/
/bundle/fonts/extra
launch launch
launch.exe launch.exe
# Internal testing
/extra-docs
/tests
/.vscode
/bundle/fonts/extra
pythonlib/*.png
scripts/*.png
test*
# Old data
_old/
*.old *.old
__pycache__/
*.pyc # Logs
wget-log wget-log
*.kate-swp *.kate-swp
*.log *.log
test.py
*.mmdb # Python interface
venv/
__pycache__/
*.pyc
*.mmdb

View file

@ -116,11 +116,3 @@ class InvalidOS(ValueError):
""" """
... ...
class DetectionWarning(RuntimeWarning):
"""
Raised when a the user has a setting enabled that can cause detection.
"""
...

View file

@ -1,14 +1,13 @@
import os.path
import re import re
from dataclasses import asdict from dataclasses import asdict
from typing import Any, Dict, Optional from typing import Any, Dict, Optional
from browserforge.fingerprints import Fingerprint, FingerprintGenerator from browserforge.fingerprints import Fingerprint, FingerprintGenerator
from yaml import CLoader, load
from camoufox.pkgman import load_yaml
# Load the browserforge.yaml file # Load the browserforge.yaml file
with open(os.path.join(os.path.dirname(__file__), 'browserforge.yml'), 'r') as f: BROWSERFORGE_DATA = load_yaml('browserforge.yml')
BROWSERFORGE_DATA = load(f, Loader=CLoader)
FP_GENERATOR = FingerprintGenerator(browser='firefox', os=('linux', 'macos', 'windows')) FP_GENERATOR = FingerprintGenerator(browser='firefox', os=('linux', 'macos', 'windows'))

View file

@ -16,6 +16,7 @@ import requests
from platformdirs import user_cache_dir from platformdirs import user_cache_dir
from tqdm import tqdm from tqdm import tqdm
from typing_extensions import TypeAlias from typing_extensions import TypeAlias
from yaml import CLoader, load
from .exceptions import UnsupportedArchitecture, UnsupportedOS from .exceptions import UnsupportedArchitecture, UnsupportedOS
@ -337,3 +338,11 @@ def unzip(
with ZipFile(zip_file) as zf: with ZipFile(zip_file) as zf:
for member in tqdm(zf.infolist(), desc=desc): for member in tqdm(zf.infolist(), desc=desc):
zf.extract(member, extract_path) zf.extract(member, extract_path)
def load_yaml(file: str) -> dict:
"""
Loads a local YAML file and returns it as a dictionary.
"""
with open(Path(__file__).parent / file, 'r') as f:
return load(f, Loader=CLoader)

View file

@ -1,6 +1,5 @@
import os import os
import sys import sys
import warnings
from os import environ from os import environ
from pprint import pprint from pprint import pprint
from random import randrange from random import randrange
@ -20,7 +19,6 @@ from .addons import (
threaded_try_load_addons, threaded_try_load_addons,
) )
from .exceptions import ( from .exceptions import (
DetectionWarning,
InvalidOS, InvalidOS,
InvalidPropertyType, InvalidPropertyType,
NonFirefoxFingerprint, NonFirefoxFingerprint,
@ -30,6 +28,7 @@ from .fingerprints import from_browserforge, generate_fingerprint
from .ip import Proxy, public_ip, valid_ipv4, valid_ipv6 from .ip import Proxy, public_ip, valid_ipv4, valid_ipv6
from .locale import geoip_allowed, get_geolocation, normalize_locale from .locale import geoip_allowed, get_geolocation, normalize_locale
from .pkgman import OS_NAME, get_path, installed_verstr from .pkgman import OS_NAME, get_path, installed_verstr
from .warnings import LeakWarning
from .xpi_dl import add_default_addons from .xpi_dl import add_default_addons
LAUNCH_FILE = { LAUNCH_FILE = {
@ -197,12 +196,7 @@ def check_custom_fingerprint(fingerprint: Fingerprint) -> None:
'If this is intentional, pass `i_know_what_im_doing=True`.' 'If this is intentional, pass `i_know_what_im_doing=True`.'
) )
warnings.warn( LeakWarning.warn('custom_fingerprint', False)
'Passing your own fingerprint is not recommended. '
'BrowserForge fingerprints are automatically generated within Camoufox '
'based on the provided `os` and `screen` constraints.',
category=DetectionWarning,
)
def check_valid_os(os: ListOrString) -> None: def check_valid_os(os: ListOrString) -> None:
@ -249,6 +243,45 @@ def set_into(target: Dict[str, Any], key: str, value: Any) -> None:
target[key] = value target[key] = value
def is_domain_set(
config: Dict[str, Any],
*properties: str,
) -> bool:
"""
Checks if a domain is set in the config.
"""
for prop in properties:
# If the . prefix exists, check if the domain is a prefix of any key in the config
if prop.endswith('.'):
if any(key.startswith(prop) for key in config):
return True
# Otherwise, check if the domain is a direct key in the config
else:
if prop in config:
return True
return False
def warn_manual_config(config: Dict[str, Any]) -> None:
"""
Warns the user if they are manually setting properties that Camoufox already sets internally.
"""
# Manual locale setting
if is_domain_set(
config, 'navigator.language', 'navigator.languages', 'headers.Accept-Language'
):
LeakWarning.warn('locale', False)
# Manual User-Agent setting
if is_domain_set(config, 'headers.User-Agent'):
LeakWarning.warn('header-ua', False)
# Manual navigator setting
if is_domain_set(config, 'navigator.'):
LeakWarning.warn('navigator', False)
# Manual screen/window setting
if is_domain_set(config, 'screen.', 'window.', 'document.body.'):
LeakWarning.warn('viewport', False)
def get_launch_options( def get_launch_options(
*, *,
config: Optional[Dict[str, Any]] = None, config: Optional[Dict[str, Any]] = None,
@ -282,24 +315,27 @@ def get_launch_options(
if config is None: if config is None:
config = {} config = {}
# Set default values for optional arguments
if addons is None: if addons is None:
addons = [] addons = []
if args is None: if args is None:
args = [] args = []
if firefox_user_prefs is None: if firefox_user_prefs is None:
firefox_user_prefs = {} firefox_user_prefs = {}
if i_know_what_im_doing is None:
i_know_what_im_doing = False
# Warn the user if headless is being used # Warn the user if headless is being used
# https://github.com/daijro/camoufox/issues/26 # https://github.com/daijro/camoufox/issues/26
if headless: if headless:
warnings.warn( LeakWarning.warn('headless', i_know_what_im_doing)
'It is currently not recommended to use headless mode in Camoufox. '
'Some WAFs are able to detect headless browsers. The issue is currently being investigated.',
category=DetectionWarning,
)
elif headless is None: elif headless is None:
headless = False headless = False
# Warn the user for manual config settings
if not i_know_what_im_doing:
warn_manual_config(config)
# Assert the target OS is valid # Assert the target OS is valid
if os: if os:
check_valid_os(os) check_valid_os(os)
@ -366,6 +402,14 @@ def get_launch_options(
geolocation = get_geolocation(geoip) geolocation = get_geolocation(geoip)
config.update(geolocation.as_config()) config.update(geolocation.as_config())
# Raise a warning when a proxy is being used without spoofing geolocation
elif (
proxy
and 'localhost' not in proxy.get('server', '')
and not is_domain_set('geolocation', config)
):
LeakWarning.warn('proxy_without_geoip')
# Set locale # Set locale
if locale: if locale:
parsed_locale = normalize_locale(locale) parsed_locale = normalize_locale(locale)
@ -391,10 +435,13 @@ def get_launch_options(
if block_webrtc: if block_webrtc:
firefox_user_prefs['media.peerconnection.enabled'] = False firefox_user_prefs['media.peerconnection.enabled'] = False
if allow_webgl: if allow_webgl:
LeakWarning.warn('allow_webgl', i_know_what_im_doing)
firefox_user_prefs['webgl.disabled'] = False firefox_user_prefs['webgl.disabled'] = False
# Launch # Load the addons
threaded_try_load_addons(get_debug_port(args), addons) threaded_try_load_addons(get_debug_port(args), addons)
# Prepare environment variables to pass to Camoufox
env_vars = { env_vars = {
**get_env_vars(config, target_os), **get_env_vars(config, target_os),
**(cast(Dict[str, Union[str, float, bool]], environ) if env is None else env), **(cast(Dict[str, Union[str, float, bool]], environ) if env is None else env),

View file

@ -0,0 +1,44 @@
import inspect
import warnings
from pathlib import Path
from typing import Optional
from camoufox.pkgman import load_yaml
WARNINGS_DATA = load_yaml('warnings.yml')
class LeakWarning(RuntimeWarning):
"""
Raised when a the user has a setting enabled that can cause detection.
"""
@staticmethod
def warn(warning_key: str, i_know_what_im_doing: Optional[bool] = None) -> None:
"""
Warns the user if a passed parameter can cause leaks.
"""
warning = WARNINGS_DATA[warning_key]
if i_know_what_im_doing:
return
if i_know_what_im_doing is not None:
warning += '\nIf this is intentional, pass `i_know_what_im_doing=True`.'
# Get caller information
current_module = Path(__file__).parent
frame = inspect.currentframe()
while frame:
if not Path(frame.f_code.co_filename).is_relative_to(current_module):
break
frame = frame.f_back
if frame:
warnings.warn_explicit(
warning,
category=LeakWarning,
filename=frame.f_code.co_filename,
lineno=frame.f_lineno,
)
return
warnings.warn(warning, category=LeakWarning)

View file

@ -0,0 +1,32 @@
headless: >-
Headless mode in Camoufox is not recommended at this time.
Some WAFs are able to detect headless browsers. The issue is currently being investigated.
navigator: >-
Manually setting navigator properties is not recommended.
Device information is automatically generated within Camoufox
based on the provided `os`.
locale: >-
Use the `locale` parameter in Camoufox instead of setting the config manually.
header-ua: >-
Do not set the header.User-Agent manually. Camoufox will generate a User-Agent for you.
viewport: >-
Manually setting screen & window properties is not recommended.
Screen dimensions are randomly generated within Camoufox
based on the provided screen constraints. See here:
https://github.com/daijro/camoufox/tree/main/pythonlib#browserforge-integration.
custom_fingerprint: >-
Passing your own fingerprint is not recommended.
BrowserForge fingerprints are automatically generated within Camoufox
based on the provided `os` and `screen` constraints.
proxy_without_geoip: >-
When using a proxy, it is heavily recommended that you pass `geoip=True`.
allow_webgl: >-
Enabling WebGL can lead to Canvas fingerprinting and detection.
Camoufox will automatically spoof your vendor and renderer, but it cannot spoof your WebGL fingerprint.

View file

@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
[tool.poetry] [tool.poetry]
name = "camoufox" name = "camoufox"
version = "0.2.7" version = "0.2.8"
description = "Wrapper around Playwright to help launch Camoufox" description = "Wrapper around Playwright to help launch Camoufox"
authors = ["daijro <daijro.dev@gmail.com>"] authors = ["daijro <daijro.dev@gmail.com>"]
license = "MIT" license = "MIT"