// Based on: Ox Security // https://www.ox.security/blog/cve-2025-65717-live-server-vscode-vulnerability/ // Modified by natsu let origin = ''; const visited = new Set(); function joinPath(basePath, relative) { if (relative.startsWith('http')) return relative; if (relative.startsWith('/')) return origin + relative; if (basePath.endsWith('/')) return basePath + relative; return basePath + '/' + relative; } function report(path) { const container = document.getElementById('results'); const details = document.createElement('details'); const summary = document.createElement('summary'); summary.textContent = decodeURI(path); details.appendChild(summary); details.addEventListener('toggle', async () => { const res = await fetch(path); const contentType = res.headers.get('content-type') || ''; const pre = document.createElement('pre'); if (contentType.startsWith('text/')) { pre.textContent = await res.text(); } else { pre.textContent = `(binary file: ${contentType || 'unknown'})`; } details.appendChild(pre); }, { once: true }); container.appendChild(details); } async function crawl(path) { if (visited.has(path)) return; visited.add(path); try { const res = await fetch(path); const text = await res.text(); report(path); if (res.headers.get('content-type')?.includes('text/html')) { const parser = new DOMParser(); const doc = parser.parseFromString(text, 'text/html'); const links = Array.from(doc.querySelectorAll('a')) .map(a => a.getAttribute('href')) .filter(h => h && h !== '#'); for (const link of links) { const nextPath = joinPath(path, link); await crawl(nextPath); } } } catch (e) { // fail silently } } async function scanPorts() { const container = document.getElementById('scan-results'); const status = document.getElementById('scan-status'); const minPort = parseInt(document.getElementById('scan-min').value) || 5000; const maxPort = parseInt(document.getElementById('scan-max').value) || 6000; const total = maxPort - minPort + 1; let scanned = 0; let found = 0; // Remove previous scan results container.querySelectorAll('.port-item').forEach(el => el.remove()); async function probe(port) { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 12000); try { await fetch(`http://localhost:${port}/`, { signal: controller.signal, mode: 'no-cors', }); const portItem = document.createElement('li'); portItem.className = 'port-item'; portItem.textContent = port + ' '; const btn = document.createElement('button'); btn.textContent = 'crawl'; btn.addEventListener('click', () => startCrawl(port)); portItem.appendChild(btn); container.appendChild(portItem); found++; } catch (e) { // port closed or timed out } finally { clearTimeout(timeout); scanned++; status.textContent = `Scanning... ${scanned}/${total}`; } } const ports = Array.from({ length: total }, (_, i) => minPort + i); const batchSize = 100; for (let i = 0; i < ports.length; i += batchSize) { await Promise.all(ports.slice(i, i + batchSize).map(p => probe(p))); } status.textContent = `Done (${found} port${found !== 1 ? 's' : ''} found)`; } document.getElementById('scan-btn').addEventListener('click', () => scanPorts()); async function startCrawl(port) { visited.clear(); const results = document.getElementById('results'); results.innerHTML = ''; origin = `http://localhost:${port}`; await crawl(origin + '/'); if (!results.childElementCount) { results.textContent = 'No results found.'; } } document.getElementById('crawl-btn').addEventListener('click', () => { const port = document.getElementById('port-input').value; if (!port) return; startCrawl(port); });