Update pythonlib & README for beta.11

- Removed warnings for using headless mode
- Xvfb is now activated with headless='virtual'
- Correctly pass window.screenX & calculate window.screenY
- Update documentation on main README and pythonlib README
- Bumped pythonlib to 0.2.10
This commit is contained in:
daijro 2024-10-09 05:49:22 -05:00
parent 49cea6eca8
commit b2f74a24f3
10 changed files with 96 additions and 66 deletions

View file

@ -440,6 +440,18 @@ Miscellaneous (battery status, etc)
- Geolocation, timezone, and locale spoofing
- etc.
#### Stealth patches
- Avoids main world execution leaks. All page agent javascript is sandboxed
- Avoids frame execution context leaks
- Fixes `navigator.webdriver` detection
- Fixes Firefox headless detection via pointer type ([#26](https://github.com/daijro/camoufox/issues/26))
- Removed potentially leaking anti-zoom/meta viewport handling patches
- Uses non-default screen & window sizes
- Re-enable fission content isolations
- Re-enable PDF.js
- Other leaking config properties changed
#### Anti font fingerprinting
- Automatically uses the correct system fonts for your User Agent
@ -450,13 +462,6 @@ Miscellaneous (battery status, etc)
- Custom implementation of Playwright for the latest Firefox
- Various config patches to evade bot detection
- Fixes leaking Playwright patches:
- All page agent javascript is sandboxed
- Fixes frame execution context leaks
- Fixes `navigator.webdriver` detection
- Removed potentially leaking anti-zoom/meta viewport handling patches
- Re-enable fission content isolation
- Re-enable PDF.js
#### Debloat/Optimizations

View file

@ -136,9 +136,9 @@ Parameters:
ff_version (Optional[int]):
Firefox version to use. Defaults to the current Camoufox version.
To prevent leaks, only use this for special cases.
headless (Optional[bool]):
headless (Union[bool, Literal['virtual']]):
Whether to run the browser in headless mode. Defaults to False.
WARNING: Please avoid using headless mode until issue #26 is fixed.
If you are running linux, passing 'virtual' will use Xvfb.
executable_path (Optional[str]):
Custom Camoufox browser executable path.
firefox_user_prefs (Optional[Dict[str, Any]]):
@ -270,7 +270,7 @@ with sync_playwright() as p:
### Virtual Display
In headless mode, all browsers are prone to being detected by anti-bot services due to the drastic differences in the browser's architecture. It is generally **NOT** recommended to use Camoufox in headless mode on a non-Linux OS.
While Camoufox includes patches to prevent headless detection, running in headless mode may still be detectable in the future. It's recommended to use a virtual display buffer to run Camoufox headlessly.
If you are running Linux, and would like to run Camoufox headlessly in a virtual display, install `xvfb`:
@ -293,7 +293,7 @@ $ which Xvfb
/usr/bin/Xvfb
```
Now, passing `headless=True` will spawn a new lightweight virtual display in the background for Camoufox to run in.
Now, passing `headless='virtual'` will spawn a new lightweight virtual display in the background for Camoufox to run in.
<hr width=50>

View file

@ -7,6 +7,7 @@ from playwright.async_api import (
Playwright,
PlaywrightContextManager,
)
from typing_extensions import Literal
from .addons import DefaultAddons
from .utils import ListOrString, _clean_locals, get_launch_options
@ -51,7 +52,7 @@ async def AsyncNewBrowser(
screen: Optional[Screen] = None,
fingerprint: Optional[Fingerprint] = None,
ff_version: Optional[int] = None,
headless: Optional[bool] = None,
headless: Optional[Union[bool, Literal['virtual']]] = None,
executable_path: Optional[str] = None,
firefox_user_prefs: Optional[Dict[str, Any]] = None,
proxy: Optional[Dict[str, str]] = None,
@ -105,9 +106,9 @@ async def AsyncNewBrowser(
ff_version (Optional[int]):
Firefox version to use. Defaults to the current Camoufox version.
To prevent leaks, only use this for special cases.
headless (Optional[bool]):
headless (Union[bool, Literal['virtual']]):
Whether to run the browser in headless mode. Defaults to False.
WARNING: Please avoid using headless mode until issue #26 is fixed.
If you are running linux, passing 'virtual' will use Xvfb.
executable_path (Optional[str]):
Custom Camoufox browser executable path.
firefox_user_prefs (Optional[Dict[str, Any]]):

View file

@ -141,3 +141,11 @@ class CannotExecuteXvfb(VirtualDisplayError):
"""
...
class VirtualDisplayNotSupported(VirtualDisplayError):
"""
Raised when the user tried to use a virtual display on a non-Linux OS.
"""
...

View file

@ -1,8 +1,9 @@
import re
from dataclasses import asdict
from random import randrange
from typing import Any, Dict, Optional
from browserforge.fingerprints import Fingerprint, FingerprintGenerator
from browserforge.fingerprints import Fingerprint, FingerprintGenerator, Screen
from camoufox.pkgman import load_yaml
@ -30,15 +31,38 @@ def _cast_to_properties(
if isinstance(data, dict):
_cast_to_properties(camoufox_data, type_key, data, ff_version)
continue
# Fix values that are out of bounds
if type_key.startswith("screen.") and isinstance(data, int) and data < 0:
data = 0
# Replace the Firefox versions with ff_version
if ff_version and isinstance(data, str):
data = re.sub(r'(?<!\d)(1[0-9]{2})(\.0)(?!\d)', rf'{ff_version}\2', data)
camoufox_data[type_key] = data
def handle_screenXY(camoufox_data: Dict[str, Any], fp_screen: Screen) -> None:
"""
Helper method to set window.screenY based on Browserforge's screenX value.
"""
# Default screenX to 0 if not provided
screenX = fp_screen.screenX
if not screenX:
camoufox_data['window.screenX'] = 0
camoufox_data['window.screenY'] = 0
return
# If screenX is within [-50, 50], use the same value for screenY
if screenX in range(-50, 51):
camoufox_data['window.screenY'] = screenX
return
# Browserforge thinks the browser is windowed. # Randomly generate a screenY value.
screenY = fp_screen.availHeight - fp_screen.outerHeight
if screenY == 0:
camoufox_data['window.screenY'] = 0
elif screenY > 0:
camoufox_data['window.screenY'] = randrange(0, screenY) # nosec
else:
camoufox_data['window.screenY'] = randrange(screenY, 0) # nosec
def from_browserforge(fingerprint: Fingerprint, ff_version: Optional[str] = None) -> Dict[str, Any]:
"""
Converts a Browserforge fingerprint to a Camoufox config.
@ -50,6 +74,8 @@ def from_browserforge(fingerprint: Fingerprint, ff_version: Optional[str] = None
bf_dict=asdict(fingerprint),
ff_version=ff_version,
)
handle_screenXY(camoufox_data, fingerprint.screen)
return camoufox_data

View file

@ -7,6 +7,7 @@ from playwright.sync_api import (
Playwright,
PlaywrightContextManager,
)
from typing_extensions import Literal
from .addons import DefaultAddons
from .utils import ListOrString, _clean_locals, get_launch_options
@ -51,7 +52,7 @@ def NewBrowser(
screen: Optional[Screen] = None,
fingerprint: Optional[Fingerprint] = None,
ff_version: Optional[int] = None,
headless: Optional[bool] = None,
headless: Optional[Union[bool, Literal['virtual']]] = None,
executable_path: Optional[str] = None,
firefox_user_prefs: Optional[Dict[str, Any]] = None,
proxy: Optional[Dict[str, str]] = None,
@ -61,7 +62,7 @@ def NewBrowser(
i_know_what_im_doing: Optional[bool] = None,
debug: Optional[bool] = None,
**launch_options: Dict[str, Any]
) -> Browser:
) -> Union[Browser, BrowserContext]:
"""
Launches a new browser instance for Camoufox.
Accepts all Playwright Firefox launch options, along with the following:
@ -105,9 +106,9 @@ def NewBrowser(
ff_version (Optional[int]):
Firefox version to use. Defaults to the current Camoufox version.
To prevent leaks, only use this for special cases.
headless (Optional[bool]):
headless (Union[bool, Literal['virtual']]):
Whether to run the browser in headless mode. Defaults to False.
WARNING: Please avoid using headless mode until issue #26 is fixed.
If you are running linux, passing 'virtual' will use Xvfb.
executable_path (Optional[str]):
Custom Camoufox browser executable path.
firefox_user_prefs (Optional[Dict[str, Any]]):

View file

@ -3,7 +3,6 @@ import sys
from os import environ
from pprint import pprint
from random import randrange
from shutil import which
from typing import Any, Dict, List, Literal, Optional, Tuple, Union, cast
import numpy as np
@ -227,35 +226,6 @@ def _clean_locals(data: Dict[str, Any]) -> Dict[str, Any]:
return data
def handle_headless(
headless: Optional[bool],
env: Dict[str, Union[str, float, bool]],
debug: Optional[bool],
i_know_what_im_doing: Optional[bool],
) -> bool:
"""
Handles the headless mode.
"""
# If headless is not being used, return False
if not headless:
return False
# Warn the user if headless is being used on a non-Linux OS
# https://github.com/daijro/camoufox/issues/26
if OS_NAME != 'lin':
LeakWarning.warn('headless-non-linux', i_know_what_im_doing)
return True
# If Xvfb is avaliable, use it instead of headless to prevent leaks
if which('Xvfb'):
env['DISPLAY'] = VIRTUAL_DISPLAY.new_or_reuse(debug=debug)
return False
# If Linux is being used and Xvfb is not avaliable, warn the user
LeakWarning.warn('headless-linux', i_know_what_im_doing)
return True
def merge_into(target: Dict[str, Any], source: Dict[str, Any]) -> None:
"""
Merges new keys/values from the source dictionary into the target dictionary.
@ -335,7 +305,7 @@ def get_launch_options(
allow_webgl: Optional[bool] = None,
proxy: Optional[Dict[str, str]] = None,
ff_version: Optional[int] = None,
headless: Optional[bool] = None,
headless: Optional[Union[bool, Literal['virtual']]] = None,
firefox_user_prefs: Optional[Dict[str, Any]] = None,
launch_options: Optional[Dict[str, Any]] = None,
debug: Optional[bool] = None,
@ -348,6 +318,8 @@ def get_launch_options(
config = {}
# Set default values for optional arguments
if headless is None:
headless = False
if addons is None:
addons = []
if args is None:
@ -360,7 +332,9 @@ def get_launch_options(
env = cast(Dict[str, Union[str, float, bool]], environ)
# Handle headless mode cases
headless = handle_headless(headless, env, debug, i_know_what_im_doing)
if headless == 'virtual':
env['DISPLAY'] = VIRTUAL_DISPLAY.new_or_reuse(debug=debug)
headless = False
# Warn the user for manual config settings
if not i_know_what_im_doing:

View file

@ -4,7 +4,12 @@ from glob import glob
from shutil import which
from typing import List, Optional
from camoufox.exceptions import CannotExecuteXvfb, CannotFindXvfb
from camoufox.exceptions import (
CannotExecuteXvfb,
CannotFindXvfb,
VirtualDisplayNotSupported,
)
from camoufox.pkgman import OS_NAME
class VirtualDisplay:
@ -72,6 +77,8 @@ class VirtualDisplay:
"""
Get the display number
"""
self.assert_linux()
if self.proc is None:
self.execute_xvfb_singleton(debug)
elif debug:
@ -116,5 +123,13 @@ class VirtualDisplay:
self._display = self._free_display()
return self._display
@staticmethod
def assert_linux():
"""
Assert that the current OS is Linux
"""
if OS_NAME != 'lin':
raise VirtualDisplayNotSupported("Virtual display is only supported on Linux.")
VIRTUAL_DISPLAY = VirtualDisplay()

View file

@ -1,12 +1,3 @@
headless-non-linux: >-
Headless mode is only recommended on Linux at this time.
Some WAFs are able to detect headless browsers. The issue is currently being investigated.
headless-linux: >-
Headless mode is only recommended on Linux with Xvfb installed.
Please see the install guide here:
https://github.com/daijro/camoufox/tree/main/pythonlib#virtual-display
navigator: >-
Manually setting navigator properties is not recommended.
Device information is automatically generated within Camoufox
@ -39,4 +30,13 @@ allow_webgl: >-
ff_version: >-
Spoofing the Firefox version will likely lead to detection.
If rotating the Firefox version is absolutely necessary, it would be more advisable to
rotate between older versions of Camoufox instead.
rotate between older versions of Camoufox instead.
# headless-non-linux: >-
# Headless mode is only recommended on Linux at this time.
# Some WAFs are able to detect headless browsers. The issue is currently being investigated.
# headless-linux: >-
# Headless mode is only recommended on Linux with Xvfb installed.
# Please see the install guide here:
# https://github.com/daijro/camoufox/tree/main/pythonlib#virtual-display

View file

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