66 lines
2.6 KiB
Swift
66 lines
2.6 KiB
Swift
import XCTest
|
|
@testable import SharedCore
|
|
|
|
final class TokenSecurityTests: XCTestCase {
|
|
func testSanitizeTokenRejectsEmptyAndRedactedValues() {
|
|
XCTAssertNil(TokenSecurity.sanitizeToken(nil))
|
|
XCTAssertNil(TokenSecurity.sanitizeToken(" "))
|
|
XCTAssertNil(TokenSecurity.sanitizeToken("REDACTED_TOKEN"))
|
|
XCTAssertEqual(TokenSecurity.sanitizeToken(" abc123 "), "abc123")
|
|
}
|
|
|
|
func testSanitizeTokenNormalizesAuthorizationPrefixes() {
|
|
XCTAssertEqual(TokenSecurity.sanitizeToken("Token abc123"), "abc123")
|
|
XCTAssertEqual(TokenSecurity.sanitizeToken("bearer xyz789"), "xyz789")
|
|
XCTAssertNil(TokenSecurity.sanitizeToken("Token "))
|
|
}
|
|
|
|
func testContainsPotentialHardcodedTokenDetectsLongHexBlob() {
|
|
let content = "private let token = \"0123456789abcdef0123456789abcdef\""
|
|
XCTAssertTrue(TokenSecurity.containsPotentialHardcodedToken(in: content))
|
|
}
|
|
|
|
func testJWTExpirationAndRotationWindow() throws {
|
|
let now = Date(timeIntervalSince1970: 1_700_000_000)
|
|
let expiration = now.addingTimeInterval(30 * 60)
|
|
|
|
let token = try makeJWT(exp: expiration)
|
|
|
|
XCTAssertEqual(TokenSecurity.jwtExpiration(token), expiration)
|
|
XCTAssertFalse(TokenSecurity.isExpired(token, now: now))
|
|
XCTAssertTrue(TokenSecurity.shouldRotate(token, now: now, rotationWindow: 60 * 60))
|
|
}
|
|
|
|
func testExpiredJWTReturnsExpired() throws {
|
|
let now = Date(timeIntervalSince1970: 1_700_000_000)
|
|
let expiration = now.addingTimeInterval(-10)
|
|
let token = try makeJWT(exp: expiration)
|
|
|
|
XCTAssertTrue(TokenSecurity.isExpired(token, now: now))
|
|
}
|
|
|
|
func testMalformedTokenDoesNotCrashAndDoesNotTriggerRotation() {
|
|
let malformed = "not-a-jwt"
|
|
XCTAssertNil(TokenSecurity.jwtExpiration(malformed))
|
|
XCTAssertFalse(TokenSecurity.isExpired(malformed))
|
|
XCTAssertFalse(TokenSecurity.shouldRotate(malformed))
|
|
}
|
|
|
|
private func makeJWT(exp: Date) throws -> String {
|
|
let header = ["alg": "HS256", "typ": "JWT"]
|
|
let payload = ["exp": Int(exp.timeIntervalSince1970)]
|
|
|
|
let headerData = try JSONSerialization.data(withJSONObject: header)
|
|
let payloadData = try JSONSerialization.data(withJSONObject: payload)
|
|
|
|
return "\(base64URL(headerData)).\(base64URL(payloadData)).signature"
|
|
}
|
|
|
|
private func base64URL(_ data: Data) -> String {
|
|
data.base64EncodedString()
|
|
.replacingOccurrences(of: "+", with: "-")
|
|
.replacingOccurrences(of: "/", with: "_")
|
|
.replacingOccurrences(of: "=", with: "")
|
|
}
|
|
}
|