T09 · Insecure Skill Coding Practices
- Location
scripts/osu-fetch.mjs:93- Finding
Unrestricted Outbound HTTP Requests Enable SSRF
- Content
View full analysis
Vulnerability Details
File Location:
scripts/osu-fetch.mjs, lines 93–107 and 150–154
Vulnerability Type: Server-Side Request Forgery (SSRF) through an unrestricted user-supplied URL
Risk Level: MediumVulnerable Code
js function buildUrl({ url, service, path, params }) { let finalUrl = url; if (!finalUrl) { if (!service || !path) { throw new Error("Provide either <url> or both --service and --path."); } const base = SERVICE_BASE_URLS[service]; if (!base) { throw new Error(`Unknown service '${service}'. Use --help to see valid services.`); } const baseWithSlash = base.endsWith("/") ? base : `${base}/`; const normalizedPath = path.startsWith("/") ? path.slice(1) : path; finalUrl = new URL(normalizedPath, baseWithSlash).toString(); } else if (!finalUrl.startsWith("http://") && !finalUrl.startsWith("https://")) { throw new Error(`URL must start with http(s):// (got ${finalUrl})`); } const u = new URL(finalUrl);The resulting URL is then requested without destination validation:
js res = await fetch(url, { method: "GET", headers: { accept: "application/json" }, signal: controller.signal, });Technical Analysis
The positional URL mode accepts any destination whose string begins with
http://orhttps://. It does not enforce the documentedcontent.osu.eduboundary or reject loopback, private-network, link-local, cloud metadata, embedded-credential, or attacker-controlled destinations.Consequently, anyone able to influence the helper's arguments can cause a request to be issued from the agent's network context. Repeatable
--paramvalues are also appended to the selected URL, allowing caller-supplied data to be transmitted to an arbitrary destination.This behavior exceeds the minimum privileges declared in
SKILL.md, which describes access to public Ohio State University APIs hosted atcontent.osu.edu. No evidence was found that the ...[truncated 1399 chars]- Remediation
View remediation
Remediation Suggestions
- Remove arbitrary full-URL mode if it is unnecessary. Accept only a predefined service name and a validated relative API path.
- If full URLs must remain supported, require
https:and enforce an exact hostname allowlist containing onlycontent.osu.edu. - Reject URLs containing embedded usernames or passwords, unexpected ports, or non-empty fragments.
- Resolve destination addresses and reject loopback, private, link-local, multicast, reserved, and cloud metadata address ranges for both IPv4 and IPv6.
- Disable automatic redirects or validate every redirect target against the same protocol, hostname, port, and address restrictions.
- Prevent path-based origin changes by validating the parsed URL after resolution rather than relying on string-prefix checks.
- Avoid placing sensitive values in query parameters because URLs may appear in logs and error messages.
- Add automated tests covering destinations such as
localhost,127.0.0.1,[::1], private RFC1918 addresses, link-local addresses, metadata endpoints, alternate ports, embedded credentials, and off-domain redirects.
A minimal hostname restriction should validate the parsed URL before calling
fetch():js const u = new URL(finalUrl); if (u.protocol !== "https:" || u.hostname !== "content.osu.edu") { throw new Error("Only HTTPS requests to content.osu.edu are allowed."); } if (u.username || u.password || (u.port && u.port !== "443")) { throw new Error("URL credentials and nonstandard ports are not allowed."); }Redirect validation and IP-range filtering should also be applied before considering the request boundary secure.
