Require explicit opt-in for unencrypted content with E2EE

This commit is contained in:
vorotamoroz committed 2026-11-11 11:18:50 +00:00
1 parent 79ffe8f6b0
commit 8451102176
26 files changed
+1614 -532

No files matched your search

@@ -4,6 +4,7 @@
import Decision from "@/modules/services/LiveSyncUI/components/Decision.svelte";
import Question from "@/modules/services/LiveSyncUI/components/Question.svelte";
import Option from "@/modules/services/LiveSyncUI/components/Option.svelte";
import PlaintextReceiptWarning from "./PlaintextReceiptWarning.svelte";
import Instruction from "@/modules/services/LiveSyncUI/components/Instruction.svelte";
import UserDecisions from "@/modules/services/LiveSyncUI/components/UserDecisions.svelte";
import InfoNote from "@/modules/services/LiveSyncUI/components/InfoNote.svelte";
@@ -13,13 +14,16 @@
TYPE_EXISTING,
TYPE_NEW,
TYPE_COMPATIBLE_EXISTING,
type OutroApplyInitialData,
} from "./setupDialogTypes";
import { $msg as translateMessage } from "@/common/translation";
type Props = {
setResult: (result: OutroAskUserModeResultType) => void;
getInitialData?: () => OutroApplyInitialData | undefined;
};
const { setResult }: Props = $props();
const { setResult, getInitialData }: Props = $props();
const enableUnencryptedContent = $derived(getInitialData?.()?.enableUnencryptedContent === true);
let userType = $state<OutroAskUserModeResultType>(TYPE_CANCELLED);
const canProceed = $derived.by(() => {
return userType === TYPE_EXISTING || userType === TYPE_NEW || userType === TYPE_COMPATIBLE_EXISTING;
@@ -79,6 +83,9 @@
"Unless you are certain, selecting this options is bit dangerous. It assumes that the server configuration is compatible with this device. If this is not the case, data loss may occur. Please ensure you know what you are doing."
)}
</InfoNote>
{#if enableUnencryptedContent && userType === TYPE_COMPATIBLE_EXISTING}
<PlaintextReceiptWarning />
{/if}
</Option>
</Instruction>
<UserDecisions>
@@ -5,15 +5,22 @@
import Question from "@/modules/services/LiveSyncUI/components/Question.svelte";
import Instruction from "@/modules/services/LiveSyncUI/components/Instruction.svelte";
import UserDecisions from "@/modules/services/LiveSyncUI/components/UserDecisions.svelte";
import PlaintextReceiptWarning from "./PlaintextReceiptWarning.svelte";
import { $msg as translateMessage } from "@/common/translation";
import { TYPE_CANCELLED, TYPE_APPLY, type OutroExistingUserResultType } from "./setupDialogTypes";
import {
TYPE_CANCELLED,
TYPE_APPLY,
type OutroApplyInitialData,
type OutroExistingUserResultType,
} from "./setupDialogTypes";
type Props = {
setResult: (result: OutroExistingUserResultType) => void;
getInitialData?: () => { isP2P?: boolean } | undefined;
getInitialData?: () => OutroApplyInitialData | undefined;
};
const { setResult, getInitialData }: Props = $props();
const isP2P = $derived(getInitialData?.()?.isP2P === true);
const enableUnencryptedContent = $derived(getInitialData?.()?.enableUnencryptedContent === true);
</script>
{#if isP2P}
@@ -37,6 +44,9 @@
{translateMessage("Restart this device, then choose the source device when P2P Rebuild opens.")}
</Question>
</Instruction>
{#if enableUnencryptedContent}
<PlaintextReceiptWarning />
{/if}
<UserDecisions>
<Decision
title={translateMessage("Restart and Select Source Device")}
@@ -71,6 +81,9 @@
)}</Question
>
</Instruction>
{#if enableUnencryptedContent}
<PlaintextReceiptWarning />
{/if}
<UserDecisions>
<Decision
title={translateMessage("Restart and Fetch Data")}
@@ -4,16 +4,23 @@
import Decision from "@/modules/services/LiveSyncUI/components/Decision.svelte";
import Question from "@/modules/services/LiveSyncUI/components/Question.svelte";
import Instruction from "@/modules/services/LiveSyncUI/components/Instruction.svelte";
import PlaintextReceiptWarning from "./PlaintextReceiptWarning.svelte";
import UserDecisions from "@/modules/services/LiveSyncUI/components/UserDecisions.svelte";
import { $msg as msg } from "@/common/translation";
import { TYPE_APPLY, TYPE_CANCELLED, type OutroNewUserResultType } from "./setupDialogTypes";
import {
TYPE_APPLY,
TYPE_CANCELLED,
type OutroApplyInitialData,
type OutroNewUserResultType,
} from "./setupDialogTypes";
type Props = {
setResult: (result: OutroNewUserResultType) => void;
getInitialData?: () => { isP2P?: boolean } | undefined;
getInitialData?: () => OutroApplyInitialData | undefined;
};
const { setResult, getInitialData }: Props = $props();
const isP2P = $derived(getInitialData?.()?.isP2P === true);
const enableUnencryptedContent = $derived(getInitialData?.()?.enableUnencryptedContent === true);
// let userType = $state<OutroNewUserResultType>(TYPE_CANCELLED);
</script>
@@ -26,6 +33,9 @@
<br />
{msg("Ui.SetupWizard.OutroNewP2PUser.GuidanceNotice")}
</p>
{#if enableUnencryptedContent}
<PlaintextReceiptWarning />
{/if}
</Guidance>
<Instruction>
<Question>{msg("Ui.SetupWizard.OutroNewP2PUser.Question")}</Question>
@@ -55,6 +65,9 @@
"After restarting, the data on this device will be uploaded to the server as the 'master copy'. Please be aware that any unintended data currently on the server will be completely overwritten."
)}
</p>
{#if enableUnencryptedContent}
<PlaintextReceiptWarning />
{/if}
</Guidance>
<Instruction>
<Question>{msg("Please select the button below to restart and proceed to the final confirmation.")}</Question>
@@ -0,0 +1,10 @@
<script lang="ts">
import InfoNote from "@/modules/services/LiveSyncUI/components/InfoNote.svelte";
import { $msg as translateMessage } from "@/common/translation";
</script>
<InfoNote warning>
{translateMessage(
"Accepting unencrypted content will be enabled. Third-party tools can add content without the E2EE passphrase. Anyone with database write access can inject plaintext. This affects received content only; content sent by this plug-in remains encrypted."
)}
</InfoNote>
@@ -40,6 +40,7 @@
E2EEAlgorithm: DEFAULT_SETTINGS.E2EEAlgorithm,
usePathObfuscation: true,
encryptInternalMetadata: true,
acceptUnencryptedContent: DEFAULT_SETTINGS.acceptUnencryptedContent,
idDerivationVersion: 0,
idDerivationKey: "",
};
@@ -392,6 +393,18 @@
"Changing the encryption algorithm will prevent access to any data previously encrypted with a different algorithm. Ensure that all your devices are configured to use the same algorithm to maintain access to your data."
)}
</InfoNote>
<InputRow label={translateMessage("Accept unencrypted content")}>
<input
type="checkbox"
bind:checked={encryptionSettings.acceptUnencryptedContent}
disabled={!encryptionSettings.encrypt}
/>
</InputRow>
<InfoNote warning>
{translateMessage(
"With this option enabled, third-party tools can add content without the E2EE passphrase. Anyone with database write access can inject plaintext. This affects received content only; content sent by this plug-in remains encrypted."
)}
</InfoNote>
</ExtraItems>
<InfoNote warning>
@@ -85,6 +85,10 @@ export type OutroAskUserModeResultType =
| typeof TYPE_CANCELLED;
export type OutroExistingUserResultType = typeof TYPE_APPLY | typeof TYPE_CANCELLED;
export type OutroApplyInitialData = {
isP2P: boolean;
enableUnencryptedContent: boolean;
};
export type OutroNewUserResultType = typeof TYPE_APPLY | typeof TYPE_CANCELLED;