Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- Cursor: show the Safari Full Disk Access recovery hint before the long browser login list so permission guidance remains visible when menu errors truncate (#1419, fixes #1417). Thanks @hhh2210!
- Cursor: present legacy request-based plans as one Requests quota with the raw used/limit count instead of unrelated token-based Auto/API bars (#1420, fixes #1418). Thanks @hhh2210!
- Cost usage: memoize Codex priority-turn trace metadata incrementally so warm refreshes scan only appended rows instead of rescanning large trace databases (#1404). Thanks @ProspectOre!
- Security: reject insecure or malformed MiniMax and Alibaba endpoint overrides while preserving valid custom HTTPS deployments (#1269). Thanks @Hinotoi-agent!

## 0.33.0 — 2026-06-11

Expand Down
137 changes: 137 additions & 0 deletions Sources/CodexBarCore/ProviderEndpointOverrideValidator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import Foundation

enum ProviderEndpointOverrideError: LocalizedError, Sendable, Equatable {
case minimax(String)
case alibabaCodingPlan(String)

var errorDescription: String? {
switch self {
case let .minimax(key):
"MiniMax endpoint override \(key) is not allowed. " +
"Use an HTTPS endpoint without user info or encoded host tricks. " +
"If MINIMAX_REQUIRE_PROVIDER_ENDPOINT_OVERRIDES=true is set, the endpoint must also be MiniMax-owned."
case let .alibabaCodingPlan(key):
"Alibaba Coding Plan endpoint override \(key) is not allowed. " +
"Use an HTTPS endpoint without user info or encoded host tricks. " +
"If ALIBABA_CODING_PLAN_REQUIRE_PROVIDER_ENDPOINT_OVERRIDES=true is set, " +
"the endpoint must also be Alibaba-owned."
}
}
}

struct ProviderEndpointOverrideValidator: Sendable {
enum HostPolicy: Sendable {
case allowAnyHTTPSHost
case providerOwnedOnly
}

private let allowedHosts: Set<String>
private let allowedDomainSuffixes: Set<String>

init(allowedHosts: [String] = [], allowedDomainSuffixes: [String] = []) {
self.allowedHosts = Set(allowedHosts.map { $0.lowercased() })
self.allowedDomainSuffixes = Set(allowedDomainSuffixes.map { $0.lowercased() })
}

func validatedHost(_ raw: String?, policy: HostPolicy = .allowAnyHTTPSHost) -> String? {
guard let raw,
let url = self.url(from: raw),
let host = self.validatedDecodedHost(for: url, policy: policy)
else { return nil }
return self.hostAuthority(host: host, port: url.port)
}

func validatedURL(_ raw: String?, policy: HostPolicy = .allowAnyHTTPSHost) -> URL? {
guard let raw,
let url = self.url(from: raw),
self.validatedDecodedHost(for: url, policy: policy) != nil
else { return nil }
return url
}

static func normalizedHTTPSURL(from raw: String) -> URL? {
let url = if Self.hasExplicitURLScheme(raw) {
URL(string: raw)
} else {
URL(string: "https://\(raw)")
}
guard let url else { return nil }
guard let scheme = url.scheme?.lowercased(), scheme == "https" else { return nil }
guard url.user == nil, url.password == nil else { return nil }
guard url.host(percentEncoded: false) != nil else { return nil }
return url
}

private func url(from raw: String) -> URL? {
Self.normalizedHTTPSURL(from: raw)
}

private static func hasExplicitURLScheme(_ raw: String) -> Bool {
guard let colonIndex = raw.firstIndex(of: ":") else { return false }
if raw[colonIndex...].hasPrefix("://") { return true }

if let authorityEnd = raw.firstIndex(where: { ["/", "?", "#"].contains($0) }),
colonIndex > authorityEnd
{
return false
}

let afterColon = raw.index(after: colonIndex)
guard afterColon < raw.endIndex else { return true }
let portEnd = raw[afterColon...].firstIndex { Set<Character>(["/", "?", "#"]).contains($0) } ?? raw.endIndex
let suffix = raw[afterColon..<portEnd]
if !suffix.isEmpty, suffix.allSatisfy(\.isNumber) {
return false
}

let scheme = raw[..<colonIndex]
guard let first = scheme.first, first.isLetter else { return false }
return scheme.dropFirst().allSatisfy { $0.isLetter || $0.isNumber || ["+", "-", "."].contains($0) }
}

private func hostAuthority(host: String, port: Int?) -> String {
let authorityHost = host.contains(":") ? "[\(host)]" : host
guard let port else { return authorityHost }
return "\(authorityHost):\(port)"
}

private func validatedDecodedHost(for url: URL, policy: HostPolicy) -> String? {
guard let decodedHost = url.host(percentEncoded: false)?.lowercased(),
!decodedHost.isEmpty,
!decodedHost.contains("%"),
let encodedHost = url.host(percentEncoded: true)?.lowercased(),
self.hostHasNoEncodedDelimiters(encodedHost, decodedHost: decodedHost, url: url)
else { return nil }

switch policy {
case .allowAnyHTTPSHost:
return decodedHost
case .providerOwnedOnly:
let isAllowedHost = self.allowedHosts.contains(decodedHost)
let isAllowedSuffix = self.allowedDomainSuffixes.contains { suffix in
decodedHost == suffix || decodedHost.hasSuffix(".\(suffix)")
}
guard isAllowedHost || isAllowedSuffix else { return nil }
return decodedHost
}
}

private func hostHasNoEncodedDelimiters(_ encodedHost: String, decodedHost: String, url: URL) -> Bool {
if decodedHost.contains(":") {
guard encodedHost == decodedHost,
let componentHost = URLComponents(url: url, resolvingAgainstBaseURL: false)?.host,
componentHost.hasPrefix("["),
componentHost.hasSuffix("]")
else { return false }

let address = componentHost.dropFirst().dropLast()
return !address.isEmpty && address.allSatisfy { $0.isHexDigit || $0 == ":" || $0 == "." }
}

let decodedDelimiters = CharacterSet(charactersIn: "/\\?#@:")
guard decodedHost.rangeOfCharacter(from: decodedDelimiters) == nil else { return false }

let encodedDelimiters = ["%2f", "%5c", "%3f", "%23", "%40", "%3a"]
return !encodedDelimiters.contains { encodedHost.contains($0) }
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import Foundation

public struct AlibabaCodingPlanSettingsReader: Sendable {
private static let endpointValidator = ProviderEndpointOverrideValidator(
allowedHosts: [
"modelstudio.console.alibabacloud.com",
"bailian-singapore-cs.alibabacloud.com",
"bailian.console.aliyun.com",
"bailian-cs.console.aliyun.com",
"bailian-beijing-cs.aliyuncs.com",
])

public static let apiTokenKey = "ALIBABA_CODING_PLAN_API_KEY"
public static let qwenAPITokenKey = "ALIBABA_QWEN_API_KEY"
public static let dashScopeAPITokenKey = "DASHSCOPE_API_KEY"
Expand All @@ -12,6 +21,11 @@ public struct AlibabaCodingPlanSettingsReader: Sendable {
public static let cookieHeaderKey = "ALIBABA_CODING_PLAN_COOKIE"
public static let hostKey = "ALIBABA_CODING_PLAN_HOST"
public static let quotaURLKey = "ALIBABA_CODING_PLAN_QUOTA_URL"
public static let requireProviderEndpointOverridesKey = "ALIBABA_CODING_PLAN_REQUIRE_PROVIDER_ENDPOINT_OVERRIDES"
private static let endpointOverrideKeys = [
Self.hostKey,
Self.quotaURLKey,
]

public static func apiToken(
environment: [String: String] = ProcessInfo.processInfo.environment) -> String?
Expand All @@ -25,7 +39,22 @@ public struct AlibabaCodingPlanSettingsReader: Sendable {
public static func hostOverride(
environment: [String: String] = ProcessInfo.processInfo.environment) -> String?
{
self.cleaned(environment[self.hostKey])
self.endpointValidator.validatedHost(
self.cleaned(environment[self.hostKey]),
policy: self.endpointOverrideHostPolicy(environment: environment))
}

public static func rejectedEndpointOverrideKey(
environment: [String: String] = ProcessInfo.processInfo.environment) -> String?
{
let policy = self.endpointOverrideHostPolicy(environment: environment)
return self.endpointOverrideKeys.first { key in
guard let value = self.cleaned(environment[key]) else { return false }
if key == Self.hostKey {
return self.endpointValidator.validatedHost(value, policy: policy) == nil
}
return self.endpointValidator.validatedURL(value, policy: policy) == nil
}
}

public static func cookieHeader(
Expand All @@ -37,11 +66,17 @@ public struct AlibabaCodingPlanSettingsReader: Sendable {
public static func quotaURL(
environment: [String: String] = ProcessInfo.processInfo.environment) -> URL?
{
guard let raw = self.cleaned(environment[self.quotaURLKey]) else { return nil }
if let url = URL(string: raw), url.scheme != nil {
return url
}
return URL(string: "https://\(raw)")
self.endpointValidator.validatedURL(
self.cleaned(environment[self.quotaURLKey]),
policy: self.endpointOverrideHostPolicy(environment: environment))
}

static func endpointOverrideHostPolicy(environment: [String: String]) -> ProviderEndpointOverrideValidator
.HostPolicy {
guard let value = self.cleaned(environment[self.requireProviderEndpointOverridesKey])?.lowercased(),
["1", "true", "yes", "on"].contains(value)
else { return .allowAnyHTTPSHost }
return .providerOwnedOnly
}

static func cleaned(_ raw: String?) -> String? {
Expand Down
Loading