mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-19 01:21:42 +00:00
* feat(ios): support per-gateway custom headers for proxied gateway connections Per-gateway custom request headers (Cloudflare Access-style service tokens) for gateways behind authenticating reverse proxies. Headers are credentials: Keychain-stored per gateway stableID, masked in settings UI, injected on the WebSocket upgrade at connect time via a provider so edits apply on the next reconnect without re-pairing. Reserved handshake headers and control chars are sanitized out. TLS pinning and pairing flows unchanged. Push relay and other non-gateway destinations proven header-free by regression test. Related: #100698 * chore(ios): sync native i18n inventory for custom header strings * fix(ios): harden gateway custom headers * chore(ios): resync native i18n inventory * fix(ios): validate custom header names
54 lines
1.8 KiB
Swift
54 lines
1.8 KiB
Swift
import Foundation
|
|
import Testing
|
|
@testable import OpenClawKit
|
|
|
|
struct GatewayCustomHeadersTests {
|
|
@Test func `sanitized keeps operator proxy credential headers`() {
|
|
let headers = GatewayCustomHeaders.sanitized([
|
|
"CF-Access-Client-Id": "client-id",
|
|
"CF-Access-Client-Secret": "client-secret",
|
|
"Authorization": "Basic dXNlcjpwYXNz",
|
|
])
|
|
#expect(headers == [
|
|
"CF-Access-Client-Id": "client-id",
|
|
"CF-Access-Client-Secret": "client-secret",
|
|
"Authorization": "Basic dXNlcjpwYXNz",
|
|
])
|
|
}
|
|
|
|
@Test func `sanitized drops reserved handshake header names`() {
|
|
let headers = GatewayCustomHeaders.sanitized([
|
|
"Host": "evil.example",
|
|
"connection": "close",
|
|
"Upgrade": "h2c",
|
|
"Sec-WebSocket-Protocol": "override",
|
|
"sec-websocket-key": "override",
|
|
"Content-Length": "0",
|
|
"Proxy-Connection": "keep-alive",
|
|
"X-Allowed": "yes",
|
|
])
|
|
#expect(headers == ["X-Allowed": "yes"])
|
|
}
|
|
|
|
@Test func `sanitized drops invalid names and control characters`() {
|
|
let headers = GatewayCustomHeaders.sanitized([
|
|
"": "value",
|
|
" ": "value",
|
|
"X Bad": "value",
|
|
"X:Bad": "value",
|
|
"X-Bad-é": "value",
|
|
"X-Split\r\nEvil": "value",
|
|
"X-Value-Split": "a\r\nEvil: b",
|
|
"X-Tab-Value": "a\tb",
|
|
"X-Fine": "value",
|
|
])
|
|
#expect(headers == ["X-Fine": "value"])
|
|
}
|
|
|
|
@Test func `reserved name check is case and whitespace insensitive`() {
|
|
#expect(GatewayCustomHeaders.isReservedName(" HOST "))
|
|
#expect(GatewayCustomHeaders.isReservedName("Sec-WebSocket-Extensions"))
|
|
#expect(!GatewayCustomHeaders.isReservedName("CF-Access-Client-Id"))
|
|
}
|
|
}
|