T09 · Insecure Skill Coding Practices
- Location
SKILL.md:40- Finding
Unrestricted URL Fetching Enables Server-Side Request Forgery
- Content
View full analysis
urllib.request.Request: """Convert to a urllib request object.""" data = None if self.body and self.method in ("POST", "PUT", "PATCH"): data = self.body.encode("utf-8") if isinstance(self.body, str) else self.body if "Content-Type" not in self.headers: self.headers["Content-Type"] = self.content_type req = urllib.request.Request( self.url, data=data, headers=self.headers, method=self.method ) return req ``` ```python def send(self, request: APIRequest) -> APIResponse: """Send a single API request.""" resp = APIResponse() start = time.time() try: req = request.to_urllib_request() with urllib.request.urlopen(req, timeout=request.timeout) as conn: ``` The same unrestricted request primitive is exposed through the benchmark and GraphQL functions: ```python def benchmark(self, url: str, count: int = 5, method: str = "GET") -> dict: latencies = [] errors = 0 for i in range(count): req = APIRequest(method=method, url=url) resp = self.send(req) ``` ```python def graphql(self, endpoint: str, query: str, variables: dict = None) -> APIResponse: body = json.dumps({"query": query, "variables": variables or {}}) req = APIRequest( method="POST", url=endpoint, body=body, content_type="application/json" ) return self.send(req) ``` ### Technical Analysis The request URL is passed directly to `urllib.request.Request` and `urllib.request.urlopen` without validating its scheme, hostname, resolved IP address, port, or redirect destination. No restrictions prevent access to loopback, private, link-local, reserved, or cloud me ...[truncated 1828 chars]- Remediation
View remediation
