(url, maxChars)
| 191 | } |
| 192 | |
| 193 | async function _fetchSimple(url, maxChars) { |
| 194 | try { |
| 195 | const response = await fetch(url, { |
| 196 | headers: { 'User-Agent': 'Mozilla/5.0 (compatible; SmallCode/0.4.0)' }, |
| 197 | timeout: 10000, |
| 198 | redirect: 'manual', // Don't auto-follow — a 30x to 169.254.169.254 would bypass the SSRF guard |
| 199 | }); |
| 200 | if (response.status >= 300 && response.status < 400) { |
| 201 | return `Refused: redirect to ${response.headers.get('location') || '(unknown)'} not followed`; |
| 202 | } |
| 203 | const html = await response.text(); |
| 204 | // Strip HTML tags for readable text |
| 205 | const text = html.replace(/<script[\s\S]*?<\/script>/gi, '') |
| 206 | .replace(/<style[\s\S]*?<\/style>/gi, '') |
| 207 | .replace(/<[^>]+>/g, ' ') |
| 208 | .replace(/\s+/g, ' ') |
| 209 | .trim(); |
| 210 | return sanitizeToolOutput(text).slice(0, maxChars); |
| 211 | } catch (e) { |
| 212 | return `Failed to fetch ${url}: ${e.message}`; |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | module.exports = { webSearch, webFetch, closeBrowser, loadPlaywright }; |
| 217 |
no test coverage detected