import puppeteer from 'puppeteer'; import { BROWSER_LAUNCH_ARGS } from '../config.js'; let browser; // One shared Chromium instance, relaunched if it disconnects export async function getBrowser() { if (!browser || !browser.connected) { if (browser) await browser.close().catch(() => {}); console.log('Launching new browser instance...'); browser = await puppeteer.launch({ headless: 'new', executablePath: process.env.PUPPETEER_EXECUTABLE_PATH || undefined, args: BROWSER_LAUNCH_ARGS }); } return browser; } // Helper to block SSRF, metadata endpoints, and local file access export async function setupPageSecurity(page) { await page.setRequestInterception(true); page.on('request', (req) => { const urlStr = req.url(); try { const parsedUrl = new URL(urlStr); const protocol = parsedUrl.protocol.toLowerCase(); const hostname = parsedUrl.hostname.toLowerCase(); // Block file://, ftp://, and other non-web protocols if (protocol !== 'http:' && protocol !== 'https:' && protocol !== 'data:' && protocol !== 'blob:') { return req.abort(); } // Block cloud metadata services and local network access const blockedHosts = ['169.254.169.254', 'metadata.google.internal', 'localhost', '127.0.0.1', '0.0.0.0', '::1']; if (blockedHosts.includes(hostname) || hostname.startsWith('10.') || hostname.startsWith('192.168.') || hostname.startsWith('172.16.')) { console.warn(`[Security Block] Blocked SSRF attempt to: ${urlStr}`); return req.abort(); } req.continue(); } catch { // If URL is invalid or unparseable and not data/blob, abort if (!urlStr.startsWith('data:') && !urlStr.startsWith('blob:')) { return req.abort(); } req.continue(); } }); } // ── Startup smoke test: verify Chromium can actually launch ── export async function verifyChromiumLaunch() { try { console.log('[Startup] Testing Chromium launch...'); console.log('[Startup] PUPPETEER_EXECUTABLE_PATH =', process.env.PUPPETEER_EXECUTABLE_PATH || '(not set, using bundled)'); console.log('[Startup] PUPPETEER_CACHE_DIR =', process.env.PUPPETEER_CACHE_DIR || '(not set)'); const testBrowser = await puppeteer.launch({ headless: 'new', executablePath: process.env.PUPPETEER_EXECUTABLE_PATH || undefined, args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-gpu'] }); const version = await testBrowser.version(); console.log(`[Startup] ✅ Chromium launched successfully: ${version}`); await testBrowser.close(); } catch (err) { console.error('[Startup] ❌ Chromium launch FAILED:', err.message); console.error('[Startup] Full error:', err); } }