T09 · Insecure Skill Coding Practices
- Location
src/core.mjs:13- Finding
Same-Origin Redirect Validation Bypass Enables Server-Side Request Forgery
- Content
View full analysis
Vulnerability Details
File Location:
src/core.mjs:8-20,src/core.mjs:71-73, andsrc/core.mjs:79-84
Vulnerability Type: Server-Side Request Forgery through unchecked redirects
Risk Level: MediumComplete 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
View remediation
Remediation Suggestions
- Set
redirect: 'manual'and implement an explicit redirect loop with a strict maximum redirect count. - Resolve each
Locationvalue relative to the current URL and validate every redirect before issuing the next request. - For journey mode, require every redirect destination to retain the origin authorized by the original
baseUrl, rather than validating only the initial step. - 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.
- 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.
- 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.
- 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.
- Set
