mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-10-01 15:02:31 +00:00
Fix percent-prefixed E2EE passphrase persistence
This commit is contained in:
@@ -137,7 +137,7 @@ The mobile pass uses Obsidian's `app.emulateMobile(true)`, a 390 by 844 CSS-pixe
|
||||
|
||||
The same workflow checks the two remote-activity status boundaries. It first holds a real CouchDB request at the selected fetch implementation and confirms that `🌐N` is visible while `📲` is absent. It then holds the real one-shot replication immediately before its replicator call, confirms that `📲` is visible while no physical request is active, releases it, and requires the finite and bounded activity counts to return to zero, the request and response counts to balance, and both indicators to disappear. Finally, it creates a remote-only chunk, holds the real on-demand fetch immediately before its remote call, makes the same logical active and idle assertions, and verifies that the fetched chunk is written into the local database. These gates make the active states deterministic without replacing the remote request or operation.
|
||||
|
||||
`test:e2e:obsidian:couchdb-manual-setup-workflow` follows the visible onboarding path for the first device when no Setup URI is available. It enters end-to-end encryption and CouchDB details, runs the read-only `Check server requirements` step, requires the prepared fixture to pass without applying a server fix, and lets the onboarding connection test create the named database. After Rebuild completes on the first device, it creates an ordinary note, asks that working device to generate a Setup URI for a second device, completes Fetch there, and verifies a bidirectional note round-trip. The workflow captures each decision point and the expanded server-check result; password controls remain visually masked.
|
||||
`test:e2e:obsidian:couchdb-manual-setup-workflow` follows the visible onboarding path for the first device when no Setup URI is available. It enters end-to-end encryption and CouchDB details, runs the read-only `Check server requirements` step, requires the prepared fixture to pass without applying a server fix, and lets the onboarding connection test create the named database. After Rebuild completes on the first device, it creates an ordinary note, asks that working device to generate a Setup URI for a second device, completes Fetch there, and verifies a bidirectional note round-trip. The workflow captures each decision point and the expanded server-check result; password controls remain visually masked. It uses an E2EE passphrase beginning with `%`, confirms that the saved settings do not contain it in plain text, and checks that Obsidian restores it after restarting with the first Vault.
|
||||
|
||||
If this status workflow fails while Obsidian is running, it writes a full-page screenshot and a JSON snapshot of the status text and counters under `/tmp/obsidian-livesync-e2e`. The dialogue-mount workflow leaves desktop and mobile screenshots for both representative Svelte routes, the Hidden File Sync workflow captures the successfully displayed JSON Resolve dialogue before selecting an option, and the Security Seed reconnect workflow captures each significant application state. The suite therefore records representative evidence without capturing every interaction. Set `E2E_OBSIDIAN_DIAGNOSTICS_DIR` to use another directory.
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ process.env.E2E_OBSIDIAN_CLI_TIMEOUT_MS ??= "90000";
|
||||
process.env.E2E_OBSIDIAN_COUCHDB_TIMEOUT_MS ??= "30000";
|
||||
|
||||
const uiTimeoutMs = Number(process.env.E2E_OBSIDIAN_SETUP_URI_TIMEOUT_MS ?? 30000);
|
||||
const e2eePassphrase = `%${randomBytes(24).toString("base64url")}`;
|
||||
const notePath = "E2E/manual-couchdb/from-first-device.md";
|
||||
const noteContent = "# Manual CouchDB setup\n\nThis note was sent by the manually configured first device.\n";
|
||||
const returnNotePath = "E2E/manual-couchdb/from-second-device.md";
|
||||
@@ -147,8 +148,7 @@ async function enterManualCouchDBSettings(port: number, couchDb: CouchDbConfig,
|
||||
.locator('input[type="checkbox"]')
|
||||
.first()
|
||||
.check({ timeout: uiTimeoutMs });
|
||||
const passphraseValue = randomBytes(24).toString("base64url");
|
||||
await passphraseInput.fill(passphraseValue);
|
||||
await passphraseInput.fill(e2eePassphrase);
|
||||
const passwordToggle = encryption.locator("button.sls-password-toggle");
|
||||
await passwordToggle.click({ timeout: uiTimeoutMs });
|
||||
assertEqual(
|
||||
@@ -158,7 +158,7 @@ async function enterManualCouchDBSettings(port: number, couchDb: CouchDbConfig,
|
||||
);
|
||||
assertEqual(
|
||||
await passphraseInput.inputValue(),
|
||||
passphraseValue,
|
||||
e2eePassphrase,
|
||||
"Toggling visibility changed the passphrase value."
|
||||
);
|
||||
await passwordToggle.click({ timeout: uiTimeoutMs });
|
||||
@@ -169,7 +169,7 @@ async function enterManualCouchDBSettings(port: number, couchDb: CouchDbConfig,
|
||||
);
|
||||
assertEqual(
|
||||
await passphraseInput.inputValue(),
|
||||
passphraseValue,
|
||||
e2eePassphrase,
|
||||
"Re-masking the passphrase changed its value."
|
||||
);
|
||||
});
|
||||
@@ -301,6 +301,23 @@ async function assertPersistedE2EE(vault: TemporaryVault): Promise<void> {
|
||||
if (typeof persisted.encryptedPassphrase !== "string" || persisted.encryptedPassphrase.length === 0) {
|
||||
throw new Error("Manual CouchDB setup did not persist an encrypted E2EE passphrase.");
|
||||
}
|
||||
if (JSON.stringify(persisted).includes(e2eePassphrase)) {
|
||||
throw new Error("Manual CouchDB setup persisted the E2EE passphrase in plain text.");
|
||||
}
|
||||
}
|
||||
|
||||
async function assertRestoredE2EEPassphrase(session: ObsidianLiveSyncSession, cliBinary: string): Promise<void> {
|
||||
const restored = await evalObsidianJson<boolean>(
|
||||
cliBinary,
|
||||
[
|
||||
"(()=>{",
|
||||
"const settings=app.plugins.plugins['obsidian-livesync'].core.services.setting.currentSettings();",
|
||||
`return JSON.stringify(settings.passphrase === ${JSON.stringify(e2eePassphrase)});`,
|
||||
"})()",
|
||||
].join(""),
|
||||
session.cliEnv
|
||||
);
|
||||
assertEqual(restored, true, "The E2EE passphrase was not restored after Obsidian restarted.");
|
||||
}
|
||||
|
||||
async function setRemotePreferredE2EEDisabled(context: RunnerContext): Promise<void> {
|
||||
@@ -454,6 +471,7 @@ async function main(): Promise<void> {
|
||||
|
||||
session = await startUnconfiguredSession(context, vaultA);
|
||||
try {
|
||||
await assertRestoredE2EEPassphrase(session, context.cliBinary);
|
||||
await scheduleRemoteOverwrite(session.remoteDebuggingPort);
|
||||
screenshots.push(await confirmRebuild(session.remoteDebuggingPort, e2eeRebuildCaptures));
|
||||
screenshots.push(
|
||||
|
||||
Reference in New Issue
Block a user