Files
portal/app/scripts/render-page.cjs
T
Дмитрий 6f4e6de9a3 fix(автоподбор): укрепление fetcher — TLS-проверка, SSRF-страж, без редиректов
- CURLOPT_SSL_VERIFYPEER/VERIFYHOST включены
- isSafeUrl: только http/https, блок loopback/приватных/служебных IP
- FOLLOWLOCATION выключен, протоколы ограничены HTTP/HTTPS
- render-page.cjs валидирует схему URL перед навигацией

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 10:45:14 +03:00

32 lines
1.1 KiB
JavaScript

// Usage: node render-page.cjs <url>
// Печатает JSON: { html, visiblePhones: [...] } — отрендеренный DOM и видимые tel:-номера.
const { chromium } = require('playwright');
(async () => {
const url = process.argv[2];
// Защита от SSRF/локального доступа: только http/https.
let parsed;
try {
parsed = new URL(url);
} catch (e) {
console.error('bad url');
process.exit(2);
}
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
console.error('bad scheme');
process.exit(2);
}
const browser = await chromium.launch({ headless: true });
try {
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });
const html = await page.content();
const visiblePhones = await page.$$eval('a[href^="tel:"]', els =>
els.map(e => (e.textContent || '').trim()).filter(Boolean)
);
process.stdout.write(JSON.stringify({ html, visiblePhones }));
} finally {
await browser.close();
}
})().catch(e => { console.error(String(e)); process.exit(1); });