Skip to content
This repository was archived by the owner on Oct 22, 2024. It is now read-only.

Commit 76b8950

Browse files
committed
DecryptionFailureBody: better error messages
Improve the error messages shown for messages from insecure devices.
1 parent bff059a commit 76b8950

File tree

5 files changed

+98
-2
lines changed

5 files changed

+98
-2
lines changed

res/css/views/messages/_DecryptionFailureBody.pcss

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,24 @@ Please see LICENSE files in the repository root for full details.
1010
color: $secondary-content;
1111
font-style: italic;
1212
}
13+
14+
/* Formatting for the "Verified identity has changed" error */
15+
.mx_DecryptionFailureVerifiedIdentityChanged > span {
16+
/* Show it in red */
17+
color: $e2e-warning-color;
18+
/* TODO: background colour? */
19+
20+
/* With a red border */
21+
border: 1px solid $e2e-warning-color;
22+
border-radius: $font-16px;
23+
24+
/* Some space inside the border */
25+
padding: 4px 12px 4px 8px;
26+
27+
/* some space between the (!) icon and text */
28+
display: inline-flex;
29+
gap: 8px;
30+
31+
/* Center vertically */
32+
align-items: center;
33+
}

src/components/views/messages/DecryptionFailureBody.tsx

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
66
Please see LICENSE files in the repository root for full details.
77
*/
88

9+
import classNames from "classnames";
910
import React, { forwardRef, ForwardRefExoticComponent, useContext } from "react";
1011
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
1112
import { DecryptionFailureCode } from "matrix-js-sdk/src/crypto-api";
1213

1314
import { _t } from "../../../languageHandler";
1415
import { IBodyProps } from "./IBodyProps";
1516
import { LocalDeviceVerificationStateContext } from "../../../contexts/LocalDeviceVerificationStateContext";
17+
import { Icon as WarningBadgeIcon } from "../../../../res/img/compound/error-16px.svg";
1618

17-
function getErrorMessage(mxEvent: MatrixEvent, isVerified: boolean | undefined): string {
19+
function getErrorMessage(mxEvent: MatrixEvent, isVerified: boolean | undefined): string | React.JSX.Element {
1820
switch (mxEvent.decryptionFailureReason) {
1921
case DecryptionFailureCode.MEGOLM_KEY_WITHHELD_FOR_UNVERIFIED_DEVICE:
2022
return _t("timeline|decryption_failure|blocked");
@@ -32,16 +34,44 @@ function getErrorMessage(mxEvent: MatrixEvent, isVerified: boolean | undefined):
3234
break;
3335

3436
case DecryptionFailureCode.HISTORICAL_MESSAGE_USER_NOT_JOINED:
37+
// TODO: event should be hidden instead of showing this error.
38+
// To be revisited as part of https://github.com/element-hq/element-meta/issues/2449
3539
return _t("timeline|decryption_failure|historical_event_user_not_joined");
40+
41+
case DecryptionFailureCode.SENDER_IDENTITY_PREVIOUSLY_VERIFIED:
42+
return (
43+
<span>
44+
<WarningBadgeIcon className="mx_Icon mx_Icon_16" />
45+
{_t("timeline|decryption_failure|sender_identity_previously_verified")}
46+
</span>
47+
);
48+
49+
case DecryptionFailureCode.UNSIGNED_SENDER_DEVICE:
50+
// TODO: event should be hidden instead of showing this error.
51+
// To be revisited as part of https://github.com/element-hq/element-meta/issues/2449
52+
return _t("timeline|decryption_failure|sender_unsigned_device");
3653
}
3754
return _t("timeline|decryption_failure|unable_to_decrypt");
3855
}
3956

57+
/** Get an extra CSS class, specific to the decryption failure reason */
58+
function errorClassName(mxEvent: MatrixEvent): string | null {
59+
switch (mxEvent.decryptionFailureReason) {
60+
case DecryptionFailureCode.SENDER_IDENTITY_PREVIOUSLY_VERIFIED:
61+
return "mx_DecryptionFailureVerifiedIdentityChanged";
62+
63+
default:
64+
return null;
65+
}
66+
}
67+
4068
// A placeholder element for messages that could not be decrypted
4169
export const DecryptionFailureBody = forwardRef<HTMLDivElement, IBodyProps>(({ mxEvent }, ref): React.JSX.Element => {
4270
const verificationState = useContext(LocalDeviceVerificationStateContext);
71+
const classes = classNames("mx_DecryptionFailureBody", "mx_EventTile_content", errorClassName(mxEvent));
72+
4373
return (
44-
<div className="mx_DecryptionFailureBody mx_EventTile_content" ref={ref}>
74+
<div className={classes} ref={ref}>
4575
{getErrorMessage(mxEvent, verificationState)}
4676
</div>
4777
);

src/i18n/strings/en_EN.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3303,6 +3303,8 @@
33033303
"historical_event_no_key_backup": "Historical messages are not available on this device",
33043304
"historical_event_unverified_device": "You need to verify this device for access to historical messages",
33053305
"historical_event_user_not_joined": "You don't have access to this message",
3306+
"sender_identity_previously_verified": "Verified identity has changed",
3307+
"sender_unsigned_device": "Encrypted by a device not verified by its owner.",
33063308
"unable_to_decrypt": "Unable to decrypt message"
33073309
},
33083310
"disambiguated_profile": "%(displayName)s (%(matrixId)s)",

test/components/views/messages/DecryptionFailureBody-test.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,32 @@ describe("DecryptionFailureBody", () => {
103103
// Then
104104
expect(container).toHaveTextContent("You don't have access to this message");
105105
});
106+
107+
it("should handle messages from users who change identities after verification", async () => {
108+
// When
109+
const event = await mkDecryptionFailureMatrixEvent({
110+
code: DecryptionFailureCode.SENDER_IDENTITY_PREVIOUSLY_VERIFIED,
111+
msg: "User previously verified",
112+
roomId: "fakeroom",
113+
sender: "fakesender",
114+
});
115+
const { container } = customRender(event);
116+
117+
// Then
118+
expect(container).toMatchSnapshot();
119+
});
120+
121+
it("should handle messages from unverified devices", async () => {
122+
// When
123+
const event = await mkDecryptionFailureMatrixEvent({
124+
code: DecryptionFailureCode.UNSIGNED_SENDER_DEVICE,
125+
msg: "Unsigned device",
126+
roomId: "fakeroom",
127+
sender: "fakesender",
128+
});
129+
const { container } = customRender(event);
130+
131+
// Then
132+
expect(container).toHaveTextContent("Encrypted by a device not verified by its owner");
133+
});
106134
});

test/components/views/messages/__snapshots__/DecryptionFailureBody-test.tsx.snap

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,18 @@ exports[`DecryptionFailureBody Should display "Unable to decrypt message" 1`] =
1919
</div>
2020
</div>
2121
`;
22+
23+
exports[`DecryptionFailureBody should handle messages from users who change identities after verification 1`] = `
24+
<div>
25+
<div
26+
class="mx_DecryptionFailureBody mx_EventTile_content mx_DecryptionFailureVerifiedIdentityChanged"
27+
>
28+
<span>
29+
<div
30+
class="mx_Icon mx_Icon_16"
31+
/>
32+
Verified identity has changed
33+
</span>
34+
</div>
35+
</div>
36+
`;

0 commit comments

Comments
 (0)