Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 44 additions & 27 deletions browserforge/fingerprints/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,23 @@ def dumps(self) -> str:
# Instead, convert to a dict first
return json.dumps(asdict(self))

@classmethod
def loads(cls, json_string: str) -> 'Fingerprint':
"""
Loads a fingerprint from a JSON string.
"""
data = json.loads(json_string)

# Reconstruct nested dataclasses
if 'screen' in data:
data['screen'] = ScreenFingerprint(**data['screen'])
if 'navigator' in data:
data['navigator'] = NavigatorFingerprint(**data['navigator'])
if 'videoCard' in data and data['videoCard']:
data['videoCard'] = VideoCard(**data['videoCard'])

return cls(**data)


@dataclass
class Screen:
Expand All @@ -111,10 +128,10 @@ class Screen:

def __post_init__(self):
if (
None not in (self.min_width, self.max_width)
and self.min_width > self.max_width
or None not in (self.min_height, self.max_height)
and self.min_height > self.max_height
None not in (self.min_width, self.max_width)
and self.min_width > self.max_width
or None not in (self.min_height, self.max_height)
and self.min_height > self.max_height
):
raise ValueError(
"Invalid screen constraints: min values cannot be greater than max values"
Expand All @@ -133,12 +150,12 @@ class FingerprintGenerator:
fingerprint_generator_network = BayesianNetwork(get_fingerprint_network())

def __init__(
self,
screen: Optional[Screen] = None,
strict: bool = False,
mock_webrtc: bool = False,
slim: bool = False,
**header_kwargs,
self,
screen: Optional[Screen] = None,
strict: bool = False,
mock_webrtc: bool = False,
slim: bool = False,
**header_kwargs,
):
"""
Initializes the FingerprintGenerator with the given options.
Expand All @@ -159,13 +176,13 @@ def __init__(
self.slim: bool = slim

def generate(
self,
*,
screen: Optional[Screen] = None,
strict: Optional[bool] = None,
mock_webrtc: Optional[bool] = None,
slim: Optional[bool] = None,
**header_kwargs,
self,
*,
screen: Optional[Screen] = None,
strict: Optional[bool] = None,
mock_webrtc: Optional[bool] = None,
slim: Optional[bool] = None,
**header_kwargs,
) -> Fingerprint:
"""
Generates a fingerprint and a matching set of ordered headers using a combination of the default options
Expand Down Expand Up @@ -224,9 +241,9 @@ def generate(
if fingerprint[attribute] == '*MISSING_VALUE*':
fingerprint[attribute] = None
if isinstance(fingerprint[attribute], str) and fingerprint[attribute].startswith(
'*STRINGIFIED*'
'*STRINGIFIED*'
):
fingerprint[attribute] = json.loads(fingerprint[attribute][len('*STRINGIFIED*') :])
fingerprint[attribute] = json.loads(fingerprint[attribute][len('*STRINGIFIED*'):])

# Manually add the set of accepted languages required by the input
accept_language_header_value = headers.get('Accept-Language', '')
Expand All @@ -243,7 +260,7 @@ def generate(
)

def partial_csp(
self, strict: Optional[bool], screen: Optional[Screen], filtered_values: Dict
self, strict: Optional[bool], screen: Optional[Screen], filtered_values: Dict
) -> Optional[Dict]:
"""
Generates partial content security policy (CSP) based on the provided options and filtered values.
Expand Down Expand Up @@ -289,22 +306,22 @@ def _is_screen_within_constraints(screen_string: str, screen_options: Screen) ->
bool: True if the screen dimensions are within the constraints, False otherwise.
"""
try:
screen = json.loads(screen_string[len('*STRINGIFIED*') :])
screen = json.loads(screen_string[len('*STRINGIFIED*'):])
return (
# Ensure that the screen width/height are greater than the minimum constraints
# Default missing values to -1 to ensure they are excluded
screen.get('width', -1) >= (screen_options.min_width or 0)
and screen.get('height', -1) >= (screen_options.min_height or 0)
# Ensure that the screen width/height are less than the maximum constraints
and screen.get('width', 0) <= (screen_options.max_width or 1e5)
and screen.get('height', 0) <= (screen_options.max_height or 1e5)
screen.get('width', -1) >= (screen_options.min_width or 0)
and screen.get('height', -1) >= (screen_options.min_height or 0)
# Ensure that the screen width/height are less than the maximum constraints
and screen.get('width', 0) <= (screen_options.max_width or 1e5)
and screen.get('height', 0) <= (screen_options.max_height or 1e5)
)
except (ValueError, TypeError):
return False

@staticmethod
def _transform_fingerprint(
fingerprint: Dict, headers: Dict, mock_webrtc: bool, slim: bool
fingerprint: Dict, headers: Dict, mock_webrtc: bool, slim: bool
) -> Fingerprint:
"""
Transforms fingerprint into a final dataclass instance.
Expand Down