{"record":{"id":"ed0f16d16cb83a3e","repo":"microsoft/semantic-kernel","slug":"the-request-uri-url-is-not-allowed-it-does-no","errorCode":null,"errorMessage":"The request URI '{url}' is not allowed. It does not match any of the allowed base URLs.","messagePattern":"The request URI '(.+?)' is not allowed\\. It does not match any of the allowed base URLs\\.","errorType":"exception","errorClass":"FunctionExecutionException","httpStatus":null,"severity":"error","filePath":"python/semantic_kernel/connectors/openapi_plugin/server_url_validator.py","lineNumber":50,"sourceCode":"async def validate_server_url(\n    url: str,\n    options: ServerUrlValidationOptions | None = None,\n    dns_resolver: DnsResolver | None = None,\n) -> None:\n    \"\"\"Validate a fully resolved OpenAPI operation URL against the supplied policy.\"\"\"\n    options = options or ServerUrlValidationOptions()\n    try:\n        parsed_url = _parse_absolute_url(url)\n    except ValueError as exc:\n        raise FunctionExecutionException(\n            f\"The request URI '{url}' is not allowed because it is not a valid absolute URI.\"\n        ) from exc\n\n    if _matches_allowed_base_url(parsed_url, options.allowed_base_urls):\n        return\n\n    if options.allowed_base_urls:\n        raise FunctionExecutionException(\n            f\"The request URI '{url}' is not allowed. It does not match any of the allowed base URLs.\"\n        )\n\n    if parsed_url.scheme.lower() != DEFAULT_ALLOWED_SCHEME:\n        raise FunctionExecutionException(\n            f\"The request URI scheme '{parsed_url.scheme}' is not allowed. \"\n            f\"Only '{DEFAULT_ALLOWED_SCHEME}' is permitted by default. \"\n            \"To allow this URL, add it to server_url_validation_allowed_base_urls.\"\n        )\n\n    if options.allow_private_network_access:\n        return\n\n    await _ensure_public_host(parsed_url, dns_resolver)\n\n\ndef try_categorize_non_public_address(\n    address: str | ipaddress.IPv4Address | ipaddress.IPv6Address,","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/python/semantic_kernel/connectors/openapi_plugin/server_url_validator.py#L32-L68","documentation":"Thrown by validate_server_url when you have configured a non-empty allowed_base_urls allow-list but the fully resolved request URL does not match any entry. Matching requires scheme, hostname, port, and path-prefix to all align (host/path compared case-insensitively). This is the explicit allow-list enforcement path of the OpenAPI plugin SSRF guard.","triggerScenarios":"An OpenAPI plugin operation resolves to a URL outside every configured base. Example: allowed_base_urls=['https://api.example.com/v1'] but an operation's server URL is https://api.other.com/v1/foo, or the path lies outside the /v1 prefix, or the scheme/port differ.","commonSituations":"OpenAPI spec lists a server URL that differs from the allow-listed base; a new endpoint lives on a different domain/subdomain; the allowed base URL was mistyped or is missing its path prefix; port mismatch (base on 443, request on 8443).","solutions":["Add the operation's exact base URL (scheme + host + port + path prefix) to ServerUrlValidationOptions.allowed_base_urls or the server_url_validation_allowed_base_urls setting","Verify scheme, hostname, port, and path-prefix all match an allowed entry; host and path matching is case-insensitive","Confirm the OpenAPI spec's servers entry matches what you allow-listed","If the destination is legitimately trusted and private access is intended, set allow_private_network_access=True instead of widening the allow-list carelessly"],"exampleFix":"# before\noptions = ServerUrlValidationOptions(allowed_base_urls=['https://api.example.com'])\nawait validate_server_url('https://api.example.com/v2/search', options)  # path /v2 not allowed\n\n# after\noptions = ServerUrlValidationOptions(allowed_base_urls=['https://api.example.com'])\nawait validate_server_url('https://api.example.com/v2/search', options)  # base path '/' matches\n# or, allow only the v2 API:\noptions = ServerUrlValidationOptions(allowed_base_urls=['https://api.example.com/v2'])","handlingStrategy":"validation","validationCode":"from urllib.parse import urlparse\n\ndef url_matches_allowed(url: str, allowed_base_urls: list[str]) -> bool:\n    def parts(u: str):\n        p = urlparse(u)\n        port = p.port or (443 if p.scheme.lower() == 'https' else 80 if p.scheme.lower() == 'http' else None)\n        return p.scheme.lower(), (p.hostname or '').lower(), port, (p.path or '/')\n    u = parts(url)\n    for base in allowed_base_urls:\n        b = parts(base)\n        if u[0] == b[0] and u[1] == b[1] and u[2] == b[2] and (u[3] == b[3] or u[3].startswith(b[3].rstrip('/') + '/')):\n            return True\n    return False\n\n# call before validate_server_url\nif not url_matches_allowed(op_url, options.allowed_base_urls):\n    raise ValueError(f'{op_url} not in allow-list')","typeGuard":null,"tryCatchPattern":"from semantic_kernel.exceptions.function_exceptions import FunctionExecutionException\n\ntry:\n    await validate_server_url(url, options)\nexcept FunctionExecutionException as e:\n    if 'does not match any of the allowed base URLs' in str(e):\n        # add url base to options.allowed_base_urls or reject the operation\n        ...","preventionTips":["Maintain allowed_base_urls as the complete scheme+host+port+path-prefix of every legitimate destination","When adding a new OpenAPI endpoint on a new host, update the allow-list in the same change","Compare host and path case-insensitively when configuring the allow-list"],"tags":["openapi","ssrf","url-validation","security","config"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}