34 lines
1.7 KiB
TypeScript
Executable File
34 lines
1.7 KiB
TypeScript
Executable File
import { constants } from 'node:fs';
|
|
import { open } from 'node:fs/promises';
|
|
import { isAbsolute } from 'node:path';
|
|
|
|
const MAX_REQUEST_BYTES = 64 * 1024;
|
|
|
|
/** Read a signed wallet request only from a bounded owner-only regular file. */
|
|
export async function loadPilotMoneroDepositRequestFile(path: string): Promise<unknown> {
|
|
if (process.platform === 'win32') throw new Error('Pilot address command requires POSIX protected-file permission checks');
|
|
if (!isAbsolute(path)) throw new Error('Pilot address request file path must be absolute');
|
|
let file;
|
|
try { file = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW); }
|
|
catch { throw new Error('Pilot address request file is inaccessible or is a symbolic link'); }
|
|
try {
|
|
const info = await file.stat();
|
|
if (!info.isFile() || info.size < 1 || info.size > MAX_REQUEST_BYTES || (info.mode & 0o077) !== 0) {
|
|
throw new Error('Pilot address request file must be bounded and owner-only');
|
|
}
|
|
const chunks: Buffer[] = [];
|
|
let total = 0;
|
|
for (;;) {
|
|
const chunk = Buffer.allocUnsafe(Math.min(4096, MAX_REQUEST_BYTES + 1 - total));
|
|
const { bytesRead } = await file.read(chunk);
|
|
if (bytesRead === 0) break;
|
|
total += bytesRead;
|
|
if (total > MAX_REQUEST_BYTES) throw new Error('Pilot address request file exceeds size limit');
|
|
chunks.push(chunk.subarray(0, bytesRead));
|
|
}
|
|
const text = new TextDecoder('utf-8', { fatal: true }).decode(Buffer.concat(chunks));
|
|
try { return JSON.parse(text) as unknown; }
|
|
catch { throw new Error('Pilot address request file does not contain valid JSON'); }
|
|
} finally { await file.close(); }
|
|
}
|