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>
This commit is contained in:
@@ -3,8 +3,7 @@
|
||||
<div class="deriving-modal">
|
||||
<div class="deriving-spinner"></div>
|
||||
<h3>{{ message }}</h3>
|
||||
<div class="deriving-timer">{{ elapsed.toFixed(1) }}s</div>
|
||||
<p class="deriving-note">This may take 3-6 seconds for security</p>
|
||||
<p class="deriving-note">This may take a few seconds</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -30,14 +30,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.deriving-timer {
|
||||
font-size: 2.5rem;
|
||||
font-weight: bold;
|
||||
color: #ff3eb5;
|
||||
font-family: monospace;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.deriving-note {
|
||||
margin: 0.5rem 0 0;
|
||||
color: #a1a1a1;
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
import {
|
||||
Component,
|
||||
OnDestroy,
|
||||
} from '@angular/core';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-deriving-modal',
|
||||
templateUrl: './deriving-modal.component.html',
|
||||
styleUrl: './deriving-modal.component.scss',
|
||||
})
|
||||
export class DerivingModalComponent implements OnDestroy {
|
||||
export class DerivingModalComponent {
|
||||
visible = false;
|
||||
elapsed = 0;
|
||||
message = 'Deriving encryption key';
|
||||
|
||||
#startTime: number | null = null;
|
||||
#animationFrame: number | null = null;
|
||||
|
||||
/**
|
||||
* Show the deriving modal and start the timer
|
||||
* Show the deriving modal
|
||||
* @param message Optional custom message
|
||||
*/
|
||||
show(message?: string): void {
|
||||
@@ -25,35 +18,12 @@ export class DerivingModalComponent implements OnDestroy {
|
||||
this.message = message;
|
||||
}
|
||||
this.visible = true;
|
||||
this.elapsed = 0;
|
||||
this.#startTime = performance.now();
|
||||
this.#updateTimer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the modal and stop the timer
|
||||
* Hide the modal
|
||||
*/
|
||||
hide(): void {
|
||||
this.visible = false;
|
||||
this.#stopTimer();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.#stopTimer();
|
||||
}
|
||||
|
||||
#updateTimer(): void {
|
||||
if (this.#startTime !== null) {
|
||||
this.elapsed = (performance.now() - this.#startTime) / 1000;
|
||||
this.#animationFrame = requestAnimationFrame(() => this.#updateTimer());
|
||||
}
|
||||
}
|
||||
|
||||
#stopTimer(): void {
|
||||
this.#startTime = null;
|
||||
if (this.#animationFrame !== null) {
|
||||
cancelAnimationFrame(this.#animationFrame);
|
||||
this.#animationFrame = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,76 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
export interface LogEntry {
|
||||
timestamp: Date;
|
||||
level: 'log' | 'warn' | 'error' | 'debug';
|
||||
message: string;
|
||||
data?: any;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class LoggerService {
|
||||
#namespace: string | undefined;
|
||||
#logs: LogEntry[] = [];
|
||||
#maxLogs = 500;
|
||||
|
||||
get logs(): LogEntry[] {
|
||||
return this.#logs;
|
||||
}
|
||||
|
||||
initialize(namespace: string): void {
|
||||
this.#namespace = namespace;
|
||||
}
|
||||
|
||||
log(value: any) {
|
||||
log(value: any, data?: any) {
|
||||
this.#assureInitialized();
|
||||
|
||||
this.#addLog('log', value, data);
|
||||
const nowString = new Date().toLocaleString();
|
||||
|
||||
console.log(`[${this.#namespace} - ${nowString}]`, JSON.stringify(value));
|
||||
}
|
||||
|
||||
warn(value: any, data?: any) {
|
||||
this.#assureInitialized();
|
||||
this.#addLog('warn', value, data);
|
||||
const nowString = new Date().toLocaleString();
|
||||
console.warn(`[${this.#namespace} - ${nowString}]`, JSON.stringify(value));
|
||||
}
|
||||
|
||||
error(value: any, data?: any) {
|
||||
this.#assureInitialized();
|
||||
this.#addLog('error', value, data);
|
||||
const nowString = new Date().toLocaleString();
|
||||
console.error(`[${this.#namespace} - ${nowString}]`, JSON.stringify(value));
|
||||
}
|
||||
|
||||
debug(value: any, data?: any) {
|
||||
this.#assureInitialized();
|
||||
this.#addLog('debug', value, data);
|
||||
const nowString = new Date().toLocaleString();
|
||||
console.debug(`[${this.#namespace} - ${nowString}]`, JSON.stringify(value));
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.#logs = [];
|
||||
}
|
||||
|
||||
#addLog(level: LogEntry['level'], message: any, data?: any) {
|
||||
const entry: LogEntry = {
|
||||
timestamp: new Date(),
|
||||
level,
|
||||
message: typeof message === 'string' ? message : JSON.stringify(message),
|
||||
data,
|
||||
};
|
||||
this.#logs.unshift(entry);
|
||||
|
||||
// Limit stored logs
|
||||
if (this.#logs.length > this.#maxLogs) {
|
||||
this.#logs.pop();
|
||||
}
|
||||
}
|
||||
|
||||
#assureInitialized() {
|
||||
if (!this.#namespace) {
|
||||
throw new Error(
|
||||
|
||||
@@ -146,12 +146,17 @@ export const decryptPermissions = async function (
|
||||
const decryptedPermissions: Permission_DECRYPTED[] = [];
|
||||
|
||||
for (const permission of permissions) {
|
||||
const decryptedPermission = await decryptPermission.call(
|
||||
this,
|
||||
permission,
|
||||
withLockedVault
|
||||
);
|
||||
decryptedPermissions.push(decryptedPermission);
|
||||
try {
|
||||
const decryptedPermission = await decryptPermission.call(
|
||||
this,
|
||||
permission,
|
||||
withLockedVault
|
||||
);
|
||||
decryptedPermissions.push(decryptedPermission);
|
||||
} catch (error) {
|
||||
// Skip corrupted permissions (e.g., encrypted with wrong key)
|
||||
console.warn('[vault] Skipping corrupted permission:', error);
|
||||
}
|
||||
}
|
||||
|
||||
return decryptedPermissions;
|
||||
|
||||
Reference in New Issue
Block a user