diff --git a/src-tauri/src/browser_runner.rs b/src-tauri/src/browser_runner.rs index 759724d..facd196 100644 --- a/src-tauri/src/browser_runner.rs +++ b/src-tauri/src/browser_runner.rs @@ -180,6 +180,49 @@ impl BrowserRunner { camoufox_config.geoip ); + // Check if we need to generate a new fingerprint on every launch + let mut updated_profile = profile.clone(); + if camoufox_config.randomize_fingerprint_on_launch == Some(true) { + log::info!( + "Generating random fingerprint for Camoufox profile: {}", + profile.name + ); + + // Create a config copy without the existing fingerprint to force generation of a new one + let mut config_for_generation = camoufox_config.clone(); + config_for_generation.fingerprint = None; + + // Generate a new fingerprint + let new_fingerprint = self + .camoufox_manager + .generate_fingerprint_config(&app_handle, profile, &config_for_generation) + .await + .map_err(|e| format!("Failed to generate random fingerprint: {e}"))?; + + log::info!( + "New fingerprint generated, length: {} chars", + new_fingerprint.len() + ); + + // Update the config with the new fingerprint for launching + camoufox_config.fingerprint = Some(new_fingerprint.clone()); + + // Save the updated fingerprint to the profile so it persists + // We need to preserve all existing config fields and only update the fingerprint + let mut updated_camoufox_config = + updated_profile.camoufox_config.clone().unwrap_or_default(); + updated_camoufox_config.fingerprint = Some(new_fingerprint); + // Preserve the randomize flag so it persists across launches + updated_camoufox_config.randomize_fingerprint_on_launch = Some(true); + updated_profile.camoufox_config = Some(updated_camoufox_config.clone()); + + log::info!( + "Updated profile camoufox_config with new fingerprint for profile: {}, fingerprint length: {}", + profile.name, + updated_camoufox_config.fingerprint.as_ref().map(|f| f.len()).unwrap_or(0) + ); + } + // Use the nodecar camoufox launcher log::info!( "Launching Camoufox via nodecar for profile: {}", @@ -187,7 +230,12 @@ impl BrowserRunner { ); let camoufox_result = self .camoufox_manager - .launch_camoufox_profile_nodecar(app_handle.clone(), profile.clone(), camoufox_config, url) + .launch_camoufox_profile_nodecar( + app_handle.clone(), + updated_profile.clone(), + camoufox_config, + url, + ) .await .map_err(|e| -> Box { format!("Failed to launch camoufox via nodecar: {e}").into() @@ -198,7 +246,6 @@ impl BrowserRunner { log::info!("Camoufox launched successfully with PID: {process_id}"); // Update profile with the process info from camoufox result - let mut updated_profile = profile.clone(); updated_profile.process_id = Some(process_id); updated_profile.last_launch = Some(SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs()); @@ -209,17 +256,33 @@ impl BrowserRunner { log::info!("Updated proxy PID mapping from temp (0) to actual PID: {process_id}"); } - // Save the updated profile + // Save the updated profile (includes new fingerprint if randomize is enabled) + log::info!( + "Saving profile {} with camoufox_config fingerprint length: {}", + updated_profile.name, + updated_profile + .camoufox_config + .as_ref() + .and_then(|c| c.fingerprint.as_ref()) + .map(|f| f.len()) + .unwrap_or(0) + ); self.save_process_info(&updated_profile)?; // Ensure tag suggestions include any tags from this profile let _ = crate::tag_manager::TAG_MANAGER.lock().map(|tm| { let _ = tm.rebuild_from_profiles(&self.profile_manager.list_profiles().unwrap_or_default()); }); log::info!( - "Updated profile with process info: {}", + "Successfully saved profile with process info: {}", updated_profile.name ); + // Emit profiles-changed to trigger frontend to reload profiles from disk + // This ensures the UI displays the newly generated fingerprint + if let Err(e) = app_handle.emit("profiles-changed", ()) { + log::warn!("Warning: Failed to emit profiles-changed event: {e}"); + } + log::info!( "Emitting profile events for successful Camoufox launch: {}", updated_profile.name diff --git a/src-tauri/src/camoufox_manager.rs b/src-tauri/src/camoufox_manager.rs index a176425..e6fe115 100644 --- a/src-tauri/src/camoufox_manager.rs +++ b/src-tauri/src/camoufox_manager.rs @@ -22,6 +22,7 @@ pub struct CamoufoxConfig { pub block_webgl: Option, pub executable_path: Option, pub fingerprint: Option, // JSON string of the complete fingerprint config + pub randomize_fingerprint_on_launch: Option, // Generate new fingerprint on every launch } impl Default for CamoufoxConfig { @@ -38,6 +39,7 @@ impl Default for CamoufoxConfig { block_webgl: None, executable_path: None, fingerprint: None, + randomize_fingerprint_on_launch: None, } } } @@ -493,6 +495,7 @@ mod tests { assert_eq!(default_config.geoip, Some(serde_json::Value::Bool(true))); assert_eq!(default_config.proxy, None); assert_eq!(default_config.fingerprint, None); + assert_eq!(default_config.randomize_fingerprint_on_launch, None); } } diff --git a/src/components/shared-camoufox-config-form.tsx b/src/components/shared-camoufox-config-form.tsx index a2ede44..1674b85 100644 --- a/src/components/shared-camoufox-config-form.tsx +++ b/src/components/shared-camoufox-config-form.tsx @@ -25,6 +25,11 @@ interface SharedCamoufoxConfigFormProps { forceAdvanced?: boolean; // Force advanced mode (for editing) } +// Determine if fingerprint editing should be disabled +const isFingerprintEditingDisabled = (config: CamoufoxConfig): boolean => { + return config.randomize_fingerprint_on_launch === true; +}; + // Component for editing nested objects like webGl:parameters interface ObjectEditorProps { value: Record | undefined; @@ -169,648 +174,684 @@ export function SharedCamoufoxConfigForm({ } }; + const isEditingDisabled = isFingerprintEditingDisabled(config); + const renderAdvancedForm = () => (
- - - ⚠️ Warning: Only edit these parameters if you know what you're doing. - Incorrect values may break websites, make them detect you, and lead to - hard-to-debug bugs.{" "} - - - - {/* Blocking Options */} -
- -
-
- - onConfigChange("block_images", checked) - } - /> - -
-
- - onConfigChange("block_webrtc", checked) - } - /> - -
-
- - onConfigChange("block_webgl", checked) - } - /> - -
+ {/* Randomize Fingerprint Option */} +
+
+ + onConfigChange("randomize_fingerprint_on_launch", checked) + } + /> +
+

+ When enabled, a new fingerprint will be generated each time the + browser is launched. +

- {/* Navigator Properties */} -
- -
-
- - - updateFingerprintConfig( - "navigator.userAgent", - e.target.value || undefined, - ) - } - placeholder="Mozilla/5.0..." - /> -
-
- - - updateFingerprintConfig( - "navigator.platform", - e.target.value || undefined, - ) - } - placeholder="e.g., MacIntel, Win32" - /> -
-
- - - updateFingerprintConfig( - "navigator.appVersion", - e.target.value || undefined, - ) - } - placeholder="e.g., 5.0 (Macintosh)" - /> -
-
- - - updateFingerprintConfig( - "navigator.oscpu", - e.target.value || undefined, - ) - } - placeholder="e.g., Intel Mac OS X 10.15" - /> -
-
- - - updateFingerprintConfig( - "navigator.hardwareConcurrency", - e.target.value ? parseInt(e.target.value, 10) : undefined, - ) - } - placeholder="e.g., 8" - /> -
-
- - - updateFingerprintConfig( - "navigator.maxTouchPoints", - e.target.value ? parseInt(e.target.value, 10) : undefined, - ) - } - placeholder="e.g., 0" - /> -
-
- - -
-
- - - updateFingerprintConfig( - "navigator.language", - e.target.value || undefined, - ) - } - placeholder="e.g., en-US" - /> -
-
-
+ {isEditingDisabled ? ( + + + Fingerprint editing is disabled because random fingerprint + generation is enabled. Disable the option above to manually edit the + fingerprint configuration. + + + ) : ( + + + ⚠️ Warning: Only edit these parameters if you know what you're doing. + Incorrect values may break websites, make them detect you, and lead + to hard-to-debug bugs.{" "} + + + )} - {/* Screen Properties */} -
- -
-
- - - updateFingerprintConfig( - "screen.width", - e.target.value ? parseInt(e.target.value, 10) : undefined, - ) - } - placeholder="e.g., 1920" - /> -
-
- - - updateFingerprintConfig( - "screen.height", - e.target.value ? parseInt(e.target.value, 10) : undefined, - ) - } - placeholder="e.g., 1080" - /> -
-
- - - updateFingerprintConfig( - "screen.availWidth", - e.target.value ? parseInt(e.target.value, 10) : undefined, - ) - } - placeholder="e.g., 1920" - /> -
-
- - - updateFingerprintConfig( - "screen.availHeight", - e.target.value ? parseInt(e.target.value, 10) : undefined, - ) - } - placeholder="e.g., 1055" - /> -
-
- - - updateFingerprintConfig( - "screen.colorDepth", - e.target.value ? parseInt(e.target.value, 10) : undefined, - ) - } - placeholder="e.g., 30" - /> -
-
- - - updateFingerprintConfig( - "screen.pixelDepth", - e.target.value ? parseInt(e.target.value, 10) : undefined, - ) - } - placeholder="e.g., 30" - /> -
-
-
- - {/* Window Properties */} -
- -
-
- - - updateFingerprintConfig( - "window.outerWidth", - e.target.value ? parseInt(e.target.value, 10) : undefined, - ) - } - placeholder="e.g., 1512" - /> -
-
- - - updateFingerprintConfig( - "window.outerHeight", - e.target.value ? parseInt(e.target.value, 10) : undefined, - ) - } - placeholder="e.g., 886" - /> -
-
- - - updateFingerprintConfig( - "window.innerWidth", - e.target.value ? parseInt(e.target.value, 10) : undefined, - ) - } - placeholder="e.g., 1512" - /> -
-
- - - updateFingerprintConfig( - "window.innerHeight", - e.target.value ? parseInt(e.target.value, 10) : undefined, - ) - } - placeholder="e.g., 886" - /> -
-
- - - updateFingerprintConfig( - "window.screenX", - e.target.value ? parseInt(e.target.value, 10) : undefined, - ) - } - placeholder="e.g., 0" - /> -
-
- - - updateFingerprintConfig( - "window.screenY", - e.target.value ? parseInt(e.target.value, 10) : undefined, - ) - } - placeholder="e.g., 0" - /> -
-
-
- - {/* Geolocation */} -
- -
-
- - - updateFingerprintConfig( - "geolocation:latitude", - e.target.value ? parseFloat(e.target.value) : undefined, - ) - } - placeholder="e.g., 41.0019" - /> -
-
- - - updateFingerprintConfig( - "geolocation:longitude", - e.target.value ? parseFloat(e.target.value) : undefined, - ) - } - placeholder="e.g., 28.9645" - /> -
-
- - - updateFingerprintConfig("timezone", e.target.value || undefined) - } - placeholder="e.g., America/New_York" - /> -
-
-
- - {/* Locale */} -
- -
-
- - - updateFingerprintConfig( - "locale:language", - e.target.value || undefined, - ) - } - placeholder="e.g., tr" - /> -
-
- - - updateFingerprintConfig( - "locale:region", - e.target.value || undefined, - ) - } - placeholder="e.g., TR" - /> -
-
- - - updateFingerprintConfig( - "locale:script", - e.target.value || undefined, - ) - } - placeholder="e.g., Latn" - /> -
-
-
- - {/* WebGL Properties */} -
- -
-
- - - updateFingerprintConfig( - "webGl:vendor", - e.target.value || undefined, - ) - } - placeholder="e.g., Mesa" - /> -
-
- - - updateFingerprintConfig( - "webGl:renderer", - e.target.value || undefined, - ) - } - placeholder="e.g., llvmpipe, or similar" - /> -
-
-
- - {/* WebGL Parameters */} -
- ) || {} - } - onChange={(value) => - updateFingerprintConfig("webGl:parameters", value) - } - title="WebGL Parameters" - /> -
- - {/* WebGL2 Parameters */} -
- ) || {} - } - onChange={(value) => - updateFingerprintConfig("webGl2:parameters", value) - } - title="WebGL2 Parameters" - /> -
- - {/* WebGL Shader Precision Formats */} -
- ) || {} - } - onChange={(value) => - updateFingerprintConfig("webGl:shaderPrecisionFormats", value) - } - title="WebGL Shader Precision Formats" - /> -
- - {/* WebGL2 Shader Precision Formats */} -
- ) || {} - } - onChange={(value) => - updateFingerprintConfig("webGl2:shaderPrecisionFormats", value) - } - title="WebGL2 Shader Precision Formats" - /> -
- - {/* Fonts */} -
- - ({ - label: font, - value: font, - })) || [] - } - onChange={(selected: Option[]) => - updateFingerprintConfig( - "fonts", - selected.map((s: Option) => s.value), - ) - } - placeholder="Add fonts..." - creatable - /> -
- - {/* Battery */} -
- -
+
+ {/* Blocking Options */} +
+
- updateFingerprintConfig("battery:charging", checked) + onConfigChange("block_images", checked) } /> - + +
+
+ + onConfigChange("block_webrtc", checked) + } + /> + +
+
+ + onConfigChange("block_webgl", checked) + } + /> +
-
- - - updateFingerprintConfig( - "battery:chargingTime", - e.target.value ? parseFloat(e.target.value) : undefined, - ) - } - placeholder="e.g., 0" - /> -
-
- - - updateFingerprintConfig( - "battery:dischargingTime", - e.target.value ? parseFloat(e.target.value) : undefined, - ) - } - placeholder="e.g., 0" - /> +
+ + {/* Navigator Properties */} +
+ +
+
+ + + updateFingerprintConfig( + "navigator.userAgent", + e.target.value || undefined, + ) + } + placeholder="Mozilla/5.0..." + /> +
+
+ + + updateFingerprintConfig( + "navigator.platform", + e.target.value || undefined, + ) + } + placeholder="e.g., MacIntel, Win32" + /> +
+
+ + + updateFingerprintConfig( + "navigator.appVersion", + e.target.value || undefined, + ) + } + placeholder="e.g., 5.0 (Macintosh)" + /> +
+
+ + + updateFingerprintConfig( + "navigator.oscpu", + e.target.value || undefined, + ) + } + placeholder="e.g., Intel Mac OS X 10.15" + /> +
+
+ + + updateFingerprintConfig( + "navigator.hardwareConcurrency", + e.target.value ? parseInt(e.target.value, 10) : undefined, + ) + } + placeholder="e.g., 8" + /> +
+
+ + + updateFingerprintConfig( + "navigator.maxTouchPoints", + e.target.value ? parseInt(e.target.value, 10) : undefined, + ) + } + placeholder="e.g., 0" + /> +
+
+ + +
+
+ + + updateFingerprintConfig( + "navigator.language", + e.target.value || undefined, + ) + } + placeholder="e.g., en-US" + /> +
-
- {/* Browser Behavior */} - {/*
+ {/* Screen Properties */} +
+ +
+
+ + + updateFingerprintConfig( + "screen.width", + e.target.value ? parseInt(e.target.value, 10) : undefined, + ) + } + placeholder="e.g., 1920" + /> +
+
+ + + updateFingerprintConfig( + "screen.height", + e.target.value ? parseInt(e.target.value, 10) : undefined, + ) + } + placeholder="e.g., 1080" + /> +
+
+ + + updateFingerprintConfig( + "screen.availWidth", + e.target.value ? parseInt(e.target.value, 10) : undefined, + ) + } + placeholder="e.g., 1920" + /> +
+
+ + + updateFingerprintConfig( + "screen.availHeight", + e.target.value ? parseInt(e.target.value, 10) : undefined, + ) + } + placeholder="e.g., 1055" + /> +
+
+ + + updateFingerprintConfig( + "screen.colorDepth", + e.target.value ? parseInt(e.target.value, 10) : undefined, + ) + } + placeholder="e.g., 30" + /> +
+
+ + + updateFingerprintConfig( + "screen.pixelDepth", + e.target.value ? parseInt(e.target.value, 10) : undefined, + ) + } + placeholder="e.g., 30" + /> +
+
+
+ + {/* Window Properties */} +
+ +
+
+ + + updateFingerprintConfig( + "window.outerWidth", + e.target.value ? parseInt(e.target.value, 10) : undefined, + ) + } + placeholder="e.g., 1512" + /> +
+
+ + + updateFingerprintConfig( + "window.outerHeight", + e.target.value ? parseInt(e.target.value, 10) : undefined, + ) + } + placeholder="e.g., 886" + /> +
+
+ + + updateFingerprintConfig( + "window.innerWidth", + e.target.value ? parseInt(e.target.value, 10) : undefined, + ) + } + placeholder="e.g., 1512" + /> +
+
+ + + updateFingerprintConfig( + "window.innerHeight", + e.target.value ? parseInt(e.target.value, 10) : undefined, + ) + } + placeholder="e.g., 886" + /> +
+
+ + + updateFingerprintConfig( + "window.screenX", + e.target.value ? parseInt(e.target.value, 10) : undefined, + ) + } + placeholder="e.g., 0" + /> +
+
+ + + updateFingerprintConfig( + "window.screenY", + e.target.value ? parseInt(e.target.value, 10) : undefined, + ) + } + placeholder="e.g., 0" + /> +
+
+
+ + {/* Geolocation */} +
+ +
+
+ + + updateFingerprintConfig( + "geolocation:latitude", + e.target.value ? parseFloat(e.target.value) : undefined, + ) + } + placeholder="e.g., 41.0019" + /> +
+
+ + + updateFingerprintConfig( + "geolocation:longitude", + e.target.value ? parseFloat(e.target.value) : undefined, + ) + } + placeholder="e.g., 28.9645" + /> +
+
+ + + updateFingerprintConfig( + "timezone", + e.target.value || undefined, + ) + } + placeholder="e.g., America/New_York" + /> +
+
+
+ + {/* Locale */} +
+ +
+
+ + + updateFingerprintConfig( + "locale:language", + e.target.value || undefined, + ) + } + placeholder="e.g., tr" + /> +
+
+ + + updateFingerprintConfig( + "locale:region", + e.target.value || undefined, + ) + } + placeholder="e.g., TR" + /> +
+
+ + + updateFingerprintConfig( + "locale:script", + e.target.value || undefined, + ) + } + placeholder="e.g., Latn" + /> +
+
+
+ + {/* WebGL Properties */} +
+ +
+
+ + + updateFingerprintConfig( + "webGl:vendor", + e.target.value || undefined, + ) + } + placeholder="e.g., Mesa" + /> +
+
+ + + updateFingerprintConfig( + "webGl:renderer", + e.target.value || undefined, + ) + } + placeholder="e.g., llvmpipe, or similar" + /> +
+
+
+ + {/* WebGL Parameters */} +
+ ) || {} + } + onChange={(value) => + updateFingerprintConfig("webGl:parameters", value) + } + title="WebGL Parameters" + /> +
+ + {/* WebGL2 Parameters */} +
+ ) || {} + } + onChange={(value) => + updateFingerprintConfig("webGl2:parameters", value) + } + title="WebGL2 Parameters" + /> +
+ + {/* WebGL Shader Precision Formats */} +
+ ) || {} + } + onChange={(value) => + updateFingerprintConfig("webGl:shaderPrecisionFormats", value) + } + title="WebGL Shader Precision Formats" + /> +
+ + {/* WebGL2 Shader Precision Formats */} +
+ ) || {} + } + onChange={(value) => + updateFingerprintConfig("webGl2:shaderPrecisionFormats", value) + } + title="WebGL2 Shader Precision Formats" + /> +
+ + {/* Fonts */} +
+ + ({ + label: font, + value: font, + })) || [] + } + onChange={(selected: Option[]) => + updateFingerprintConfig( + "fonts", + selected.map((s: Option) => s.value), + ) + } + placeholder="Add fonts..." + creatable + /> +
+ + {/* Battery */} +
+ +
+
+
+ + updateFingerprintConfig("battery:charging", checked) + } + /> + +
+
+
+ + + updateFingerprintConfig( + "battery:chargingTime", + e.target.value ? parseFloat(e.target.value) : undefined, + ) + } + placeholder="e.g., 0" + /> +
+
+ + + updateFingerprintConfig( + "battery:dischargingTime", + e.target.value ? parseFloat(e.target.value) : undefined, + ) + } + placeholder="e.g., 0" + /> +
+
+
+ + {/* Browser Behavior */} + {/*
*/} +
); @@ -841,13 +883,38 @@ export function SharedCamoufoxConfigForm({ + {/* Randomize Fingerprint Option */} +
+
+ + onConfigChange("randomize_fingerprint_on_launch", checked) + } + /> + +
+

+ When enabled, a new fingerprint will be generated each time the + browser is launched. The generated fingerprint is saved for + reference. +

+
+ {/* Automatic Location Configuration */} -
+
{/* Screen Resolution */} -
+
@@ -929,7 +996,7 @@ export function SharedCamoufoxConfigForm({ />
-
+ diff --git a/src/types.ts b/src/types.ts index 32b86a0..352b4f2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -94,6 +94,7 @@ export interface CamoufoxConfig { block_webgl?: boolean; executable_path?: string; fingerprint?: string; // JSON string of the complete fingerprint config + randomize_fingerprint_on_launch?: boolean; // Generate new fingerprint on every launch } // Extended interface for the advanced fingerprint configuration