{"record":{"id":"0fe2a1873dda80c5","repo":"CoplayDev/unity-mcp","slug":"context-refusing-to-send-credentials-to-an-unex","errorCode":null,"errorMessage":"{context}: refusing to send credentials to an unexpected host in URL '{url}' (expected https://{allowedHost}).","messagePattern":"(.+?): refusing to send credentials to an unexpected host in URL '(.+?)' \\(expected https://(.+?)\\)\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"MCPForUnity/Editor/Services/AssetGen/Providers/ProviderHttp.cs","lineNumber":26,"sourceCode":"    /// <summary>\n    /// Shared HTTP-response helpers for provider adapters: read the response text (falling back to\n    /// a UTF-8 decode of the raw body) and truncate long bodies for inclusion in error messages.\n    /// </summary>\n    internal static class ProviderHttp\n    {\n        /// <summary>\n        /// Throw unless <paramref name=\"url\"/> is an absolute https URL whose host is exactly\n        /// <paramref name=\"allowedHost\"/>. Adapters route every auth-bearing request URL through\n        /// this so a malicious/MITM'd provider response (e.g. a rogue response_url) can't redirect\n        /// the API key to an attacker host. The error is scrubbed of the key.\n        /// </summary>\n        public static void RequireHost(string url, string allowedHost, string apiKey, string context)\n        {\n            if (!Uri.TryCreate(url, UriKind.Absolute, out Uri u)\n                || u.Scheme != Uri.UriSchemeHttps\n                || !string.Equals(u.Host, allowedHost, StringComparison.OrdinalIgnoreCase))\n            {\n                throw new Exception(SecretRedactor.Scrub(\n                    $\"{context}: refusing to send credentials to an unexpected host in URL '{url}' (expected https://{allowedHost}).\",\n                    apiKey));\n            }\n        }\n\n        /// <summary>Response text, falling back to a UTF-8 decode of the raw body when Text is empty.</summary>\n        public static string BodyText(HttpResult res)\n        {\n            string text = res?.Text;\n            if (string.IsNullOrEmpty(text) && res?.Body != null)\n                text = Encoding.UTF8.GetString(res.Body);\n            return text;\n        }\n\n        /// <summary>Cap a (possibly null) string at 500 chars for inclusion in an error message.</summary>\n        public static string Truncate(string s)\n        {\n            if (string.IsNullOrEmpty(s)) return string.Empty;","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/CoplayDev/unity-mcp/blob/c21bf496bca87d54e75bad048563c3adb1782081/MCPForUnity/Editor/Services/AssetGen/Providers/ProviderHttp.cs#L8-L44","documentation":"Thrown by ProviderHttp.RequireHost, a credential-leak guard. Every auth-bearing request URL is routed through it so that a malicious or MITM'd provider response (e.g. a rogue response_url or download URL) cannot redirect the API key to an attacker-controlled host. It requires an absolute https URL whose host exactly equals allowedHost; anything else throws. The message is scrubbed of the key. This is a deliberate security fail-fast, not a bug.","triggerScenarios":"A provider response embeds a download/redirect URL on a different domain than the expected provider host; a CDN host changed; an endpoint constant was edited to a wrong host; http (non-https) URL; a relative URL passed where absolute was expected.","commonSituations":"Sketchfab/Meshy moves its asset CDN to a new domain; a developer overrides an endpoint constant; a test stub returns a localhost URL; an attacker-influenced field reaches RequireHost.","solutions":["Do NOT disable or bypass this check — it prevents API-key exfiltration.","If the host change is legitimate (e.g. a new CDN), update the allowedHost passed to RequireHost for that call site to the new trusted host.","If unexpected, investigate the upstream response that produced the off-host URL — it may indicate a compromised/MITM endpoint.","Ensure the URL is absolute https and the host matches exactly (no trailing slash, correct subdomain)."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Validate the host yourself before relying on a provider-supplied URL.\nstatic bool IsTrustedHost(string url, string allowed)\n    => Uri.TryCreate(url, UriKind.Absolute, out var u)\n       && u.Scheme == Uri.UriSchemeHttps\n       && string.Equals(u.Host, allowed, StringComparison.OrdinalIgnoreCase);","typeGuard":"static bool IsSafeCredentialUrl(string url, string allowedHost)\n    => Uri.TryCreate(url, UriKind.Absolute, out var u)\n       && u.Scheme == Uri.UriSchemeHttps\n       && u.Host.Equals(allowedHost, StringComparison.OrdinalIgnoreCase);","tryCatchPattern":"// This is a security fail-fast — do NOT swallow silently. Investigate the source of the URL.\ntry { ProviderHttp.RequireHost(url, allowedHost, apiKey, ctx); }\ncatch (Exception) { McpLog.Error($\"Blocked credential send to off-host URL: {url}\"); throw; }","preventionTips":["Never bypass RequireHost; update allowedHost to the new trusted CDN host if a provider legitimately moves.","Treat any off-host URL from a provider response as suspicious until confirmed legitimate.","Keep endpoint/CDN host constants in one place so a migration is a single edit."],"tags":["security","ssrf","credential-leak","http","asset-gen","fail-fast"],"backgroundTag":null,"analyzedSha":"c21bf496bca87d54e75bad048563c3adb1782081","analyzedAt":"2026-08-13T17:36:56.095Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}