Release v0.0.9 - Add reckless mode with whitelisted apps

- Add reckless mode checkbox to auto-approve signing requests
- Implement whitelisted apps management page
- Reckless mode logic: allow all if whitelist empty, otherwise only whitelisted hosts
- Add shouldRecklessModeApprove() in background service worker
- Update default avatar to Plebeian Market Account icon
- Fix manifest version scripts to strip v prefix for browsers

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-19 10:56:45 +01:00
parent 5550d41293
commit ddb74c61b2
27 changed files with 952 additions and 116 deletions

View File

@@ -12,6 +12,7 @@ import {
nip44Encrypt,
PromptResponse,
PromptResponseMessage,
shouldRecklessModeApprove,
signEvent,
storePermission,
} from './background-common';
@@ -63,57 +64,65 @@ browser.runtime.onMessage.addListener(async (message /*, sender*/) => {
}
const req = request as BackgroundRequestMessage;
const permissionState = checkPermissions(
browserSessionData,
currentIdentity,
req.host,
req.method,
req.params
);
if (permissionState === false) {
throw new Error('Permission denied');
}
// Check reckless mode first
const recklessApprove = await shouldRecklessModeApprove(req.host);
if (recklessApprove) {
debug('Request auto-approved via reckless mode.');
} else {
// Normal permission flow
const permissionState = checkPermissions(
browserSessionData,
currentIdentity,
req.host,
req.method,
req.params
);
if (permissionState === undefined) {
// Ask user for permission.
const width = 375;
const height = 600;
const { top, left } = await getPosition(width, height);
const base64Event = Buffer.from(
JSON.stringify(req.params ?? {}, undefined, 2)
).toString('base64');
const response = await new Promise<PromptResponse>((resolve, reject) => {
const id = crypto.randomUUID();
openPrompts.set(id, { resolve, reject });
browser.windows.create({
type: 'popup',
url: `prompt.html?method=${req.method}&host=${req.host}&id=${id}&nick=${currentIdentity.nick}&event=${base64Event}`,
height,
width,
top,
left,
});
});
debug(response);
if (response === 'approve' || response === 'reject') {
await storePermission(
browserSessionData,
currentIdentity,
req.host,
req.method,
response === 'approve' ? 'allow' : 'deny',
req.params?.kind
);
}
if (['reject', 'reject-once'].includes(response)) {
if (permissionState === false) {
throw new Error('Permission denied');
}
} else {
debug('Request allowed (via saved permission).');
if (permissionState === undefined) {
// Ask user for permission.
const width = 375;
const height = 600;
const { top, left } = await getPosition(width, height);
const base64Event = Buffer.from(
JSON.stringify(req.params ?? {}, undefined, 2)
).toString('base64');
const response = await new Promise<PromptResponse>((resolve, reject) => {
const id = crypto.randomUUID();
openPrompts.set(id, { resolve, reject });
browser.windows.create({
type: 'popup',
url: `prompt.html?method=${req.method}&host=${req.host}&id=${id}&nick=${currentIdentity.nick}&event=${base64Event}`,
height,
width,
top,
left,
});
});
debug(response);
if (response === 'approve' || response === 'reject') {
await storePermission(
browserSessionData,
currentIdentity,
req.host,
req.method,
response === 'approve' ? 'allow' : 'deny',
req.params?.kind
);
}
if (['reject', 'reject-once'].includes(response)) {
throw new Error('Permission denied');
}
} else {
debug('Request allowed (via saved permission).');
}
}
const relays: Relays = {};