/* ============================================================================ * p2p-admit.js — the ONLY way a content id enters p2p_content * (docs/p2p-architecture.md, "Security rules"). * * Callers must already have: the complete file on the server's own disk, its * SHA-256 computed BY THE SERVER, and validateMedia() passed. This adds the * optional malware scan (P2P_MALWARE_SCAN=1, off by default) and writes the * row. The scan command gets the path as its last argument: exit 0 = clean, * 1 = infected (rejected), anything else = scanner error (not admitted now). * ========================================================================== */ import { spawn } from 'node:child_process'; export function scanFile(path, cmd) { const parts = String(cmd).split(/\s+/).filter(Boolean); return new Promise((resolve) => { let child; try { child = spawn(parts[0], [...parts.slice(1), path], { stdio: ['ignore', 'pipe', 'pipe'] }); } catch (e) { resolve({ result: 'error', detail: e.message }); return; } let out = ''; child.stdout.on('data', (d) => { out = (out + d).slice(-2000); }); child.stderr.on('data', (d) => { out = (out + d).slice(-2000); }); child.on('error', (e) => resolve({ result: 'error', detail: e.message })); child.on('close', (code) => resolve( code === 0 ? { result: 'clean' } : code === 1 ? { result: 'infected', detail: out.trim() } : { result: 'error', detail: out.trim() || 'exit ' + code }, )); }); } const CID_RE = /^[0-9a-f]{64}$/; // info: { path, cid, videoId, size, height, vcodec, acodec, duration, meta, origin } // deps: { cfg (P2P config), upsertContent, scan = scanFile, now = Date.now, log = console } // → { ok: true, scan } | { ok: false, reason } export async function admitFile(info, deps) { const { cfg, upsertContent, scan = scanFile, now = Date.now, log = console } = deps; if (!cfg.enabled) return { ok: false, reason: 'p2p disabled' }; if (!CID_RE.test(String(info.cid || ''))) return { ok: false, reason: 'bad cid' }; let scanResult = 'skipped'; if (cfg.malwareScan) { const r = await scan(info.path, cfg.scanCmd); if (r.result !== 'clean') { log.warn?.(`[p2p] ${info.videoId} ${info.cid.slice(0, 12)} not admitted: scan ${r.result} ${r.detail || ''}`); return { ok: false, reason: 'scan ' + r.result }; } scanResult = 'clean'; } await upsertContent({ cid: info.cid, videoId: info.videoId, size: info.size, height: info.height, vcodec: info.vcodec, acodec: info.acodec, duration: info.duration, meta: info.meta || {}, origin: info.origin, scan: scanResult, now: now(), }); return { ok: true, scan: scanResult }; }