T09 · Insecure Skill Coding Practices
Warning
- Location
- src/core.mjs:13
- Finding
- Same-Origin Redirect Validation Bypass Enables Server-Side Request Forgery## Vulnerability Details **File Location**: `src/core.mjs:8-20`, `src/core.mjs:71-73`, and `src/core.mjs:79-84` **Vulnerability Type**: Server-Side Request Forgery through unchecked redirects **Risk Level**: Medium ### Complete Code Snippets ```javascript export async function fetchEvidence(url, { timeoutMs = 10_000, signal } = {}) { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeoutMs); const abort = () => controller.abort(); signal?.addEventListener('abort', abort, { once: true }); try { const response = await fetch(url, { redirect: 'follow', signal: controller.signal, headers: { accept: 'text/html,application/xhtml+xml,text/plain;q=0.9,*/*;q=0.1', 'user-agent': 'AIWebsiteCheck/0.1 (+read-only assessment)' } }); const contentType = response.headers.get('content-type') || ''; const body = await response.text(); return { url: response.url, requestedUrl: url, status: response.status, ok: response.ok, contentType, body: body.slice(0, MAX_BODY_BYTES), truncated: body.length > MAX_BODY_BYTES }; } catch (error) { return { url, requestedUrl: url, status: null, ok: false, contentType: '', body: '', error: error.name === 'AbortError' ? 'timeout' : error.message }; } finally { clearTimeout(timer); signal?.removeEventListener('abort', abort); } } ``` ```javascript const page = await fetchEvidence(target.href, options); const root = new URL(page.url || target.href).origin; const [robots, sitemap, llms] = await Promise.all(['robots.txt', 'sitemap.xml', 'llms.txt'].map((path) => fetchEvidence(new URL(`/${path}`, root).href, options))); ``` ```javascript export async function runJourney(baseUrl, definition, options = {}) { if (!definition || !Array.isArray(definition.steps) || !definition.steps.length) throw new Error('Journey JSON must contain a non-empty steps array.'); const base = new URL(baseUrl); const step ...[truncated 3208 chars]
- Remediation
- ## Remediation Suggestions 1. Set `redirect: 'manual'` and implement an explicit redirect loop with a strict maximum redirect count. 2. Resolve each `Location` value relative to the current URL and validate every redirect before issuing the next request. 3. For journey mode, require every redirect destination to retain the origin authorized by the original `baseUrl`, rather than validating only the initial step. 4. For scan mode, decide whether redirects are allowed. If allowed, clearly define whether discovery requests remain bound to the original origin or require explicit authorization for the final origin. 5. Resolve destination hostnames and reject loopback, link-local, private, multicast, unspecified, and other restricted IP ranges when those destinations are outside the intended assessment scope. 6. Revalidate after DNS resolution and on every redirect to reduce DNS rebinding and redirect-chain bypass risks. Apply equivalent checks to IPv4, IPv6, and IPv4-mapped IPv6 addresses. 7. Add tests covering public-to-private redirects, cross-origin journey redirects, multi-hop redirect chains, IPv6 loopback, and redirects that change origin before discovery requests are generated.
