{"record":{"id":"43a72ccd2626c15b","repo":"microsoft/semantic-kernel","slug":"the-operation-path-resolves-to-requestauthority","errorCode":null,"errorMessage":"The operation path resolves to '{requestAuthority}', which does not match the configured server '{serverAuthority}'.","messagePattern":"The operation path resolves to '(.+?)', which does not match the configured server '(.+?)'\\.","errorType":"exception","errorClass":"KernelException","httpStatus":null,"severity":"critical","filePath":"dotnet/src/Functions/Functions.OpenApi/Model/RestApiOperation.cs","lineNumber":195,"sourceCode":"\n    /// <summary>\n    /// Verifies that URI construction did not move the request off the configured server. A selected\n    /// operation path must resolve to a request on the same scheme, host, and port and within the\n    /// server's base path. Otherwise an absolute or authority-changing operation path (for example\n    /// \"https://another-host/admin\") could redirect a credential-bearing request to an unintended\n    /// target even though it carries no dot-segment. This complements <see cref=\"ValidatePathSegments\"/>\n    /// so operation selection, path validation, and request construction share one canonical target.\n    /// </summary>\n    /// <param name=\"serverUrl\">The configured server URL.</param>\n    /// <param name=\"requestUrl\">The request URL produced by combining the server URL and operation path.</param>\n    private static void EnsureRequestTargetMatchesServer(Uri serverUrl, Uri requestUrl)\n    {\n        var serverAuthority = serverUrl.GetLeftPart(UriPartial.Authority);\n        var requestAuthority = requestUrl.GetLeftPart(UriPartial.Authority);\n\n        if (!string.Equals(serverAuthority, requestAuthority, StringComparison.OrdinalIgnoreCase))\n        {\n            throw new KernelException($\"The operation path resolves to '{requestAuthority}', which does not match the configured server '{serverAuthority}'.\");\n        }\n\n        // GetServerUrl guarantees a trailing slash, so the server's base path always ends with '/'.\n        var basePath = serverUrl.AbsolutePath;\n        var requestPath = requestUrl.AbsolutePath;\n\n        if (!string.Equals(requestPath, basePath.TrimEnd('/'), StringComparison.Ordinal) &&\n            !requestPath.StartsWith(basePath, StringComparison.Ordinal))\n        {\n            throw new KernelException($\"The operation path resolves to '{requestPath}', which is outside the configured server base path '{basePath}'.\");\n        }\n    }\n\n    /// <summary>\n    /// Builds operation request headers.\n    /// </summary>\n    /// <param name=\"arguments\">The operation arguments.</param>\n    /// <returns>The request headers.</returns>","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/dotnet/src/Functions/Functions.OpenApi/Model/RestApiOperation.cs#L177-L213","documentation":"SSRF-prevention guard in RestApiOperation.BuildOperationUrl. After combining the server URL with the operation path via new Uri(serverUrl, path), EnsureRequestTargetMatchesServer compares the authority (scheme://host:port) of the resulting request URL against the configured server's authority. If they differ, the operation path has redirected the request to another host, which is rejected so a credential-bearing request cannot be sent to an unintended target.","triggerScenarios":"An OpenAPI operation path that is itself an absolute URL (e.g. path 'https://evil.com/admin') or contains a scheme://host prefix that overrides the server base. Also a path beginning with '//' (protocol-relative) that a browser-style resolver would interpret against a different host. Any case where Uri(serverUrl, path) yields a different authority than serverUrl.","commonSituations":"An OpenAPI document authored with absolute paths instead of relative ones; a spec scraped/converted by a tool that emitted full URLs in the path field; a malicious or buggy spec whose paths escape the declared server.","solutions":["Edit the OpenAPI document so operation paths are relative (start without a scheme/host), e.g. /users/{id} rather than https://host/users/{id}.","If the operation legitimately targets a different host, declare that host in the document's servers array so the authority matches.","Pre-validate the spec: reject any path where new Uri(serverUrl, path).Authority != serverUrl.Authority before importing.","Confirm the spec is from a trusted source; this guard exists precisely because untrusted specs are dangerous."],"exampleFix":"// before - absolute path in the spec redirects off the server\n\"paths\": { \"https://evil.com/admin\": { } }\n\n// after - relative path stays within the configured server\n\"servers\": [ { \"url\": \"https://api.example.com\" } ],\n\"paths\": { \"/admin\": { } }","handlingStrategy":"validation","validationCode":"foreach (var pathKey in doc.Paths.Keys)\n{\n    var resolved = new Uri(serverUrl, pathKey.TrimStart('/'));\n    if (!string.Equals(resolved.GetLeftPart(UriPartial.Authority), serverUrl.GetLeftPart(UriPartial.Authority), StringComparison.OrdinalIgnoreCase))\n        throw new InvalidOperationException($\"Path '{pathKey}' redirects off server authority.\");\n}","typeGuard":"static bool PathStaysOnServer(Uri serverUrl, string opPath)\n{\n    var resolved = new Uri(serverUrl, opPath.TrimStart('/'));\n    return string.Equals(resolved.GetLeftPart(UriPartial.Authority), serverUrl.GetLeftPart(UriPartial.Authority), StringComparison.OrdinalIgnoreCase);\n}","tryCatchPattern":"try { var url = operation.BuildOperationUrl(arguments, serverUrlOverride, apiHostUrl); }\ncatch (KernelException ex) when (ex.Message.Contains(\"does not match the configured server\"))\n{ logger.LogWarning(\"Operation path escapes server authority; rejecting spec path.\"); throw; }","preventionTips":["Only import OpenAPI documents from trusted sources.","Keep operation paths relative, never absolute URLs.","Pre-scan paths against the declared server authority."],"tags":["openapi","ssrf","rest-operation","security","server-url"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}