Started Walet RPC auth work without adding MD5 Digest support

This commit is contained in:
2026-10-21 17:33:33 -04:00
parent f00ea55dfd
commit 40f4aa6d4e
7 changed files with 467 additions and 22 deletions
+7 -9
View File
@@ -11,8 +11,6 @@ const MAX_TRANSFERS = 4096;
const configSchema = z.object({
network: z.literal('mainnet'),
url: z.string().min(1).max(2048),
username: z.string().min(1).max(256),
password: z.string().min(1).max(1024),
}).strict();
const rpcEnvelopeSchema = z.object({
jsonrpc: z.literal('2.0'), id: z.string().uuid(), result: z.record(z.string(), z.unknown()).optional(),
@@ -137,18 +135,18 @@ export async function loadMoneroObserverConfig(env: NodeJS.ProcessEnv = process.
if (process.platform !== 'win32' && (info.mode & 0o077) !== 0) throw new Error();
const parsed = configSchema.parse(JSON.parse(await readFile(path, 'utf8')));
validateEndpoint(parsed.url);
if (/[\r\n\x00]/.test(parsed.username) || /[\r\n\x00]/.test(parsed.password)) throw new Error();
return parsed;
} catch {
throw new Error('Invalid or inaccessible Monero wallet-RPC config; credentials must be in a protected regular file');
throw new Error('Invalid or inaccessible protected Monero wallet-RPC configuration');
}
}
/** Restricted read-only surface. No generic RPC method, subaddress creation, transfer or sweep API exists. */
/** Restricted RPC surface. Authentication is delegated to an explicitly injected secure transport. */
function createWalletRpc(config: z.infer<typeof configSchema>, transport: MoneroFixtureTransport,
allowedMethods: readonly ('get_height' | 'get_address' | 'get_transfers' | 'validate_address' | 'create_address')[]) {
const endpoint = validateEndpoint(config.url);
const authorization = `Basic ${Buffer.from(`${config.username}:${config.password}`).toString('base64')}`;
// Removed Basic auth header
async function rpc(method: 'get_height' | 'get_address' | 'get_transfers' | 'validate_address' | 'create_address', params: Record<string, unknown> = {}) {
if (!allowedMethods.includes(method)) throw new Error('Monero wallet-RPC method is not allowed for this client');
const id = randomUUID(), controller = new AbortController();
@@ -159,7 +157,7 @@ function createWalletRpc(config: z.infer<typeof configSchema>, transport: Monero
let response: Response;
try {
response = await transport(endpoint.href, { method: 'POST', redirect: 'error', signal: controller.signal,
headers: { authorization, accept: 'application/json', 'content-type': 'application/json' },
headers: { accept: 'application/json', 'content-type': 'application/json' },
body: JSON.stringify({ jsonrpc: '2.0', id, method, params }) });
} catch { throw new Error('Monero wallet-RPC request failed'); }
if (!response.ok || response.redirected || !response.body ||
@@ -194,7 +192,7 @@ function createWalletRpc(config: z.infer<typeof configSchema>, transport: Monero
}
/** Read-only observer capability. It deliberately cannot create addresses or move funds. */
export function createMoneroWalletObserver(config: z.infer<typeof configSchema>, transport: MoneroFixtureTransport = fetch) {
export function createMoneroWalletObserver(config: z.infer<typeof configSchema>, transport: MoneroFixtureTransport) {
const rpc = createWalletRpc(config, transport, ['get_height', 'get_address', 'get_transfers']);
return {
async verifyMainnetWallet(): Promise<string> {
@@ -254,11 +252,11 @@ export async function moneroWalletFingerprint(primaryAddress: string): Promise<s
* from API and withdrawal capabilities. This
* client is intentionally not attached to startup, routes, UI, or automatic crediting.
*/
export function createMoneroDepositAddressRpc(config: z.infer<typeof configSchema>, transport: MoneroFixtureTransport = fetch) {
export function createMoneroDepositAddressRpc(config: z.infer<typeof configSchema>, transport: MoneroFixtureTransport) {
const rpc = createWalletRpc(config, transport, ['get_address', 'validate_address', 'create_address']);
return {
async verifyWallet(): Promise<string> {
const address = addressResultSchema.parse(await rpc('get_address', { account_index: 0 }));
const address = addressResultSchema.parse(await rpc('get_address', { account_index: 0, address_index: [0] }));
await validateXmrAddress(address.address);
validateAddressResultSchema.parse(await rpc('validate_address', { address: address.address }));
return moneroWalletFingerprint(address.address);