Skip to content

Commit

Permalink
Display preflight warnings (#20677)
Browse files Browse the repository at this point in the history
## Description 

Displays optional warnings from the dapp preflight. Adds ability to
dismiss the overlay to support the "Proceed" action. "Proceed" simply
hides the overlay and allows the user to continue their action.

|With warnings (block disabled)|With hard block|
|-|-|
|<img width="472" alt="image"
src="https://github.com/user-attachments/assets/bff4e62b-31da-4ca7-9ddb-c88ce26e0f28"
/>|<img width="472" alt="image"
src="https://github.com/user-attachments/assets/edc65027-a71f-4989-8a2e-e958960da618"
/>|


## Test plan 

How did you test the new or updated feature?

---

## Release notes

Check each box that your changes affect. If none of the boxes relate to
your changes, release notes aren't required.

For each box you select, include information after the relevant heading
that describes the impact of your changes that a user might notice and
any actions they must take to implement updates.

- [ ] Protocol: 
- [ ] Nodes (Validators and Full nodes): 
- [ ] Indexer: 
- [ ] JSON-RPC: 
- [ ] GraphQL: 
- [ ] CLI: 
- [ ] Rust SDK:
- [ ] REST API:
  • Loading branch information
tlmysten authored Dec 18, 2024
1 parent 9229371 commit 0599c49
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 38 deletions.
65 changes: 45 additions & 20 deletions apps/wallet/src/ui/app/components/known-scam-overlay/index.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,63 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { cx } from 'class-variance-authority';

import { Button } from '../../shared/ButtonUI';
import { Heading } from '../../shared/heading';
import { Portal } from '../../shared/Portal';
import type { DappPreflightResponse } from './types';
import WarningSvg from './warning.svg';

export type ScamOverlayProps = {
onDismiss(): void;
open: boolean;
title?: string;
subtitle?: string;
preflight: DappPreflightResponse;
onClickBack(): void;
onClickContinue(): void;
};

export function ScamOverlay({ open, onDismiss, title, subtitle }: ScamOverlayProps) {
if (!open) return null;
function Warning({ title, subtitle }: { title: string; subtitle: string }) {
return (
<div className="flex flex-col gap-2 text-center pb-4">
<Heading variant="heading2" weight="semibold" color="gray-90">
{title || 'Malicious website'}
</Heading>
<div className="flex text-center font-medium text-pBody text-gray-90">
<div className="font-medium text-pBody text-gray-90">
{subtitle ||
'This website has been flagged for malicious behavior. To protect your wallet from potential threats, please return to safety.'}
</div>
</div>
</div>
);
}

export function ScamOverlay({ preflight, onClickBack, onClickContinue }: ScamOverlayProps) {
const { block, warnings } = preflight;

if (!block.enabled && !warnings?.length) return null;

return (
<Portal containerId="overlay-portal-container">
<div className="h-full w-full bg-issue-light flex flex-col p-4 justify-center items-center gap-4 absolute top-0 left-0 bottom-0 z-50">
<div
className={cx(
'h-full w-full flex flex-col p-4 justify-center items-center gap-4 absolute top-0 left-0 bottom-0 z-50',
block.enabled ? 'bg-issue-light' : 'bg-warning-light',
)}
>
<WarningSvg />
<div className="flex flex-col gap-2 text-center pb-4">
<Heading variant="heading2" weight="semibold" color="gray-90">
{title || 'Malicious website'}
</Heading>
<div className="flex text-center font-medium text-pBody text-gray-90">
<div className="font-medium text-pBody text-gray-90">
{subtitle ||
'This website has been flagged for malicious behavior. To protect your wallet from potential threats, please return to safety.'}
</div>
</div>
</div>

<div className="gap-2 mt-auto w-full items-stretch">
<Button variant="primary" text="I understand" onClick={onDismiss} />
{!!block.enabled && <Warning {...preflight.block} />}

{warnings?.map(({ title, subtitle }, i) => {
// warnings list won't ever change, index key is fine
return <Warning key={i} title={title} subtitle={subtitle} />;
})}

<div className="flex flex-col gap-2 mt-auto w-full items-stretch">
<Button variant="primary" text="Return to safety" onClick={onClickBack} />
{!block.enabled && (
<Button variant="outlineWarning" text="Proceed" onClick={onClickContinue} />
)}
</div>
</div>
</Portal>
Expand Down
4 changes: 4 additions & 0 deletions apps/wallet/src/ui/app/components/known-scam-overlay/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export type DappPreflightResponse = {
title: string;
subtitle: string;
};
warnings?: {
title: string;
subtitle: string;
}[];
};

export type Network = 'mainnet' | 'testnet' | 'devnet' | 'local';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ export function useShowScamWarning({

return {
data,
isOpen: !!data?.block.enabled && !isError,
isPending,
isError,
};
Expand Down
39 changes: 22 additions & 17 deletions apps/wallet/src/ui/app/components/user-approve-container/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { ampli } from '_src/shared/analytics/ampli';
import { type PermissionType } from '_src/shared/messaging/messages/payloads/permissions';
import { Transaction } from '@mysten/sui/transactions';
import cn from 'clsx';
import { useCallback, useMemo, useState } from 'react';
import { useMemo, useState } from 'react';
import type { ReactNode } from 'react';
import { useParams } from 'react-router-dom';

Expand Down Expand Up @@ -53,14 +54,18 @@ export function UserApproveContainer({
checkAccountLock,
}: UserApproveContainerProps) {
const [submitting, setSubmitting] = useState(false);
const handleOnResponse = useCallback(
async (allowed: boolean) => {
setSubmitting(true);
await onSubmit(allowed);
setSubmitting(false);
},
[onSubmit],
);
const [scamOverlayDismissed, setScamOverlayDismissed] = useState(false);

const handleDismissScamOverlay = () => {
ampli.bypassedScamWarning({ hostname: new URL(origin).hostname });
setScamOverlayDismissed(true);
};

const handleOnResponse = async (allowed: boolean) => {
setSubmitting(true);
await onSubmit(allowed);
setSubmitting(false);
};

const { data: selectedAccount } = useAccountByAddress(address);
const parsedOrigin = useMemo(() => new URL(origin), [origin]);
Expand All @@ -82,8 +87,7 @@ export function UserApproveContainer({
const message = request && request.tx && 'message' in request.tx ? request.tx.message : undefined;

const {
data,
isOpen,
data: preflight,
isPending: isDomainCheckLoading,
isError,
} = useShowScamWarning({
Expand All @@ -99,12 +103,13 @@ export function UserApproveContainer({

return (
<>
<ScamOverlay
open={isOpen}
title={data?.block.title}
subtitle={data?.block.subtitle}
onDismiss={() => handleOnResponse(false)}
/>
{!scamOverlayDismissed && !!preflight && (
<ScamOverlay
preflight={preflight}
onClickBack={() => handleOnResponse(false)}
onClickContinue={handleDismissScamOverlay}
/>
)}
<div className="flex flex-1 flex-col flex-nowrap h-full">
<div className="flex-1 pb-0 flex flex-col">
<DAppInfoCard
Expand Down

0 comments on commit 0599c49

Please sign in to comment.