Skip to content

Conversation

@PopcornDev1
Copy link

Summary

This PR resolves Issue daijro#443 and adds critical detection prevention improvements to all fingerprint spoofing patches.

Changes:

  • Re-enables WebRTC IP spoofing and fix critical leaks
  • Adds self-destruct pattern to prevent fingerprinting script detection
  • Fixes timezone persistence across page navigations
  • Adds global navigator config support (platform, hardwareConcurrency, timezone)

Changed Patches

1. navigator-spoofing.patch (new)

Fixes Issue daijro#443 where global CAMOU_CONFIG settings weren't being applied.

What it fixes:

  • navigator.platform not spoofed
  • navigator.hardwareConcurrency not spoofed
  • Global timezone config not applied
  • geoip=True crash (SIGSEGV)

Implementation:

  • Adds MaskConfig hooks to Navigator::GetPlatform() and Navigator::HardwareConcurrency()
  • Uses lazy initialization for timezone (EnsureGlobalTimezoneInitialized()) to avoid SIGSEGV from calling SpiderMonkey before engine is ready

2. webrtc-ip-spoofing.patch (re-enabled and fixed)

Re-enables WebRTC IP spoofing with leak prevention.

New additions:

  • getStats() API sanitization - Sanitizes mIceCandidateStats[].mAddress values in PeerConnectionImpl::GetStats() callback
  • IPv6 regex - Now handles all compressed formats:
    • ::1 (loopback)
    • fe80::1 (link-local)
    • 2001:db8::1 (documentation)
    • ::ffff:192.168.1.1 (IPv4-mapped)
    • :: (all zeros)
    • Full 8-group format

3. timezone-spoofing.patch (improved)

Adds timezone persistence across page navigations.

Problem: When a page navigates, a new JS realm is created that doesn't inherit the timezone override. The timezone would revert to default.

Solution: Added hook in nsGlobalWindowOuter::SetNewDocument():

// After new inner window realm is created
if (TimezoneManager::GetTimezone(userContextId, storedTimezone)) {
  JS::SetRealmTimeZoneOverride(cx, timezoneASCII.get());
}

4. audio-fingerprint-manager.patch (improved)

Adds DisableFunction pattern to prevent detection.

Problem: window.setAudioFingerprintSeed was permanently visible to page JavaScript, allowing detection.

Solution: Dual self-destruct pattern:

  1. JS_DeleteProperty - Removes function from window after first call
  2. DisableFunction + IsFunctionEnabledForWebIDL - Prevents function from appearing on new pages in same context

Also added 3-level userContextId resolution (document principal → docshell → top browsing context).

5. screen-spoofing.patch (improved)

Adds DisableFunction pattern to both functions:

  • window.setScreenDimensions
  • window.setScreenColorDepth

Same self-destruct implementation as audio patch.

PopcornDev1 and others added 3 commits February 3, 2026 17:50
…urrency, and timezone (daijro#443)

Fixes issue where global CAMOU_CONFIG settings for navigator.platform,
navigator.hardwareConcurrency, and timezone were not applied in the main
window Navigator (only WorkerNavigator was patched via fingerprint-injection.patch).
- navigator-spoofing.patch: Global config fixes for platform/hardwareConcurrency/timezone
- timezone-spoofing.patch: Persistence across page navigation via SetNewDocument hook
- audio-fingerprint-manager.patch: Full 6-method coverage with self-destruct pattern
- screen-spoofing.patch: Per-context dimensions with self-destruct pattern
- webrtc-ip-spoofing.patch: Re-enabled with getStats() sanitization + comprehensive IPv6 regex
@PopcornDev1 PopcornDev1 requested a review from icepaq February 5, 2026 06:08
@coderabbitai
Copy link

coderabbitai bot commented Feb 5, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/resolve-issue-#443

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@icepaq
Copy link

icepaq commented Feb 9, 2026

@paragon-review

@paragon-review
Copy link

paragon-review bot commented Feb 9, 2026

Paragon Summary

This pull request review identified 10 issues across 2 categories in 5 files. The review analyzed code changes, potential bugs, security vulnerabilities, performance issues, and code quality concerns using automated analysis tools.

Fixes Issue daijro#443 by enabling global navigator config spoofing, re-enables WebRTC IP spoofing with leak prevention, and adds self-destruct patterns across fingerprint patches to prevent detection.

Key changes:

  • Re-enabled WebRTC IP spoofing with getStats() sanitization and IPv6 regex
  • Fixed Issue a few bugs with timezone and webrtc in the ff146 beta daijro/camoufox#443 - global navigator config support (platform, hardwareConcurrency, timezone, geoip crash)
  • Added timezone persistence across page navigations via nsGlobalWindowOuter::SetNewDocument() hook
  • Added self-destruct/DisableFunction pattern to prevent detection (audio, screen functions)
  • Created new patches: navigator-spoofing.patch, audio-fingerprint-manager.patch, screen-spoofing.patch

Confidence score: 3/5

  • This PR has moderate risk due to 2 high-priority issues that should be addressed
  • Score reflects significant bugs, performance issues, or architectural concerns
  • Review high-priority findings carefully before merging

5 files reviewed, 10 comments

Severity breakdown: High: 2, Medium: 8


Tip: @paragon-run <instructions> to chat with our agent or push fixes!

Dashboard

+
+ mLocalRequestedSDP = sanitizedSDP;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Race condition in getStats callback closure capturing mWindow

Race condition in getStats callback closure capturing mWindow. Window pointer may become dangling or null before lambda executes on main thread.

View Details

Location: patches/webrtc-ip-spoofing.patch (lines 549)

Analysis

Race condition in getStats callback closure capturing mWindow

What fails Raw window pointer captured in lambda without lifetime safety
Result Captured window pointer may be invalid
Expected Use weak reference or proper lifetime management for window in async callback
Impact Potential use-after-free or null dereference when window is closed before callback
How to reproduce
Close window while GetStats is pending, then wait for callback
AI Fix Prompt
Fix this issue: Race condition in getStats callback closure capturing mWindow. Window pointer may become dangling or null before lambda executes on main thread.

Location: patches/webrtc-ip-spoofing.patch (lines 549)
Problem: Raw window pointer captured in lambda without lifetime safety
Current behavior: Captured window pointer may be invalid
Expected: Use weak reference or proper lifetime management for window in async callback
Steps to reproduce: Close window while GetStats is pending, then wait for callback

Provide a code fix.


Tip: Reply with @paragon-run to automatically fix this issue

+
+ mLocalRequestedSDP = sanitizedSDP;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: IPv6 detection in getStats uses FindChar(':') which may incorrectly match IPv4

IPv6 detection in getStats uses FindChar(':') which may incorrectly match IPv4 addresses containing colons like '192.168.1.1:8080'. Combined with port numbers this causes incorrect address type detection.

View Details

Location: patches/webrtc-ip-spoofing.patch (lines 549)

Analysis

**IPv6 detection in getStats uses FindChar(':') which may incorrectly match IPv4 addresses containing **

What fails Overly simple IPv6 detection using single colon check
Result Address incorrectly detected as IPv6 due to port colon
Expected Proper detection using IPv6 specific checks like multiple colons or bracketed notation
Impact IPv4-like addresses with ports may be treated as IPv6, breaking sanitization
How to reproduce
Provide stats with address '192.168.1.1:8080' and observe IPv6 handling
AI Fix Prompt
Fix this issue: IPv6 detection in getStats uses FindChar(':') which may incorrectly match IPv4 addresses containing colons like '192.168.1.1:8080'. Combined with port numbers this causes incorrect address type detection.

Location: patches/webrtc-ip-spoofing.patch (lines 549)
Problem: Overly simple IPv6 detection using single colon check
Current behavior: Address incorrectly detected as IPv6 due to port colon
Expected: Proper detection using IPv6 specific checks like multiple colons or bracketed notation
Steps to reproduce: Provide stats with address '192.168.1.1:8080' and observe IPv6 handling

Provide a code fix.


Tip: Reply with @paragon-run to automatically fix this issue

"BarProps.h",
@@ -326,4 +326,9 @@ SOURCES += [
"nsWindowSizes.cpp",
]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: AudioBuffer::CopyFromChannel applies fingerprint transformation to destination array after copy

AudioBuffer::CopyFromChannel applies fingerprint transformation to destination array after copy. This modifies caller's buffer which may not be expected and could cause data corruption.

View Details

Location: patches/audio-fingerprint-manager.patch (lines 184)

Analysis

AudioBuffer::CopyFromChannel applies fingerprint transformation to destination array after copy

What fails Fingerprint transformation applied to caller's destination buffer
Result Destination array contains fingerprinted data instead of original
Expected Apply transformation to source data before copy, not to caller's destination buffer
Impact Unexpected modification of caller's buffer could cause audio processing errors
How to reproduce
Call copyFromChannel with destination array, then check if array was modified
AI Fix Prompt
Fix this issue: AudioBuffer::CopyFromChannel applies fingerprint transformation to destination array after copy. This modifies caller's buffer which may not be expected and could cause data corruption.

Location: patches/audio-fingerprint-manager.patch (lines 184)
Problem: Fingerprint transformation applied to caller's destination buffer
Current behavior: Destination array contains fingerprinted data instead of original
Expected: Apply transformation to source data before copy, not to caller's destination buffer
Steps to reproduce: Call copyFromChannel with destination array, then check if array was modified

Provide a code fix.


Tip: Reply with @paragon-run to automatically fix this issue

+
+ mLocalRequestedSDP = sanitizedSDP;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance: Extremely complex IPv6 regex pattern may cause performance issues or miss edge cases

Extremely complex IPv6 regex pattern may cause performance issues or miss edge cases. The pattern is 400+ characters and attempts to match all IPv6 compressed formats but may have regex engine compatibility issues or exponential backtracking problems.

View Details

Location: patches/webrtc-ip-spoofing.patch (lines 549)

Analysis

Extremely complex IPv6 regex pattern may cause performance issues or miss edge cases

What fails The IPv6 regex uses complex alternation with nested groups that may cause catastrophic backtracking in some regex engines or miss valid IPv6 formats.
Result Potential for regex backtracking performance issues or incorrect matching on edge case IPv6 addresses.
Expected Use a simpler approach or validate IPv6 addresses programmatically instead of with a single massive regex.
Impact WebRTC connection performance could degrade or IPv6 addresses might leak if regex fails to match valid patterns.
How to reproduce
Test the regex against various IPv6 formats including ::1, fe80::1, 2001:db8::1, ::ffff:192.168.1.1, and malformed inputs.
AI Fix Prompt
Fix this issue: Extremely complex IPv6 regex pattern may cause performance issues or miss edge cases. The pattern is 400+ characters and attempts to match all IPv6 compressed formats but may have regex engine compatibility issues or exponential backtracking problems.

Location: patches/webrtc-ip-spoofing.patch (lines 549)
Problem: The IPv6 regex uses complex alternation with nested groups that may cause catastrophic backtracking in some regex engines or miss valid IPv6 formats.
Current behavior: Potential for regex backtracking performance issues or incorrect matching on edge case IPv6 addresses.
Expected: Use a simpler approach or validate IPv6 addresses programmatically instead of with a single massive regex.
Steps to reproduce: Test the regex against various IPv6 formats including ::1, fe80::1, 2001:db8::1, ::ffff:192.168.1.1, and malformed inputs.

Provide a code fix.


Tip: Reply with @paragon-run to automatically fix this issue

+
+ uint32_t userContextId = 0;
+
+ // 1) document principal
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maintainability: Severe code duplication of userContextId resolution logic across multiple files

Severe code duplication of userContextId resolution logic across multiple files. The 3-level fallback pattern is copy-pasted in AudioFingerprintManager, ScreenDimensionManager, TimezoneManager, WebRTCIPManager, and nsGlobalWindowInner. This creates maintenance burden and inconsistency risk.

View Details

Location: patches/audio-fingerprint-manager.patch (lines 97)

Analysis

Severe code duplication of userContextId resolution logic across multiple files

What fails The same 3-level userContextId resolution pattern (document principal → docshell → top browsing context) is duplicated across 5+ files.
Result Code duplication leads to maintenance overhead and potential inconsistencies if fixes are applied to only some locations.
Expected Extract the userContextId resolution into a shared utility function in a common header.
Impact Increases maintenance burden and risk of bugs when modifications are needed. Creates 70+ lines of duplicated code per file.
How to reproduce
Compare code in AudioFingerprintManager.cpp:91-123, ScreenDimensionManager.cpp:91-123, TimezoneManager.cpp:29-61, WebRTCIPManager.cpp:53-61, and nsGlobalWindowInner.cpp:7431-7460.
AI Fix Prompt
Fix this issue: Severe code duplication of userContextId resolution logic across multiple files. The 3-level fallback pattern is copy-pasted in AudioFingerprintManager, ScreenDimensionManager, TimezoneManager, WebRTCIPManager, and nsGlobalWindowInner. This creates maintenance burden and inconsistency risk.

Location: patches/audio-fingerprint-manager.patch (lines 97)
Problem: The same 3-level userContextId resolution pattern (document principal → docshell → top browsing context) is duplicated across 5+ files.
Current behavior: Code duplication leads to maintenance overhead and potential inconsistencies if fixes are applied to only some locations.
Expected: Extract the userContextId resolution into a shared utility function in a common header.
Steps to reproduce: Compare code in AudioFingerprintManager.cpp:91-123, ScreenDimensionManager.cpp:91-123, TimezoneManager.cpp:29-61, WebRTCIPManager.cpp:53-61, and nsGlobalWindowInner.cpp:7431-7460.

Provide a code fix.


Tip: Reply with @paragon-run to automatically fix this issue

return;
}

+ // Apply audio fingerprint transformation to frequency data
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: AnalyserNode applies fingerprint transformation to mOutputBuffer before copying to destination

AnalyserNode applies fingerprint transformation to mOutputBuffer before copying to destination. This modifies internal buffer that may be reused, causing inconsistent results across multiple get*Data calls.

View Details

Location: patches/audio-fingerprint-manager.patch (lines 366)

Analysis

AnalyserNode applies fingerprint transformation to mOutputBuffer before copying to destination

What fails Fingerprint transformation applied to reusable internal buffer
Result Second call returns already-fingerprinted data from mOutputBuffer
Expected Apply transformation to destination array after copy, not to reusable internal buffer
Impact Internal buffer modification causes inconsistent fingerprinting across calls
How to reproduce
Call GetFloatFrequencyData twice with same seed, compare results
AI Fix Prompt
Fix this issue: AnalyserNode applies fingerprint transformation to mOutputBuffer before copying to destination. This modifies internal buffer that may be reused, causing inconsistent results across multiple get*Data calls.

Location: patches/audio-fingerprint-manager.patch (lines 366)
Problem: Fingerprint transformation applied to reusable internal buffer
Current behavior: Second call returns already-fingerprinted data from mOutputBuffer
Expected: Apply transformation to destination array after copy, not to reusable internal buffer
Steps to reproduce: Call GetFloatFrequencyData twice with same seed, compare results

Provide a code fix.


Tip: Reply with @paragon-run to automatically fix this issue

+
+ AudioFingerprintManager::SetSeed(userContextId, seed);
+
+ // Self-destruct: remove this function from the window object after first use
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Missing null check for JSContext in SetAudioFingerprintSeed self-destruct

Missing null check for JSContext in SetAudioFingerprintSeed self-destruct. If nsContentUtils::GetCurrentJSContext() returns null, the code silently skips self-destruct but doesn't indicate failure to caller.

View Details

Location: patches/audio-fingerprint-manager.patch (lines 249)

Analysis

Missing null check for JSContext in SetAudioFingerprintSeed self-destruct

What fails Missing null check for JSContext in self-destruct code
Result Function not removed from window but no error reported
Expected Should handle null JSContext gracefully or report error
Impact Inconsistent state where function remains visible after use
How to reproduce
Call setAudioFingerprintSeed when JSContext is not available
AI Fix Prompt
Fix this issue: Missing null check for JSContext in SetAudioFingerprintSeed self-destruct. If nsContentUtils::GetCurrentJSContext() returns null, the code silently skips self-destruct but doesn't indicate failure to caller.

Location: patches/audio-fingerprint-manager.patch (lines 249)
Problem: Missing null check for JSContext in self-destruct code
Current behavior: Function not removed from window but no error reported
Expected: Should handle null JSContext gracefully or report error
Steps to reproduce: Call setAudioFingerprintSeed when JSContext is not available

Provide a code fix.


Tip: Reply with @paragon-run to automatically fix this issue

--- a/intl/components/src/TimeZone.cpp
+++ b/intl/components/src/TimeZone.cpp
@@ -8,7 +8,8 @@
diff --git a/dom/base/TimezoneManager.cpp b/dom/base/TimezoneManager.cpp
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: TimezoneManager::SetTimezone doesn't validate timezone string before storing

TimezoneManager::SetTimezone doesn't validate timezone string before storing. Invalid timezone identifiers are stored in RoverfoxStorageManager and only fail later when applied to JS realm.

View Details

Location: patches/timezone-spoofing.patch (lines 1)

Analysis

TimezoneManager::SetTimezone doesn't validate timezone string before storing

What fails Missing timezone validation in TimezoneManager::SetTimezone
Result Invalid timezone stored and only fails when JS::SetRealmTimeZoneOverride called
Expected Should validate timezone before storing to storage
Impact Wasted storage operations and delayed error detection
How to reproduce
Call setTimezone with invalid string like 'Invalid/Timezone'
AI Fix Prompt
Fix this issue: TimezoneManager::SetTimezone doesn't validate timezone string before storing. Invalid timezone identifiers are stored in RoverfoxStorageManager and only fail later when applied to JS realm.

Location: patches/timezone-spoofing.patch (lines 1)
Problem: Missing timezone validation in TimezoneManager::SetTimezone
Current behavior: Invalid timezone stored and only fails when JS::SetRealmTimeZoneOverride called
Expected: Should validate timezone before storing to storage
Steps to reproduce: Call setTimezone with invalid string like 'Invalid/Timezone'

Provide a code fix.


Tip: Reply with @paragon-run to automatically fix this issue

+
+ mLocalRequestedSDP = sanitizedSDP;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: WebRTCIPManager includes RoverfoxStorageManager

WebRTCIPManager includes RoverfoxStorageManager.h in cpp but not header. This creates inconsistent compilation units if header is included elsewhere without the required storage header.

View Details

Location: patches/webrtc-ip-spoofing.patch (lines 549)

Analysis

WebRTCIPManager includes RoverfoxStorageManager.h in cpp but not header

What fails Header dependency violation - implementation header used only in .cpp
Result Missing RoverfoxStorageManager.h causes undefined types
Expected RoverfoxStorageManager.h should be in WebRTCIPManager.h if used in inline functions
Impact Potential ODR violations or compilation errors in unified builds
How to reproduce
Include WebRTCIPManager.h in another file that uses storage
AI Fix Prompt
Fix this issue: WebRTCIPManager `includes` RoverfoxStorageManager.h in cpp but not header. This creates inconsistent compilation units if header is included elsewhere without the required storage header.

Location: patches/webrtc-ip-spoofing.patch (lines 549)
Problem: Header dependency violation - implementation header used only in .cpp
Current behavior: Missing RoverfoxStorageManager.h causes undefined types
Expected: RoverfoxStorageManager.h should be in WebRTCIPManager.h if used in inline functions
Steps to reproduce: Include WebRTCIPManager.h in another file that uses storage

Provide a code fix.


Tip: Reply with @paragon-run to automatically fix this issue

+ RoverfoxStorageManager::PutUint(heightKey, static_cast<uint32_t>(aHeight));
+}
+
+/* static */ bool
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: ScreenDimensionManager::GetDimensions leaves output parameters uninitialized on failure

ScreenDimensionManager::GetDimensions leaves output parameters uninitialized on failure. When width or height is 0, the function returns false but doesn't set aWidth and aHeight, leaving them with undefined values that callers might use.

View Details

Location: patches/screen-spoofing.patch (lines 48)

Analysis

ScreenDimensionManager::GetDimensions leaves output parameters uninitialized on failure

What fails GetDimensions returns false when width/height are 0 but doesn't initialize the output parameters.
Result aWidth and aHeight contain garbage/undefined values when function returns false.
Expected Output parameters should be set to 0 or default values before returning false.
Impact Potential use of uninitialized memory leading to undefined behavior or incorrect screen calculations.
How to reproduce
Call ScreenDimensionManager::GetDimensions with a context that has no stored dimensions, then attempt to use the width/height values.
Patch Details
/* static */ bool
 ScreenDimensionManager::GetDimensions(uint32_t aUserContextId, int32_t* aWidth, int32_t* aHeight) {
+  *aWidth = 0;
+  *aHeight = 0;
   nsString widthKey, heightKey;
AI Fix Prompt
Fix this issue: ScreenDimensionManager::GetDimensions leaves output parameters uninitialized on failure. When width or height is 0, the function returns false but doesn't set aWidth and aHeight, leaving them with undefined values that callers might use.

Location: patches/screen-spoofing.patch (lines 48)
Problem: GetDimensions returns false when width/height are 0 but doesn't initialize the output parameters.
Current behavior: aWidth and aHeight contain garbage/undefined values when function returns false.
Expected: Output parameters should be set to 0 or default values before returning false.
Steps to reproduce: Call ScreenDimensionManager::GetDimensions with a context that has no stored dimensions, then attempt to use the width/height values.

Provide a code fix.


Tip: Reply with @paragon-run to automatically fix this issue

+ if (candStat.mAddress.WasPassed() && !candStat.mAddress.Value().IsEmpty()) {
+ nsAutoCString addr;
+ addr.Assign(NS_ConvertUTF16toUTF8(candStat.mAddress.Value()));
+ bool isIPv6 = addr.FindChar(':') != kNotFound;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is paragon right about the risk of a port falsely setting the ipv6 flag? If so maybe use regex or look for columns AND NOT periods?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants