Multi-Hoster-Upload/gateway/registry.js
Administrator 602e48cb01 fix(gateway): write the token registry with owner-only permissions (0600)
registry.json holds each server's bearer token (the connection secret). It was
written with the process default umask, leaving it group/world-readable on POSIX
multi-user hosts. Write with mode 0o600 and chmod the existing file (writeFile
only applies mode on creation). No-op on Windows (NTFS uses ACLs, and the file
already sits under the user profile and is gitignored), effective on Linux/macOS
where the gateway may run.

Gateway-only change — not part of the app installer or auto-updater, so no
version bump.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 17:57:39 +02:00

52 lines
1.5 KiB
JavaScript

import { readFile, writeFile, chmod } from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
const HERE = dirname(fileURLToPath(import.meta.url));
const REGISTRY_PATH = join(HERE, 'registry.json');
export async function loadRegistry() {
try {
const raw = await readFile(REGISTRY_PATH, 'utf8');
const parsed = JSON.parse(raw);
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
return {};
}
return parsed;
} catch {
return {};
}
}
export async function saveRegistry(registry) {
const data = registry && typeof registry === 'object' ? registry : {};
await writeFile(REGISTRY_PATH, JSON.stringify(data, null, 2) + '\n', { encoding: 'utf8', mode: 0o600 });
try { await chmod(REGISTRY_PATH, 0o600); } catch {}
}
export async function upsertEntry(entry) {
if (!entry || typeof entry.label !== 'string' || entry.label.length === 0) {
throw new Error('upsertEntry: entry.label is required');
}
const registry = await loadRegistry();
registry[entry.label] = {
host: entry.host,
port: entry.port,
token: entry.token,
fp: entry.fp,
label: entry.label,
version: entry.version,
lastConnectedAt: entry.lastConnectedAt ?? new Date().toISOString(),
};
await saveRegistry(registry);
return registry[entry.label];
}
export async function getEntry(label) {
if (typeof label !== 'string' || label.length === 0) return null;
const registry = await loadRegistry();
return registry[label] ?? null;
}
export { REGISTRY_PATH };