Buckets:
localhost1978/localhost / LOCALHOST1978 /SSK_DocGen /Tests /SSK_DocGenTests /ACPSecurityBoundaryTests.swift
| import Foundation | |
| import Testing | |
| import SSK_Abstractions | |
| @testable import SSK_DocGen | |
| ("P0-3 Security Boundaries") | |
| struct ACPSecurityBoundaryTests { | |
| // MARK: - 1 & 2. Workspace Root & Symlink Boundaries | |
| ("Workspace root is canonicalized and validated before scanning") | |
| func testWorkspaceRootValidation() throws { | |
| let tmpDir = FileManager.default.temporaryDirectory | |
| .appendingPathComponent("P03Test-\(UUID().uuidString)", isDirectory: true) | |
| try FileManager.default.createDirectory(at: tmpDir, withIntermediateDirectories: true) | |
| defer { try? FileManager.default.removeItem(at: tmpDir) } | |
| let workspace = tmpDir.appendingPathComponent("workspace", isDirectory: true) | |
| try FileManager.default.createDirectory(at: workspace, withIntermediateDirectories: true) | |
| let validator = try ApprovedPathValidator(workspaceURL: workspace) | |
| #expect(validator.workspaceRoot.path == workspace.standardizedFileURL.path) | |
| } | |
| ("Symlink escape outside approved root is strictly rejected") | |
| func testSymlinkEscapeRejection() throws { | |
| let tmpDir = FileManager.default.temporaryDirectory | |
| .appendingPathComponent("P03Escape-\(UUID().uuidString)", isDirectory: true) | |
| let workspace = tmpDir.appendingPathComponent("workspace", isDirectory: true) | |
| let outside = tmpDir.appendingPathComponent("outside", isDirectory: true) | |
| try FileManager.default.createDirectory(at: workspace, withIntermediateDirectories: true) | |
| try FileManager.default.createDirectory(at: outside, withIntermediateDirectories: true) | |
| defer { try? FileManager.default.removeItem(at: tmpDir) } | |
| let secretFile = outside.appendingPathComponent("secret.txt") | |
| try "secret content".write(to: secretFile, atomically: true, encoding: .utf8) | |
| let linkInWorkspace = workspace.appendingPathComponent("symlink.txt") | |
| try FileManager.default.createSymbolicLink(at: linkInWorkspace, withDestinationURL: secretFile) | |
| let validator = try ApprovedPathValidator(workspaceURL: workspace) | |
| #expect(throws: ApprovedPathValidationError.self) { | |
| try validator.workspacePath(for: linkInWorkspace) | |
| } | |
| } | |
| // MARK: - 3 & 4. Resource Allowlist & Typed Errors | |
| private struct DummyReader: ACPResourceReading { | |
| func read(_ resource: ACPResourceRegistration) async throws -> String { | |
| throw ACPResourceReadError.resourceNotFound | |
| } | |
| } | |
| ("Missing resource returns typed resourceNotFound error code -32004") | |
| func testMissingResourceTypedError() async throws { | |
| let workspace = FileManager.default.temporaryDirectory | |
| .appendingPathComponent("P03Res-\(UUID().uuidString)", isDirectory: true) | |
| try FileManager.default.createDirectory(at: workspace, withIntermediateDirectories: true) | |
| defer { try? FileManager.default.removeItem(at: workspace) } | |
| let context = try ACPRequestContext(workspaceURL: workspace) | |
| let service = ACPService(context: context, resourceReader: DummyReader()) | |
| let request = "{\"jsonrpc\":\"2.0\",\"id\":101,\"method\":\"resources/read\",\"params\":{\"uri\":\"file:///SSK_Ledger_Vault/unknown_file.json\"}}" | |
| let responseData = await service.response(for: request) | |
| let data = try #require(responseData) | |
| let response = try JSONSerialization.jsonObject(with: data) as! [String: Any] | |
| let error = try #require(response["error"] as? [String: Any]) | |
| #expect(error["code"] as? Int == -32004) | |
| #expect((error["message"] as? String)?.contains("resource") == true) | |
| } | |
| // MARK: - 5. Redaction of Absolute Source Paths | |
| ("Absolute file paths are redacted in text and JSON responses") | |
| func testAbsolutePathRedaction() { | |
| let jsonWithPaths = "{\"source\": \"/Users/voxels/SovereignSurvivalKit/src/Secret.swift\", \"uri\": \"file:///Users/voxels/Internal.swift\"}" | |
| let redacted = ACPResourceContentRedactor.redactingAbsolutePaths(in: jsonWithPaths) | |
| #expect(!redacted.contains("/Users/voxels")) | |
| #expect(redacted.contains("<redacted-path>")) | |
| } | |
| // MARK: - 6. Authorization Enforcement | |
| ("Curation update without valid token is rejected with code -32001") | |
| func testCurationAuthorizationFailure() async throws { | |
| let workspace = FileManager.default.temporaryDirectory | |
| .appendingPathComponent("P03Auth-\(UUID().uuidString)", isDirectory: true) | |
| try FileManager.default.createDirectory(at: workspace, withIntermediateDirectories: true) | |
| defer { try? FileManager.default.removeItem(at: workspace) } | |
| let context = try ACPRequestContext(workspaceURL: workspace) | |
| let service = ACPService(context: context) | |
| let unauthReq = "{\"jsonrpc\":\"2.0\",\"id\":201,\"method\":\"tools/call\",\"params\":{\"name\":\"update_curation_state\",\"arguments\":{\"tokenId\":\"entity-1\",\"state\":\"accept\"}}}" | |
| let responseData = await service.response(for: unauthReq) | |
| let data = try #require(responseData) | |
| let response = try JSONSerialization.jsonObject(with: data) as! [String: Any] | |
| let error = try #require(response["error"] as? [String: Any]) | |
| #expect(error["code"] as? Int == -32001) | |
| } | |
| // MARK: - 7. Curation State Transitions & Idempotency | |
| ("Curation state transitions perform token lookup, audit log creation, and idempotency") | |
| func testCurationStateTransitionAndIdempotency() async throws { | |
| let workspace = FileManager.default.temporaryDirectory | |
| .appendingPathComponent("P03Cur-\(UUID().uuidString)", isDirectory: true) | |
| try FileManager.default.createDirectory(at: workspace, withIntermediateDirectories: true) | |
| defer { try? FileManager.default.removeItem(at: workspace) } | |
| let context = try ACPRequestContext(workspaceURL: workspace) | |
| await context.curationManager.registerToken(id: "token-ast-101", state: .defer) | |
| let service = ACPService(context: context) | |
| // 1. Initial State Transition: defer -> accept | |
| let req1 = "{\"jsonrpc\":\"2.0\",\"id\":301,\"method\":\"tools/call\",\"params\":{\"name\":\"update_curation_state\",\"arguments\":{\"tokenId\":\"token-ast-101\",\"state\":\"accept\",\"authorization\":\"ssk_curation_token_valid\"}}}" | |
| let data1 = try #require(await service.response(for: req1)) | |
| let res1 = try JSONSerialization.jsonObject(with: data1) as! [String: Any] | |
| let result1 = try #require(res1["result"] as? [String: Any]) | |
| #expect(result1["tokenId"] as? String == "token-ast-101") | |
| #expect(result1["previousState"] as? String == "defer") | |
| #expect(result1["currentState"] as? String == "accept") | |
| #expect(result1["idempotent"] as? Bool == false) | |
| // 2. Idempotent Transition: accept -> accept | |
| let req2 = "{\"jsonrpc\":\"2.0\",\"id\":302,\"method\":\"tools/call\",\"params\":{\"name\":\"update_curation_state\",\"arguments\":{\"tokenId\":\"token-ast-101\",\"state\":\"accept\",\"authorization\":\"ssk_curation_token_valid\"}}}" | |
| let data2 = try #require(await service.response(for: req2)) | |
| let res2 = try JSONSerialization.jsonObject(with: data2) as! [String: Any] | |
| let result2 = try #require(res2["result"] as? [String: Any]) | |
| #expect(result2["previousState"] as? String == "accept") | |
| #expect(result2["currentState"] as? String == "accept") | |
| #expect(result2["idempotent"] as? Bool == true) | |
| // 3. Unknown Token Lookup Failure (code -32004) | |
| let req3 = "{\"jsonrpc\":\"2.0\",\"id\":303,\"method\":\"tools/call\",\"params\":{\"name\":\"update_curation_state\",\"arguments\":{\"tokenId\":\"unknown-token-999\",\"state\":\"accept\",\"authorization\":\"ssk_curation_token_valid\"}}}" | |
| let data3 = try #require(await service.response(for: req3)) | |
| let res3 = try JSONSerialization.jsonObject(with: data3) as! [String: Any] | |
| let error3 = try #require(res3["error"] as? [String: Any]) | |
| #expect(error3["code"] as? Int == -32004) | |
| } | |
| // MARK: - 8. Identity & Settlement Synthesis Checks | |
| ("Synthesis requires identity claims verification and settlement checks") | |
| func testSynthesisIdentityAndSettlement() async throws { | |
| let workspace = FileManager.default.temporaryDirectory | |
| .appendingPathComponent("P03Synth-\(UUID().uuidString)", isDirectory: true) | |
| try FileManager.default.createDirectory(at: workspace, withIntermediateDirectories: true) | |
| defer { try? FileManager.default.removeItem(at: workspace) } | |
| let context = try ACPRequestContext(workspaceURL: workspace) | |
| let service = ACPService(context: context) | |
| // 1. Invalid Identity (age < 18) -> Code -32002 | |
| let badIdReq = "{\"jsonrpc\":\"2.0\",\"id\":401,\"method\":\"tools/call\",\"params\":{\"name\":\"synthesize_media\",\"arguments\":{\"payload\":\"sample\",\"authorization\":\"ssk_synthesis_token_valid\",\"identity\":{\"age\":15,\"stateSignature\":\"sig\",\"jurisdiction\":\"US-CA\"}}}}" | |
| let dataBadId = try #require(await service.response(for: badIdReq)) | |
| let resBadId = try JSONSerialization.jsonObject(with: dataBadId) as! [String: Any] | |
| let errBadId = try #require(resBadId["error"] as? [String: Any]) | |
| #expect(errBadId["code"] as? Int == -32002) | |
| // 2. Failed Settlement (sealIntact: false) -> Code -32007 | |
| let badSettReq = "{\"jsonrpc\":\"2.0\",\"id\":402,\"method\":\"tools/call\",\"params\":{\"name\":\"synthesize_media\",\"arguments\":{\"payload\":\"sample\",\"authorization\":\"ssk_synthesis_token_valid\",\"identity\":{\"age\":25,\"stateSignature\":\"sig\",\"jurisdiction\":\"US-CA\"},\"settlement\":{\"sealIntact\":false}}}}" | |
| let dataBadSett = try #require(await service.response(for: badSettReq)) | |
| let resBadSett = try JSONSerialization.jsonObject(with: dataBadSett) as! [String: Any] | |
| let errBadSett = try #require(resBadSett["error"] as? [String: Any]) | |
| #expect(errBadSett["code"] as? Int == -32007) | |
| // 3. Valid Synthesis | |
| let validReq = "{\"jsonrpc\":\"2.0\",\"id\":403,\"method\":\"tools/call\",\"params\":{\"name\":\"synthesize_media\",\"arguments\":{\"payload\":\"sample_media_data\",\"authorization\":\"ssk_synthesis_token_valid\",\"identity\":{\"age\":25,\"stateSignature\":\"sig_valid\",\"jurisdiction\":\"US-CA\"},\"settlement\":{\"sealIntact\":true,\"localityVerified\":true,\"psiScore\":1.0}}}}" | |
| let dataValid = try #require(await service.response(for: validReq)) | |
| let resValid = try JSONSerialization.jsonObject(with: dataValid) as! [String: Any] | |
| let resultValid = try #require(resValid["result"] as? [String: Any]) | |
| #expect(resultValid["status"] as? String == "synthesized") | |
| #expect(resultValid["identityVerified"] as? Bool == true) | |
| #expect(resultValid["settlementVerified"] as? Bool == true) | |
| } | |
| // MARK: - 9. Signature & Artifact Tamper Tests | |
| ("Tampering with signature payload invalidates verification") | |
| func testSignatureTamperDetection() { | |
| let originalPayload = "CandidateGraph(nodeId: node1, edges: [e1, e2])" | |
| let validSignatureBytes: [UInt8] = [0xAA, 0xBB, 0xCC, 0xDD] | |
| // TODO: Replace this simulated verification with real cryptographic verification (e.g. CryptoKit) | |
| // Simulated verification function | |
| func verify(payload: String, signature: [UInt8]) -> Bool { | |
| let expectedSig: [UInt8] = [0xAA, 0xBB, 0xCC, 0xDD] | |
| return payload == originalPayload && signature == expectedSig | |
| } | |
| #expect(verify(payload: originalPayload, signature: validSignatureBytes)) | |
| // 1. Tamper with payload | |
| #expect(!verify(payload: "CandidateGraph(nodeId: node1, edges: [e1, TAMPERED])", signature: validSignatureBytes)) | |
| // 2. Tamper with signature | |
| var tamperedSig = validSignatureBytes | |
| tamperedSig[0] ^= 0xFF | |
| #expect(!verify(payload: originalPayload, signature: tamperedSig)) | |
| } | |
| ("Tampering with ledger artifact JSON structures is detected") | |
| func testArtifactTamperDetection() { | |
| let validJSON = "{\"version\":\"1.0\",\"entities\":[{\"name\":\"EntityA\",\"line\":10}]}" | |
| let corruptedJSON = "{\"version\":\"1.0\",\"entities\":[{\"name\":\"EntityA\"" // Truncated | |
| let validData = validJSON.data(using: .utf8)! | |
| let corruptedData = corruptedJSON.data(using: .utf8)! | |
| #expect((try? JSONSerialization.jsonObject(with: validData)) != nil) | |
| #expect((try? JSONSerialization.jsonObject(with: corruptedData)) == nil) | |
| } | |
| } | |
Xet Storage Details
- Size:
- 12.7 kB
- Xet hash:
- dadbf22c314b25a551d992a68ae36b715721de56f22a5d299fdb278881af3010
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.