T09 · Insecure Skill Coding Practices
- Location
moodle_connector.py:513- Finding
Moodle authentication token disclosed to caller-controlled network destinations
- Content
View full analysis
Path: """ Download a Moodle file (PDF, doc, etc.) with local caching. Returns the local path. """ # Build a stable filename from the URL url_hash = hashlib.sha256(file_url.encode()).hexdigest()[:16] # Try to get a meaningful name from the URL url_name = file_url.split("/")[-1].split("?")[0] or "file" filename = f"{url_hash}_{url_name}" if dest is None: dest = CACHE_DIR / "files" / filename dest.parent.mkdir(parents=True, exist_ok=True) if dest.exists(): log.debug("File already cached: %s", dest) return dest # Append token if not already present sep = "&" if "?" in file_url else "?" url_with_token = f"{file_url}{sep}token={self.token}" log.info("Downloading: %s → %s", file_url, dest.name) try: with self.session.get(url_with_token, stream=True, timeout=60) as r: ``` The unrestricted URL is reachable through the public connector method: ```python def download(self, url: str, output: Optional[str] = None) -> str: dest = Path(output) if output else None path = self.api.download_file(url, dest) ``` The MCP interface permits its client to supply that URL: ```python elif name == "download": url = arguments.get("url") output = arguments.get("output") result = connector.download(url, output) ``` ### Technical Analysis The downloader accepts a caller-controlled URL without parsing its scheme, hostname, port, or path. It unconditionally adds the active Moodle web-service token to the query string and sends the request. Consequently, a caller can direct ...[truncated 1715 chars]- Remediation
View remediation
