import { posix } from 'node:path'; import { gameOfServer, relDestForKind, type ArtifactKind } from '../config.js'; export const BANK_CONFIG_FILE = 'electric_cherry_bank.cfg'; export function isPrivateConfigPath(path: string, game = 't5'): boolean { return ( posix.normalize(path.replaceAll('\\', '/')) === posix.join(relDestForKind('config', game), BANK_CONFIG_FILE) ); } export function isPrivateConfigTarget(server: string, kind: string, path: string): boolean { if (kind !== 'script' && kind !== 'config' && kind !== 'baseline' && kind !== 'plugin') throw new Error('Invalid artifact kind'); const game = gameOfServer(server); return isPrivateConfigPath( posix.join(relDestForKind(kind, game), path.replaceAll('\\', '/')), game ); } export function assertPublicFileTarget(server: string, kind: ArtifactKind, path: string): void { if (isPrivateConfigTarget(server, kind, path)) throw new Error( 'Private bank configuration cannot be captured or changed by a file campaign.' ); } export function isReservedConfigFilename(path: string): boolean { return posix.basename(path.replaceAll('\\', '/')).toLowerCase() === BANK_CONFIG_FILE; } // Legacy campaign evidence omits the kind and stores the path relative to its destination. export function isPrivateConfigEvidencePath(path: string): boolean { return ( posix.normalize(path.replaceAll('\\', '/')) === BANK_CONFIG_FILE || isPrivateConfigPath(path) ); } /** Credential injection uses bank.configPath, never a shared file's path field. */ export class PrivateFileOperationError extends Error { constructor() { super('Private bank configuration cannot be read or changed by file operations.'); } } export function assertPublicFilePayload(value: unknown): void { if (!value || typeof value !== 'object') return; if ('path' in value && typeof value.path === 'string' && isPrivateConfigPath(value.path)) throw new PrivateFileOperationError(); for (const item of Object.values(value)) assertPublicFilePayload(item); }