mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-10-19 14:07:19 +02:00
fix: prevent lock of old profiles
This commit is contained in:
1 parent
bf2e2e0ba7
commit
5aca43c0b3
7 files changed
+138
-11
No files matched your search
Generated
+3
-3
@@ -1720,7 +1720,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "donutbrowser"
|
||||
version = "0.31.1"
|
||||
version = "0.31.2"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"aes-gcm",
|
||||
@@ -5029,9 +5029,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "process-wrap"
|
||||
version = "9.1.0"
|
||||
version = "9.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e842efad9119158434d193c6682e2ebee4b44d6ad801d7b349623b3f57cdf55"
|
||||
checksum = "f95f6259ebd0640685b7cd26d635861f39208df0f89b2b61ff5cf76310fa54f6"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"indexmap 2.14.2",
|
||||
|
||||
@@ -1521,3 +1521,106 @@ test("an interactive launch continues the last session once the identity travels
|
||||
await app.close();
|
||||
}
|
||||
});
|
||||
|
||||
test("a payload profile an older version stored launches and is stored as an identity", async () => {
|
||||
assert.ok(process.env.WAYFERN_TEST_TOKEN, "WAYFERN_TEST_TOKEN is required");
|
||||
const localWayfernVersion = cachedFixtureVersion(
|
||||
process.env.DONUT_E2E_PROJECT_ROOT,
|
||||
);
|
||||
const app = appFromEnvironment("browser-legacy-payload", {
|
||||
seedVersionCache: localWayfernVersion ?? false,
|
||||
wayfernTermsAccepted: false,
|
||||
});
|
||||
let browserPid;
|
||||
try {
|
||||
const prepared = await prepareWayfern(
|
||||
app,
|
||||
process.env.DONUT_E2E_PROJECT_ROOT,
|
||||
);
|
||||
if (!app.session) await app.start();
|
||||
await app.invoke("accept_wayfern_terms");
|
||||
const major = Number.parseInt(prepared.version.split(".")[0], 10);
|
||||
if (major < 152) {
|
||||
console.log(
|
||||
`[donut-e2e] Wayfern ${prepared.version} converts no stored payload; skipping the legacy launch`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const profile = await createRealProfile(
|
||||
app,
|
||||
prepared.version,
|
||||
"Stored By 0.30",
|
||||
);
|
||||
|
||||
// The file as v0.30 wrote it: the whole device payload, no identity, and
|
||||
// none of the fields added since. Its raw JSON never equals the profile it
|
||||
// parses to, and comparing the two raw once refused every launch of such
|
||||
// a profile as WAYFERN_BROWSER_BUSY.
|
||||
const metadataFile = path.join(
|
||||
app.dataRoot,
|
||||
"data",
|
||||
"profiles",
|
||||
profile.id,
|
||||
"metadata.json",
|
||||
);
|
||||
const stored = JSON.parse(await readFile(metadataFile, "utf8"));
|
||||
// Set, so listing the profiles never rewrites the file in the new shape.
|
||||
assert.equal(typeof stored.host_os, "string");
|
||||
delete stored.temporary;
|
||||
for (const key of [
|
||||
"webrtc_mode",
|
||||
"persona",
|
||||
"camera_file",
|
||||
"camera_crop",
|
||||
"restore_session",
|
||||
"identity_overrides",
|
||||
"location",
|
||||
]) {
|
||||
delete stored.wayfern_config[key];
|
||||
}
|
||||
stored.wayfern_config = JSON.parse(
|
||||
await readFile(process.env.DONUT_E2E_LEGACY_CONFIG, "utf8"),
|
||||
);
|
||||
await writeFile(metadataFile, JSON.stringify(stored, null, 2));
|
||||
|
||||
const legacy = (await app.invoke("list_browser_profiles")).find(
|
||||
(p) => p.id === profile.id,
|
||||
);
|
||||
assert.equal(legacy.wayfern_config.identity_id, null);
|
||||
const launched = await app.invoke("launch_browser_profile", {
|
||||
profile: legacy,
|
||||
});
|
||||
assert.ok(launched.process_id);
|
||||
browserPid = launched.process_id;
|
||||
|
||||
const converted = (await app.invoke("list_browser_profiles")).find(
|
||||
(p) => p.id === profile.id,
|
||||
);
|
||||
assert.equal(
|
||||
typeof converted.wayfern_config.identity_id,
|
||||
"string",
|
||||
"the launch must store the payload profile as an identity",
|
||||
);
|
||||
assert.equal(
|
||||
converted.wayfern_config.fingerprint,
|
||||
undefined,
|
||||
"a converted profile must store no device payload",
|
||||
);
|
||||
await app.invoke("kill_browser_profile", { profile: converted });
|
||||
await waitForProcessExit(app, browserPid);
|
||||
await app.invoke("delete_profile", { profileId: profile.id });
|
||||
} catch (error) {
|
||||
await app.capture("failure");
|
||||
throw error;
|
||||
} finally {
|
||||
if (app.session && browserPid && processExists(browserPid)) {
|
||||
const profile = (
|
||||
await app.invoke("list_browser_profiles").catch(() => [])
|
||||
).find((item) => item.process_id === browserPid);
|
||||
if (profile)
|
||||
await app.invoke("kill_browser_profile", { profile }).catch(() => {});
|
||||
}
|
||||
await app.close();
|
||||
}
|
||||
});
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"name": "donutbrowser",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0",
|
||||
"version": "0.31.1",
|
||||
"version": "0.31.2",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"predev": "pnpm licenses:generate",
|
||||
|
||||
Generated
+1
-1
@@ -1738,7 +1738,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "donutbrowser"
|
||||
version = "0.31.1"
|
||||
version = "0.31.2"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"aes-gcm",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "donutbrowser"
|
||||
version = "0.31.1"
|
||||
version = "0.31.2"
|
||||
description = "Simple Yet Powerful Anti-Detect Browser"
|
||||
authors = ["zhom@github"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -31,6 +31,15 @@ fn atomic_write_locked(path: &Path, data: &[u8]) -> std::io::Result<()> {
|
||||
fs::rename(&tmp, path)
|
||||
}
|
||||
|
||||
/// Whether a metadata.json still holds `expected`, compared as this version
|
||||
/// reads it. A file an older version wrote lacks every field added since, so
|
||||
/// its raw JSON never equals the profile it parses to, and a raw compare
|
||||
/// reports a change on every call.
|
||||
fn holds_profile(content: &str, expected: &BrowserProfile) -> Result<bool, serde_json::Error> {
|
||||
let stored: BrowserProfile = serde_json::from_str(content)?;
|
||||
Ok(serde_json::to_value(&stored)? == serde_json::to_value(expected)?)
|
||||
}
|
||||
|
||||
/// Collapse an empty proxy/VPN id to `None`.
|
||||
///
|
||||
/// REST and MCP clients send `""` to detach a proxy or VPN, since omitting the
|
||||
@@ -484,12 +493,9 @@ impl ProfileManager {
|
||||
.join(profile.id.to_string())
|
||||
.join("metadata.json");
|
||||
let json = serde_json::to_string_pretty(profile)?;
|
||||
let expected_json = serde_json::to_string_pretty(expected)?;
|
||||
let _serialized = METADATA_WRITES.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let current = fs::read_to_string(&profile_file)?;
|
||||
if serde_json::from_str::<serde_json::Value>(¤t)?
|
||||
!= serde_json::from_str::<serde_json::Value>(&expected_json)?
|
||||
{
|
||||
if !holds_profile(¤t, expected)? {
|
||||
return Ok(false);
|
||||
}
|
||||
atomic_write_locked(&profile_file, json.as_bytes())?;
|
||||
@@ -2087,6 +2093,24 @@ mod tests {
|
||||
assert!(merged.identity_baseline.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_file_an_older_version_wrote_holds_the_profile_it_reads_as() {
|
||||
// No `temporary` and none of the newer Wayfern fields, as v0.30 wrote it.
|
||||
let legacy = r#"{
|
||||
"id": "7b0f7c9e-3f55-4c1b-9d7e-0f2f1f0c5a11",
|
||||
"name": "Old",
|
||||
"browser": "wayfern",
|
||||
"version": "152.0.7977.64",
|
||||
"wayfern_config": { "fingerprint": "{}", "os": "macos", "identity_id": null }
|
||||
}"#;
|
||||
let expected: BrowserProfile = serde_json::from_str(legacy).unwrap();
|
||||
assert!(holds_profile(legacy, &expected).unwrap());
|
||||
|
||||
let mut changed = expected.clone();
|
||||
changed.wayfern_config.as_mut().unwrap().identity_id = Some("id-1".to_string());
|
||||
assert!(!holds_profile(legacy, &changed).unwrap());
|
||||
}
|
||||
|
||||
fn create_test_profile_manager() -> (&'static ProfileManager, TempDir) {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "Donut",
|
||||
"version": "0.31.1",
|
||||
"version": "0.31.2",
|
||||
"identifier": "com.donutbrowser",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm copy-proxy-binary && pnpm dev",
|
||||
|
||||
Reference in new issue
Block a user