// ==UserScript== // @name NIP-07 Relay Auth Filter // @namespace // @version 1.0 // @description Block NIP-42 auth and extra maybe encryption // @match *://chachi.chat/* // @grant none // @run-at document-start // ==/UserScript== (function () { "use strict"; // Disable encryption for site const ALLOW_DECRYPT = false; const ALLOW_ENCRYPT = false; // Block all auth requests except for these relays const RELAY_AUTH_WHITELIST = [ 'pyramid.fiatjaf.com', 'nostr.land', 'nostr.wine' ]; function wrapNipObject(nip, allowDecrypt, allowEncrypt) { if (!nip) return nip; const wrapped = { ...nip }; if (typeof nip.decrypt === "function") { wrapped.decrypt = async function (...args) { if (!allowDecrypt) throw new Error("Decrypt blocked by filter"); return nip.decrypt.apply(nip, args); }; } if (typeof nip.encrypt === "function") { wrapped.encrypt = async function (...args) { if (!allowEncrypt) throw new Error("Encrypt blocked by filter"); return nip.encrypt.apply(nip, args); }; } return wrapped; } function wrapNostr(nostr) { if (!nostr || typeof nostr.signEvent !== "function") return nostr; const originalSignEvent = nostr.signEvent.bind(nostr); const wrapped = { ...nostr }; wrapped.signEvent = async function (event) { // Block relay auth (kind 22242) for non-whitelisted relays if (event.kind === 22242) { const relay = event.tags?.find(t => t[0] === 'relay')?.[1]; if (relay && URL.canParse(relay)) { const url = new URL(relay); if (!RELAY_AUTH_WHITELIST.includes(url.hostname)) { throw new Error("Relay auth blocked: relay not in whitelist"); } } } return originalSignEvent(event); }; // Wrap nip04 and nip44 objects if (nostr.nip04) wrapped.nip04 = wrapNipObject(nostr.nip04, ALLOW_DECRYPT, ALLOW_ENCRYPT); if (nostr.nip44) wrapped.nip44 = wrapNipObject(nostr.nip44, ALLOW_DECRYPT, ALLOW_ENCRYPT); return wrapped; } // Intercept window.nostr assignments let nostrValue = window.nostr; Object.defineProperty(window, "nostr", { get: () => nostrValue, set: (value) => { nostrValue = value && typeof value.signEvent === "function" ? wrapNostr(value) : value; }, configurable: true, enumerable: true }); // Wrap existing window.nostr if present if (window.nostr) window.nostr = wrapNostr(window.nostr); // Check again after page load + 1s window.addEventListener("load", () => { setTimeout(() => { if (window.nostr && typeof window.nostr.signEvent === "function") { const isWrapped = window.nostr.signEvent.toString().includes("RELAY_AUTH_WHITELIST"); if (!isWrapped) window.nostr = wrapNostr(window.nostr); } }, 1000); }); })();