test(ios): ClientTLS coverage 55.76% -> 89.49%, gate it, fix the three dead CI legs

ClientTLS was the most security-sensitive package in the tree and the least
covered, and it was not in the coverage gate at all (the gate's 4-package set
predates it). 48 -> 84 tests against the real macOS keychain, serialized with a
custom Testing trait after a @globalActor proved insufficient (actors yield at
await, so cross-await critical sections got interleaved by other cases' cleanup).

CI: the app/ipad/ios17 legs ran a bundle containing LiveServerSmokeTests, which
spawns tsx, with no npm ci -- a hard failure, not a skip, on a bare checkout.
Adds the missing iPad UI-test leg, and makes a missing iOS 17 runtime fail loudly
instead of silently reporting green.
This commit is contained in:
Yaojia Wang
2026-07-30 12:45:26 +02:00
parent 850531fd07
commit a5fa843f00
14 changed files with 1733 additions and 46 deletions

View File

@@ -0,0 +1,52 @@
import Foundation
// B4 · Read back the fields of a PKCS#10 CSR so tests can assert on what was
// ACTUALLY sent to the control plane (subject CN, embedded public key) instead
// of trusting the encoder's own view. Uses the throwaway `TestDER` reader from
// CertificateSigningRequestTests.
/// The two CSR fields the store/rotation invariants are stated in terms of.
struct ParsedCSR: Equatable {
/// `CertificationRequestInfo.subject` the single CN RDN.
let subjectCommonName: String
/// `subjectPKInfo` BIT STRING content: the X9.63 uncompressed point.
let publicPointX963: Data
/// The exact `CertificationRequestInfo` DER (the bytes that were signed).
let certificationRequestInfoDER: Data
}
/// Parse a `CertificationRequest` DER. `nil` when the shape is not the canonical
/// `SEQUENCE { info, algId, BIT STRING }` this package emits.
func parseCSR(_ der: Data) -> ParsedCSR? {
let bytes = [UInt8](der)
guard let outer = TestDER.read(bytes, at: 0), outer.end == bytes.count else { return nil }
let parts = TestDER.children(bytes, outer)
guard parts.count == 3 else { return nil }
let info = parts[0]
let infoChildren = TestDER.children(bytes, info)
guard infoChildren.count == 4 else { return nil }
// subject: SEQUENCE { SET { SEQUENCE { OID commonName, UTF8String } } }
let rdnSequence = TestDER.children(bytes, infoChildren[1])
guard let rdnSet = rdnSequence.first else { return nil }
let attributes = TestDER.children(bytes, rdnSet)
guard let attribute = attributes.first else { return nil }
let attributeParts = TestDER.children(bytes, attribute)
guard attributeParts.count == 2 else { return nil }
let commonNameBytes = Array(bytes[attributeParts[1].valueStart..<attributeParts[1].valueEnd])
guard let commonName = String(bytes: commonNameBytes, encoding: .utf8) else { return nil }
// subjectPKInfo: SEQUENCE { AlgorithmIdentifier, BIT STRING point }
let spkiChildren = TestDER.children(bytes, infoChildren[2])
guard spkiChildren.count == 2 else { return nil }
let bitString = spkiChildren[1]
guard bitString.valueEnd > bitString.valueStart + 1 else { return nil }
let point = Data(bytes[(bitString.valueStart + 1)..<bitString.valueEnd])
return ParsedCSR(
subjectCommonName: commonName,
publicPointX963: point,
certificationRequestInfoDER: Data(bytes[info.start..<info.end])
)
}