Merge branch 'unstable' into content-docs-audit-10961466235628399718

This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2026-02-09 08:57:59 -05:00
commit 5216b45985
6 files changed
+114 -9

No files matched your search

+38
View File
@@ -800,6 +800,8 @@ export class NostrClient {
get sessionActorCipherClosuresPrivateKey() { return this.signerManager.sessionActorCipherClosuresPrivateKey; }
set sessionActorCipherClosuresPrivateKey(val) { this.signerManager.sessionActorCipherClosuresPrivateKey = val; }
get extensionPermissionCache() { return this.signerManager.extensionPermissionCache; }
get pool() { return this.connectionManager.pool; }
set pool(val) { this.connectionManager.pool = val; }
@@ -940,6 +942,23 @@ export class NostrClient {
}
}
resolveEventDTag(event, fallbackEvent = null) {
if (!event || typeof event !== "object") {
if (fallbackEvent) {
return this.resolveEventDTag(fallbackEvent);
}
return "";
}
const val = getDTagValueFromTags(event.tags);
if (val) {
return val;
}
if (fallbackEvent) {
return this.resolveEventDTag(fallbackEvent);
}
return "";
}
getActiveKey(video) {
return getActiveKey(video);
}
@@ -2973,6 +2992,21 @@ export class NostrClient {
return this.signerManager.ensureActiveSignerForPubkey(pubkey);
}
async registerPrivateKeySigner({ privateKey, pubkey }) {
if (!privateKey) {
throw new Error("Private key is required.");
}
const adapter = await createNsecAdapter({ privateKey, pubkey });
this.signerManager.setActiveSigner(adapter);
this.sessionActor = {
pubkey: adapter.pubkey,
privateKey: privateKey,
source: "nsec",
createdAt: Date.now(),
};
return adapter;
}
async loginWithExtension(options) {
return this.signerManager.loginWithExtension(options);
}
@@ -2981,6 +3015,10 @@ export class NostrClient {
return this.signerManager.connectRemoteSigner(params);
}
installNip46Client(client) {
this.signerManager.installNip46Client(client);
}
async useStoredRemoteSigner(options) {
return this.signerManager.useStoredRemoteSigner(options);
}
+53 -4
View File
@@ -47,6 +47,7 @@ import {
summarizeUrlForLog,
} from "../nip46LoggingUtils.js";
import { bytesToHex } from "../../../vendor/crypto-helpers.bundle.min.js";
import { devLogger, userLogger } from "../../utils/logger.js";
import { createPrivateKeyCipherClosures } from "../signerHelpers.js";
import { queueSignEvent } from "../signRequestQueue.js";
@@ -263,6 +264,10 @@ export class SignerManager {
this.extensionReady = false;
this.extensionPermissionsGranted = false;
this.extensionPermissionCache = new Map();
const storedPermissions = readStoredNip07Permissions();
for (const method of storedPermissions) {
this.extensionPermissionCache.set(method, true);
}
this.sessionActorCipherClosures = null;
this.sessionActorCipherClosuresPrivateKey = null;
this.remoteSignerListeners = new Set();
@@ -311,21 +316,45 @@ export class SignerManager {
async ensureSessionActor() {
if (this.sessionActor) {
return this.sessionActor;
return this.sessionActor.pubkey;
}
if (this.lockedSessionActor) {
return this.lockedSessionActor;
return this.lockedSessionActor.pubkey;
}
const storedEntry = readStoredSessionActorEntry();
if (storedEntry) {
this.lockedSessionActor = storedEntry;
this.sessionActor = storedEntry;
return storedEntry;
return storedEntry.pubkey;
}
return null;
const tools = await ensureNostrTools();
let privateKey = "";
if (tools && typeof tools.generateSecretKey === "function") {
const secret = tools.generateSecretKey();
privateKey = bytesToHex(secret);
} else if (tools && typeof tools.generatePrivateKey === "function") {
privateKey = tools.generatePrivateKey();
} else {
throw new Error("Failed to generate session actor: nostr-tools missing key generation.");
}
let pubkey = "";
if (tools && typeof tools.getPublicKey === "function") {
pubkey = tools.getPublicKey(privateKey);
}
const newActor = {
pubkey,
privateKey,
source: "generated",
createdAt: Date.now(),
};
this.sessionActor = newActor;
return pubkey;
}
clearStoredSessionActor() {
@@ -423,6 +452,9 @@ export class SignerManager {
if (missing.length === 0) {
this.extensionPermissionsGranted = true;
this.extensionPermissionCache.set(cacheKey, true);
for (const method of methods) {
this.extensionPermissionCache.set(method, true);
}
return { ok: true };
}
@@ -441,6 +473,9 @@ export class SignerManager {
writeStoredNip07Permissions(missing);
this.extensionPermissionsGranted = true;
this.extensionPermissionCache.set(cacheKey, true);
for (const method of missing) {
this.extensionPermissionCache.set(method, true);
}
return { ok: true };
}
return { ok: false, error: "permission-denied" };
@@ -548,6 +583,16 @@ export class SignerManager {
return { pubkey: normalized, signer: adapter };
}
installNip46Client(client) {
if (!client) {
return;
}
this.nip46Client = client;
this.pubkey = client.userPubkey || client.remotePubkey; // Fallback
this.setActiveSigner(client.getActiveSigner());
this.emitRemoteSignerChange();
}
async connectRemoteSigner({
connectionString,
remember = true,
@@ -894,6 +939,10 @@ export class SignerManager {
};
}
signEventWithPrivateKey(event, privateKey) {
return signEventWithPrivateKey(event, privateKey);
}
logout() {
const previousPubkey = this.pubkey;
this.pubkey = null;
+3
View File
@@ -5,6 +5,9 @@ import { normalizeNostrPubkey, sanitizeRelayList } from "./nip46Client.js";
import { DEFAULT_RELAY_URLS, RELAY_URLS } from "./toolkit.js";
export class RelayBatchFetcher {
/**
* @param {import("./client.js").NostrClient} client
*/
constructor(client) {
this.client = client;
}
+11 -1
View File
@@ -484,7 +484,17 @@ export function clearStoredSessionActor() {
export function isSessionActor(nostrClient) {
const sa = nostrClient?.sessionActor;
return !!sa && sa.source !== "nsec";
if (!sa) {
return false;
}
if (
nostrClient?.pubkey &&
sa.pubkey &&
nostrClient.pubkey !== sa.pubkey
) {
return false;
}
return sa.source !== "nsec";
}
function _closeSessionActorDb() {
+4 -3
View File
@@ -458,10 +458,11 @@ describe("NIP-07 Login Permissions", () => {
it("NIP-07 login does not wait for deferred permission grants", async () => {
clearStoredPermissions();
const env = setupLoginEnvironment();
const originalEnsurePermissions = nostrClient.ensureExtensionPermissions;
const target = nostrClient.signerManager || nostrClient;
const originalEnsurePermissions = target.ensureExtensionPermissions;
let completionPromise;
nostrClient.ensureExtensionPermissions = async () => {
target.ensureExtensionPermissions = async () => {
completionPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error("permission denied")), 300);
});
@@ -484,7 +485,7 @@ describe("NIP-07 Login Permissions", () => {
"deferred permission grants should still reject",
);
} finally {
nostrClient.ensureExtensionPermissions = originalEnsurePermissions;
target.ensureExtensionPermissions = originalEnsurePermissions;
env.restore();
nostrClient.logout();
clearStoredPermissions();
+5 -1
View File
@@ -15,7 +15,11 @@ test("ensureActiveSignerForPubkey prefers concurrent login over late extension i
const client = new NostrClient();
client.pubkey = HEX_PUBKEY;
// Pre-condition: Cache permissions to trigger the waitForNip07Extension logic
client.extensionPermissionCache.add("sign_event");
if (typeof client.extensionPermissionCache.add === 'function') {
client.extensionPermissionCache.add("sign_event");
} else if (typeof client.extensionPermissionCache.set === 'function') {
client.extensionPermissionCache.set("sign_event", true);
}
// Mock a NIP-46 signer that might be set during the race
const mockNip46Signer = {