Files
plebeian-signer/projects/chrome/src/prompt.ts
mleku abd4a21f8f Release v1.0.2 - Fix Buffer polyfill race condition in prompt
- Fix race condition where permission prompts failed on first request
  due to Buffer polyfill not being initialized during module evaluation
- Replace Buffer.from() with native browser APIs (atob + TextDecoder)
  in prompt.ts for reliable base64 decoding
- Add debug logging to reckless mode approval checks
- Update permission encryption to support v2 vault key format
- Enhance LoggerService with warn/error/debug methods and log storage
- Add logs component for viewing extension activity
- Simplify deriving modal component
- Rename icon files from gooti to plebian-signer
- Update permissions component with improved styling

Files modified:
- projects/chrome/src/prompt.ts
- projects/firefox/src/prompt.ts
- projects/*/src/background-common.ts
- projects/common/src/lib/services/logger/logger.service.ts
- projects/*/src/app/components/home/logs/ (new)
- projects/*/public/*.svg, *.png (renamed)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 08:52:44 +01:00

227 lines
6.7 KiB
TypeScript

import browser from 'webextension-polyfill';
import { Nip07Method } from '@common';
import { PromptResponse, PromptResponseMessage } from './background-common';
/**
* Decode base64 string to UTF-8 using native browser APIs.
* This avoids race conditions with the Buffer polyfill initialization.
*/
function base64ToUtf8(base64: string): string {
const binaryString = atob(base64);
const bytes = Uint8Array.from(binaryString, char => char.charCodeAt(0));
return new TextDecoder('utf-8').decode(bytes);
}
const params = new URLSearchParams(location.search);
const id = params.get('id') as string;
const method = params.get('method') as Nip07Method;
const host = params.get('host') as string;
const nick = params.get('nick') as string;
let event = '{}';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let eventParsed: any = {};
try {
event = base64ToUtf8(params.get('event') as string);
eventParsed = JSON.parse(event);
} catch (e) {
console.error('Failed to parse event:', e);
}
let title = '';
switch (method) {
case 'getPublicKey':
title = 'Get Public Key';
break;
case 'signEvent':
title = 'Sign Event';
break;
case 'nip04.encrypt':
title = 'Encrypt';
break;
case 'nip44.encrypt':
title = 'Encrypt';
break;
case 'nip04.decrypt':
title = 'Decrypt';
break;
case 'nip44.decrypt':
title = 'Decrypt';
break;
case 'getRelays':
title = 'Get Relays';
break;
default:
break;
}
const titleSpanElement = document.getElementById('titleSpan');
if (titleSpanElement) {
titleSpanElement.innerText = title;
}
Array.from(document.getElementsByClassName('nick-INSERT')).forEach(
(element) => {
(element as HTMLElement).innerText = nick;
}
);
Array.from(document.getElementsByClassName('host-INSERT')).forEach(
(element) => {
(element as HTMLElement).innerText = host;
}
);
const kindSpanElement = document.getElementById('kindSpan');
if (kindSpanElement && eventParsed.kind !== undefined) {
kindSpanElement.innerText = eventParsed.kind;
}
const cardGetPublicKeyElement = document.getElementById('cardGetPublicKey');
if (cardGetPublicKeyElement) {
if (method === 'getPublicKey') {
// Do nothing.
} else {
cardGetPublicKeyElement.style.display = 'none';
}
}
const cardGetRelaysElement = document.getElementById('cardGetRelays');
if (cardGetRelaysElement) {
if (method === 'getRelays') {
// Do nothing.
} else {
cardGetRelaysElement.style.display = 'none';
}
}
const cardSignEventElement = document.getElementById('cardSignEvent');
const card2SignEventElement = document.getElementById('card2SignEvent');
if (cardSignEventElement && card2SignEventElement) {
if (method === 'signEvent') {
const card2SignEvent_jsonElement = document.getElementById(
'card2SignEvent_json'
);
if (card2SignEvent_jsonElement) {
card2SignEvent_jsonElement.innerText = event;
}
} else {
cardSignEventElement.style.display = 'none';
card2SignEventElement.style.display = 'none';
}
}
const cardNip04EncryptElement = document.getElementById('cardNip04Encrypt');
const card2Nip04EncryptElement = document.getElementById('card2Nip04Encrypt');
if (cardNip04EncryptElement && card2Nip04EncryptElement) {
if (method === 'nip04.encrypt') {
const card2Nip04Encrypt_textElement = document.getElementById(
'card2Nip04Encrypt_text'
);
if (card2Nip04Encrypt_textElement) {
const eventObject = eventParsed as { peerPubkey: string; plaintext: string };
card2Nip04Encrypt_textElement.innerText = eventObject.plaintext || '';
}
} else {
cardNip04EncryptElement.style.display = 'none';
card2Nip04EncryptElement.style.display = 'none';
}
}
const cardNip44EncryptElement = document.getElementById('cardNip44Encrypt');
const card2Nip44EncryptElement = document.getElementById('card2Nip44Encrypt');
if (cardNip44EncryptElement && card2Nip44EncryptElement) {
if (method === 'nip44.encrypt') {
const card2Nip44Encrypt_textElement = document.getElementById(
'card2Nip44Encrypt_text'
);
if (card2Nip44Encrypt_textElement) {
const eventObject = eventParsed as { peerPubkey: string; plaintext: string };
card2Nip44Encrypt_textElement.innerText = eventObject.plaintext || '';
}
} else {
cardNip44EncryptElement.style.display = 'none';
card2Nip44EncryptElement.style.display = 'none';
}
}
const cardNip04DecryptElement = document.getElementById('cardNip04Decrypt');
const card2Nip04DecryptElement = document.getElementById('card2Nip04Decrypt');
if (cardNip04DecryptElement && card2Nip04DecryptElement) {
if (method === 'nip04.decrypt') {
const card2Nip04Decrypt_textElement = document.getElementById(
'card2Nip04Decrypt_text'
);
if (card2Nip04Decrypt_textElement) {
const eventObject = eventParsed as { peerPubkey: string; ciphertext: string };
card2Nip04Decrypt_textElement.innerText = eventObject.ciphertext || '';
}
} else {
cardNip04DecryptElement.style.display = 'none';
card2Nip04DecryptElement.style.display = 'none';
}
}
const cardNip44DecryptElement = document.getElementById('cardNip44Decrypt');
const card2Nip44DecryptElement = document.getElementById('card2Nip44Decrypt');
if (cardNip44DecryptElement && card2Nip44DecryptElement) {
if (method === 'nip44.decrypt') {
const card2Nip44Decrypt_textElement = document.getElementById(
'card2Nip44Decrypt_text'
);
if (card2Nip44Decrypt_textElement) {
const eventObject = eventParsed as { peerPubkey: string; ciphertext: string };
card2Nip44Decrypt_textElement.innerText = eventObject.ciphertext || '';
}
} else {
cardNip44DecryptElement.style.display = 'none';
card2Nip44DecryptElement.style.display = 'none';
}
}
//
// Functions
//
async function deliver(response: PromptResponse) {
const message: PromptResponseMessage = {
id,
response,
};
try {
await browser.runtime.sendMessage(message);
} catch (error) {
console.error('Failed to send message:', error);
}
window.close();
}
document.addEventListener('DOMContentLoaded', function () {
const rejectOnceButton = document.getElementById('rejectOnceButton');
rejectOnceButton?.addEventListener('click', () => {
deliver('reject-once');
});
const rejectAlwaysButton = document.getElementById('rejectAlwaysButton');
rejectAlwaysButton?.addEventListener('click', () => {
deliver('reject');
});
const approveOnceButton = document.getElementById('approveOnceButton');
approveOnceButton?.addEventListener('click', () => {
deliver('approve-once');
});
const approveAlwaysButton = document.getElementById('approveAlwaysButton');
approveAlwaysButton?.addEventListener('click', () => {
deliver('approve');
});
});