This commit is contained in:
thePR0M3TH3AN committed 2025-10-26 21:50:09 -04:00
1 parent 281a6511f1
commit 42edebcf6d
45 files changed
+8446 -1010

No files matched your search

+205 -6
View File
@@ -13,12 +13,45 @@ import {
persistAdminState,
readCachedAdminState,
} from "./adminListStore.js";
import { ensureNostrTools } from "./nostr/toolkit.js";
import { userLogger } from "./utils/logger.js";
const HEX_KEY_PATTERN = /^[0-9a-f]{64}$/i;
function normalizeNpub(value) {
return typeof value === "string" ? value.trim() : "";
}
function normalizeHexKey(value) {
if (typeof value !== "string") {
return "";
}
const trimmed = value.trim().toLowerCase();
return HEX_KEY_PATTERN.test(trimmed) ? trimmed : "";
}
function bytesToHex(bytes) {
if (!bytes || typeof bytes !== "object") {
return "";
}
if (typeof bytes.length !== "number") {
return "";
}
let hex = "";
for (const byte of Array.from(bytes)) {
const normalized = Number(byte);
if (!Number.isInteger(normalized) || normalized < 0 || normalized > 255) {
return "";
}
hex += normalized.toString(16).padStart(2, "0");
}
return normalizeHexKey(hex);
}
function dedupeNpubs(values) {
const normalized = Array.isArray(values) ? values.map(normalizeNpub) : [];
return Array.from(
@@ -73,7 +106,9 @@ class AccessControl {
constructor() {
this.editors = new Set();
this.whitelist = new Set();
this.whitelistPubkeys = new Set();
this.blacklist = new Set();
this.blacklistPubkeys = new Set();
this.whitelistEnabled = getWhitelistMode();
this.hasLoaded = false;
this.lastError = null;
@@ -112,6 +147,85 @@ class AccessControl {
}
}
_rebuildHexSets(
whitelistValues = [],
blacklistValues = [],
toolkitCandidate = null
) {
const whitelistHex = new Set();
const blacklistHex = new Set();
const decodeSources = [];
const addDecodeSource = (candidate) => {
if (
candidate &&
typeof candidate === "object" &&
candidate.nip19 &&
typeof candidate.nip19.decode === "function" &&
!decodeSources.includes(candidate.nip19)
) {
decodeSources.push(candidate.nip19);
}
};
addDecodeSource(toolkitCandidate);
const scopeTools =
(typeof window !== "undefined" ? window?.NostrTools : null) ||
(typeof globalThis !== "undefined" ? globalThis?.NostrTools : null) ||
null;
addDecodeSource(scopeTools);
const decodeNpubToHex = (npub) => {
const normalized = normalizeNpub(npub);
if (!normalized) {
return "";
}
for (const nip19 of decodeSources) {
try {
const decoded = nip19.decode(normalized);
if (!decoded || decoded.type !== "npub") {
continue;
}
const data = decoded.data;
if (typeof data === "string") {
const normalizedHex = normalizeHexKey(data);
if (normalizedHex) {
return normalizedHex;
}
} else {
const converted = bytesToHex(data);
if (converted) {
return converted;
}
}
} catch (error) {
continue;
}
}
return "";
};
for (const entry of whitelistValues) {
const decodedHex = decodeNpubToHex(entry);
if (decodedHex) {
whitelistHex.add(decodedHex);
}
}
for (const entry of blacklistValues) {
const decodedHex = decodeNpubToHex(entry);
if (decodedHex) {
blacklistHex.add(decodedHex);
}
}
this.whitelistPubkeys = whitelistHex;
this.blacklistPubkeys = blacklistHex;
}
_applyState(state, options = {}) {
const markLoaded = options.markLoaded !== false;
const editors = Array.isArray(state?.editors) ? state.editors : [];
@@ -152,6 +266,37 @@ class AccessControl {
});
this.blacklist = new Set(sanitizedBlacklist);
const toolkitCandidate =
(typeof window !== "undefined" ? window?.NostrTools : null) ||
(typeof globalThis !== "undefined" ? globalThis?.NostrTools : null);
this._rebuildHexSets(normalizedWhitelist, sanitizedBlacklist, toolkitCandidate);
try {
const ensured = ensureNostrTools();
if (ensured && typeof ensured.then === "function") {
ensured
.then((tools) => {
if (!tools) {
return;
}
this._rebuildHexSets(
Array.from(this.whitelist),
Array.from(this.blacklist),
tools
);
})
.catch(() => {});
} else if (ensured) {
this._rebuildHexSets(
Array.from(this.whitelist),
Array.from(this.blacklist),
ensured
);
}
} catch (error) {
// ignore toolkit errors; we'll attempt to hydrate again on the next refresh
}
this.whitelistEnabled = getWhitelistMode();
this.lastError = null;
@@ -220,7 +365,9 @@ class AccessControl {
if (!this.hasLoaded && !this._hydratedFromCache) {
this.editors.clear();
this.whitelist.clear();
this.whitelistPubkeys.clear();
this.blacklist.clear();
this.blacklistPubkeys.clear();
}
throw error;
} finally {
@@ -497,29 +644,72 @@ class AccessControl {
if (this.whitelist.has(normalized)) {
return false;
}
const normalizedHexCandidate = normalizeHexKey(normalized);
if (normalizedHexCandidate) {
if (this.whitelistPubkeys.has(normalizedHexCandidate)) {
return false;
}
return this.blacklistPubkeys.has(normalizedHexCandidate);
}
return this.blacklist.has(normalized);
}
canAccess(candidate) {
let npub = "";
let hex = "";
const considerHexCandidate = (value) => {
const normalizedHex = normalizeHexKey(value);
if (!normalizedHex) {
return false;
}
hex = normalizedHex;
return true;
};
if (typeof candidate === "string") {
npub = candidate;
if (!considerHexCandidate(candidate)) {
npub = candidate;
}
} else if (candidate && typeof candidate === "object") {
if (typeof candidate.npub === "string") {
npub = candidate.npub;
} else if (typeof candidate.pubkey === "string") {
try {
npub = window.NostrTools.nip19.npubEncode(candidate.pubkey);
} catch (error) {
npub = candidate.pubkey;
if (!considerHexCandidate(candidate.pubkey)) {
try {
npub = window.NostrTools.nip19.npubEncode(candidate.pubkey);
} catch (error) {
npub = candidate.pubkey;
}
}
}
}
const normalized = normalizeNpub(npub);
if (!normalized) {
return false;
if (!hex) {
return false;
}
if (this.isLockdownActive()) {
return false;
}
if (this.whitelistPubkeys.has(hex)) {
return true;
}
if (this.blacklistPubkeys.has(hex)) {
return false;
}
if (this.whitelistEnabled && !this.whitelistPubkeys.has(hex)) {
return false;
}
return true;
}
if (this.isAdminEditor(normalized)) {
@@ -530,6 +720,15 @@ class AccessControl {
return false;
}
if (hex) {
if (this.whitelistPubkeys.has(hex)) {
return true;
}
if (this.blacklistPubkeys.has(hex)) {
return false;
}
}
if (this.whitelist.has(normalized)) {
return true;
}