Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/app/screens/Nostr/ConfirmSignMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ function ConfirmSignMessage() {
const navigate = useNavigate();

const event = navState.args?.event as Event;
const hasKind3Warning = navState.args?.hasKind3Warning as boolean;
const origin = navState.origin as OriginData;
const [loading, setLoading] = useState(false);
const [successMessage, setSuccessMessage] = useState("");
Expand Down Expand Up @@ -128,6 +129,33 @@ function ConfirmSignMessage() {
}
content={content}
/>
{hasKind3Warning && (
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mb-4 dark:bg-yellow-900/20 dark:border-yellow-800">
<div className="flex">
<div className="flex-shrink-0">
<svg
className="h-5 w-5 text-yellow-400"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="evenodd"
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
clipRule="evenodd"
/>
</svg>
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-yellow-800 dark:text-yellow-200">
{t("follow_list_warning_title")}
</h3>
<div className="mt-2 text-sm text-yellow-700 dark:text-yellow-300">
<p>{t("follow_list_warning_description")}</p>
</div>
</div>
</div>
</div>
)}
<div
className="flex justify-center items-center mb-4 text-gray-400 dark:text-neutral-600 hover:text-gray-600 dark:hover:text-neutral-400 text-sm cursor-pointer"
onClick={toggleShowJSON}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,82 @@ describe("signEvent", () => {
});
});

describe("kind 3 warning functionality", () => {
test("shows warning for kind 3 event with fewer than 2 p-tags even with existing permissions", async () => {
// prepare DB with permission for kind 3
const kind3Permission = {
...permissionInDB,
method: "nostr/signMessage/3",
};
await db.permissions.bulkAdd([kind3Permission]);

const kind3Message: MessageSignEvent = {
...message,
args: {
event: {
content: "",
created_at: 1714716414,
kind: 3,
tags: [["p", "some-short-pubkey"]], // Only 1 p-tag
},
},
};

await signEvent(kind3Message, sender);

// Should prompt even with existing permission
expect(utils.openPrompt).toHaveBeenCalledWith({
args: {
event: kind3Message.args.event,
hasKind3Warning: true,
},
origin: kind3Message.origin,
action: "public/nostr/confirmSignMessage",
});
});

test("does not show warning for kind 3 event with 2 or more p-tags", async () => {
// prepare DB with permission for kind 3
const kind3Permission = {
...permissionInDB,
method: "nostr/signMessage/3",
};
await db.permissions.bulkAdd([kind3Permission]);

const kind3Message: MessageSignEvent = {
...message,
args: {
event: {
content: "",
created_at: 1714716414,
kind: 3,
tags: [
["p", "pubkey1"],
["p", "pubkey2"],
], // 2 p-tags
},
},
};

const result = await signEvent(kind3Message, sender);

// Should not prompt, permission exists and no warning needed
expect(utils.openPrompt).not.toHaveBeenCalled();
expect(result).toStrictEqual(requestResponse);
});

test("does not show warning for non-kind 3 events", async () => {
// prepare DB with permission for kind 1
await db.permissions.bulkAdd([permissionInDB]);

const result = await signEvent(message, sender);

// Should not prompt, permission exists and it's not kind 3
expect(utils.openPrompt).not.toHaveBeenCalled();
expect(result).toStrictEqual(requestResponse);
});
});

describe("directly calls signEvent with method and params", () => {
test("if permission for signEvent exists and is enabled", async () => {
// prepare DB with matching permission
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ import {
} from "~/common/constants";
import state from "../../state";
import { validateEvent } from "./helpers";
import { EventKind } from "~/extension/providers/nostr/types";

// Helper function to check if a kind 3 event has insufficient p-tags
function hasInsufficientPTags(event: any): boolean {
if (event.kind !== EventKind.Contacts) return false;

const pTags = event.tags?.filter((tag: string[]) => tag[0] === 'p') || [];
return pTags.length < 2;
}

const signEventOrPrompt = async (message: MessageSignEvent, sender: Sender) => {
const host = getHostFromSender(sender);
Expand Down Expand Up @@ -55,7 +64,10 @@ const signEventOrPrompt = async (message: MessageSignEvent, sender: Sender) => {
return { denied: true };
}

if (hasPermission) {
// Always warn for kind 3 events with insufficient p-tags, even if permission exists
const needsKind3Warning = hasInsufficientPTags(event);

if (hasPermission && !needsKind3Warning) {
return signEvent();
} else {
const promptResponse = await utils.openPrompt<{
Expand All @@ -65,6 +77,10 @@ const signEventOrPrompt = async (message: MessageSignEvent, sender: Sender) => {
}>({
...message,
action: "public/nostr/confirmSignMessage",
args: {
...message.args,
hasKind3Warning: needsKind3Warning,
}
});

// add permission to db only if user decided to always allow this request
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,8 @@
"block_and_ignore": "Block and ignore {{host}}",
"block_added": "Added {{host}} to the blocklist, please reload the website.",
"recipient": "Recipient",
"follow_list_warning_title": "Follow List Warning",
"follow_list_warning_description": "You're about to sign a follow list with fewer than 2 follows. This could overwrite your entire follow list. Please double-check that this is intended.",
"kinds": {
"unknown": {
"title": "kind {{kind}}",
Expand Down