swift-sdk/Tests/MCPTests/OAuthURLValidatorTests.swift
Maksym Mova 6132fd4b5b
V0.12.x (#215)
* fix #203 NetworkTransport race, with main-isolated variables (#206)

Co-authored-by: wti <wti@users.noreply.github.com>

* feat/auth: Authentication implementation (#205)

* feat: added authentication

* feat(auth): improvement to error handling

* feat(auth): updated errors and tests in OAuthClientRegistrar

* feat(auth): updated ci conformance package version

* feat(auth): bumped node version

* feat(auth): updated ci conformance step

* feat(auth): improvement to Linux support

* chore: added docc plugin

* chore: added spi yml

* chore: added documentation step to ci

* chore: increased timeout for test from 5 to 10m

---------

Co-authored-by: Paal Øye-Strømme <paal.o.eye@gmail.com>
Co-authored-by: wti <wti@users.noreply.github.com>
Co-authored-by: yehorsobko <yehorsobko@macpaw.com>
2026-03-24 11:05:22 +02:00

141 lines
4.9 KiB
Swift

import Foundation
import Testing
@testable import MCP
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
@Suite("OAuthURLValidator")
struct OAuthURLValidatorTests {
// MARK: - validateHTTPSOrLoopback
@Test("Accepts HTTPS URL")
func testValidateHTTPSOrLoopbackAcceptsHTTPS() throws {
try OAuthURLValidator().validateHTTPSOrLoopback(
URL(string: "https://example.com/mcp")!, context: "test")
}
@Test("Accepts loopback HTTP")
func testValidateHTTPSOrLoopbackAcceptsLoopback() throws {
try OAuthURLValidator().validateHTTPSOrLoopback(
URL(string: "http://localhost:8080/mcp")!, context: "test")
try OAuthURLValidator().validateHTTPSOrLoopback(
URL(string: "http://127.0.0.1:9000/mcp")!, context: "test")
}
@Test("Rejects remote HTTP")
func testValidateHTTPSOrLoopbackRejectsRemoteHTTP() {
#expect(throws: OAuthAuthorizationError.self) {
try OAuthURLValidator().validateHTTPSOrLoopback(
URL(string: "http://example.com/mcp")!, context: "test")
}
}
@Test("Rejects URL with fragment")
func testValidateHTTPSOrLoopbackRejectsFragment() {
#expect(throws: OAuthAuthorizationError.self) {
try OAuthURLValidator().validateHTTPSOrLoopback(
URL(string: "https://example.com/mcp#frag")!, context: "test")
}
}
// MARK: - validateAuthorizationServer
@Test("Accepts HTTPS authorization server")
func testValidateAuthorizationServerAcceptsHTTPS() throws {
try OAuthURLValidator().validateAuthorizationServer(
URL(string: "https://auth.example.com")!, context: "test")
}
@Test("Rejects HTTP authorization server by default")
func testValidateAuthorizationServerRejectsHTTP() {
#expect(throws: OAuthAuthorizationError.self) {
try OAuthURLValidator().validateAuthorizationServer(
URL(string: "http://auth.example.com")!, context: "test")
}
}
@Test("Accepts loopback HTTP when flag is set")
func testValidateAuthorizationServerAcceptsLoopbackWhenAllowed() throws {
let v = OAuthURLValidator(allowLoopbackHTTPForAuthorizationServer: true)
try v.validateAuthorizationServer(
URL(string: "http://localhost:8080")!, context: "test")
}
@Test("Rejects loopback HTTP when flag is not set")
func testValidateAuthorizationServerRejectsLoopbackWhenNotAllowed() {
#expect(throws: OAuthAuthorizationError.self) {
try OAuthURLValidator().validateAuthorizationServer(
URL(string: "http://localhost:8080")!, context: "test")
}
}
// MARK: - validateRedirectURI
@Test("Accepts HTTPS redirect URI")
func testValidateRedirectURIAcceptsHTTPS() throws {
try OAuthURLValidator().validateRedirectURI(
URL(string: "https://app.example.com/callback")!)
}
@Test("Accepts loopback HTTP redirect URI")
func testValidateRedirectURIAcceptsLoopback() throws {
try OAuthURLValidator().validateRedirectURI(
URL(string: "http://localhost:8080/callback")!)
}
@Test("Rejects remote HTTP redirect URI")
func testValidateRedirectURIRejectsRemoteHTTP() {
#expect(throws: OAuthAuthorizationError.self) {
try OAuthURLValidator().validateRedirectURI(
URL(string: "http://app.example.com/callback")!)
}
}
@Test("Rejects redirect URI with fragment")
func testValidateRedirectURIRejectsFragment() {
#expect(throws: OAuthAuthorizationError.self) {
try OAuthURLValidator().validateRedirectURI(
URL(string: "https://app.example.com/callback#section")!)
}
}
// MARK: - isPrivateIPHost
@Test("Identifies private IPv4 ranges")
func testIsPrivateIPHostIPv4() {
let v = OAuthURLValidator()
#expect(v.isPrivateIPHost("10.0.0.1"))
#expect(v.isPrivateIPHost("172.16.0.1"))
#expect(v.isPrivateIPHost("172.31.255.255"))
#expect(v.isPrivateIPHost("192.168.1.1"))
#expect(v.isPrivateIPHost("169.254.169.254"))
#expect(v.isPrivateIPHost("100.64.0.1"))
}
@Test("Does not block public IPv4 addresses")
func testIsPrivateIPHostPublicIPv4() {
let v = OAuthURLValidator()
#expect(!v.isPrivateIPHost("1.2.3.4"))
#expect(!v.isPrivateIPHost("8.8.8.8"))
#expect(!v.isPrivateIPHost("203.0.113.1"))
}
@Test("Identifies private IPv6 ULA and link-local addresses")
func testIsPrivateIPHostIPv6() {
let v = OAuthURLValidator()
#expect(v.isPrivateIPHost("fc00::1"))
#expect(v.isPrivateIPHost("fd12:3456::1"))
#expect(v.isPrivateIPHost("fe80::1"))
}
@Test("Does not block public hostnames")
func testIsPrivateIPHostPublicHostname() {
let v = OAuthURLValidator()
#expect(!v.isPrivateIPHost("example.com"))
#expect(!v.isPrivateIPHost("localhost"))
}
}