Viewing is open to everyone. Downloading and starring need a GitHub sign-in. Sign in with GitHub

CKeySafeActionsTests.swift

v0.1.0-build.85 Latest Commit ede38f5

Size
3.9 KB
Lines
68
Language
swift
SHA-256
f9424cb5f801…
View on GitHub

Sign in with GitHub to download. Each download is recorded with your GitHub username.

1import CKeyScanKit
2import XCTest
3
4/// Safe Actions against the conformance vectors (tests/vectors/safe-actions-v1.json).
5final class CKeySafeActionsTests: XCTestCase {
6 private struct Vector: Decodable {
7 struct Fields: Decodable { let scope: Int; let issuer: String; let app: String; let action: String; let key_id: String; let request_id: String }
8 let name: String
9 let payload: String
10 let public_key: String
11 let now: Double
12 let max_lifetime: UInt32
13 let expect: String
14 let fields: Fields?
15 }
16
17 private func vectors() throws -> [Vector] {
18 // Tests/CKeyScanKitTests -> repository root.
19 let root = URL(fileURLWithPath: #filePath).deletingLastPathComponent().deletingLastPathComponent()
20 .deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent()
21 let data = try Data(contentsOf: root.appendingPathComponent("tests/vectors/safe-actions-v1.json"))
22 struct File: Decodable { let vectors: [Vector] }
23 return try JSONDecoder().decode(File.self, from: data).vectors
24 }
25
26 func testSameDeviceVectorsRunAndBadOnesAreRefused() async throws {
27 for v in try vectors() where v.fields?.scope == 0 || v.expect == "integrity" {
28 let ran = expectation(description: v.name)
29 ran.isInverted = v.expect != "ok"
30 let issuer = v.fields?.issuer ?? "citeory.com"
31 let actions = CKeySafeActions(appID: v.fields?.app ?? "com.viadere", maxLifetime: v.max_lifetime,
32 now: { Date(timeIntervalSince1970: v.now) })
33 .trust(issuer: issuer, keyID: v.fields?.key_id ?? "2026-11", publicKeyHex: v.public_key)
34 .register(v.fields?.action ?? "open.library", issuers: [issuer]) { _ in ran.fulfill() }
35 let outcome = await actions.handle(v.payload)
36 switch outcome {
37 case .performed: XCTAssertEqual(v.expect, "ok", v.name)
38 case .rejected: XCTAssertNotEqual(v.expect, "ok", v.name)
39 default: XCTFail("\(v.name): unexpected \(outcome)")
40 }
41 await fulfillment(of: [ran], timeout: 0.5)
42 }
43 }
44
45 func testACodeWorksOnceAndNonSafeActionsAreLeftAlone() async throws {
46 let v = try XCTUnwrap(try vectors().first { $0.expect == "ok" && $0.fields?.scope == 0 })
47 let fields = try XCTUnwrap(v.fields)
48 let actions = CKeySafeActions(appID: fields.app, now: { Date(timeIntervalSince1970: v.now) })
49 .trust(issuer: fields.issuer, keyID: fields.key_id, publicKeyHex: v.public_key)
50 .register(fields.action, issuers: [fields.issuer]) { _ in }
51 if case .performed = await actions.handle(v.payload) {} else { XCTFail("first use runs") }
52 if case .rejected(let reason) = await actions.handle(v.payload) { XCTAssertTrue(reason.contains("already")) } else { XCTFail("second use is refused") }
53 if case .notASafeAction = await actions.handle("https://citeory.com") {} else { XCTFail("ordinary links are not Safe Actions") }
54 }
55
56 func testCrossDeviceNeedsTheIssuerAndTheConfirmation() async throws {
57 let v = try XCTUnwrap(try vectors().first { $0.expect == "ok" && $0.fields?.scope == 1 })
58 let fields = try XCTUnwrap(v.fields)
59 let ran = expectation(description: "must not run")
60 ran.isInverted = true
61 // No request_info: a cross-device action cannot run at all.
62 let actions = CKeySafeActions(appID: fields.app, now: { Date(timeIntervalSince1970: v.now) })
63 .trust(issuer: fields.issuer, keyID: fields.key_id, publicKeyHex: v.public_key)
64 .register(fields.action, issuers: [fields.issuer], scope: .crossDevice) { _ in ran.fulfill() }
65 if case .rejected = await actions.handle(v.payload) {} else { XCTFail("refused without the issuer's request information") }
66 await fulfillment(of: [ran], timeout: 0.5)
67 }
68}