apple_kit

Apple-native services for Radroots iOS and macOS apps
git clone https://radroots.dev/git/apple_kit.git
Log | Files | Refs | README | LICENSE

RadrootsGovernedFileReaderTests.swift (7988B)


      1 import Darwin
      2 import Foundation
      3 import Testing
      4 
      5 @testable import RadrootsKit
      6 
      7 @Test func governedFileReaderReadsAnExactBoundedRegularFile() throws {
      8     try withGovernedFileFixture { root in
      9         let directory = root.appendingPathComponent("config", isDirectory: true)
     10         try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: false)
     11         let payload = Data("governed".utf8)
     12         try payload.write(to: directory.appendingPathComponent("control.json"))
     13 
     14         #expect(
     15             try RadrootsGovernedFileReader.read(
     16                 root: root,
     17                 relativePath: "config/control.json",
     18                 maximumBytes: payload.count
     19             ) == payload
     20         )
     21     }
     22 }
     23 
     24 @Test(arguments: ["", "/absolute", ".", "..", "config/", "config//control", "config/../control"])
     25 func governedFileReaderRejectsInvalidRelativePaths(_ relativePath: String) throws {
     26     try withGovernedFileFixture { root in
     27         #expect(throws: RadrootsGovernedFileReadError.invalidRequest) {
     28             _ = try RadrootsGovernedFileReader.read(
     29                 root: root,
     30                 relativePath: relativePath,
     31                 maximumBytes: 16
     32             )
     33         }
     34     }
     35 }
     36 
     37 @Test func governedFileReaderRejectsRootIntermediateAndLeafSymlinks() throws {
     38     try withGovernedFileFixture { root in
     39         let actual = root.appendingPathComponent("actual", isDirectory: true)
     40         try FileManager.default.createDirectory(at: actual, withIntermediateDirectories: false)
     41         try Data("value".utf8).write(to: actual.appendingPathComponent("control"))
     42 
     43         let rootLink = root.deletingLastPathComponent()
     44             .appendingPathComponent("\(root.lastPathComponent)-link")
     45         defer { try? FileManager.default.removeItem(at: rootLink) }
     46         try FileManager.default.createSymbolicLink(at: rootLink, withDestinationURL: root)
     47         #expect(throws: RadrootsGovernedFileReadError.invalidObject) {
     48             _ = try RadrootsGovernedFileReader.read(
     49                 root: rootLink,
     50                 relativePath: "actual/control",
     51                 maximumBytes: 16
     52             )
     53         }
     54 
     55         let directoryLink = root.appendingPathComponent("directory-link")
     56         try FileManager.default.createSymbolicLink(at: directoryLink, withDestinationURL: actual)
     57         #expect(throws: RadrootsGovernedFileReadError.invalidObject) {
     58             _ = try RadrootsGovernedFileReader.read(
     59                 root: root,
     60                 relativePath: "directory-link/control",
     61                 maximumBytes: 16
     62             )
     63         }
     64 
     65         let leafLink = root.appendingPathComponent("leaf-link")
     66         try FileManager.default.createSymbolicLink(
     67             at: leafLink,
     68             withDestinationURL: actual.appendingPathComponent("control")
     69         )
     70         #expect(throws: RadrootsGovernedFileReadError.invalidObject) {
     71             _ = try RadrootsGovernedFileReader.read(
     72                 root: root,
     73                 relativePath: "leaf-link",
     74                 maximumBytes: 16
     75             )
     76         }
     77     }
     78 }
     79 
     80 @Test func governedFileReaderRejectsDirectoriesFifosAndOversizedFiles() throws {
     81     try withGovernedFileFixture { root in
     82         let directory = root.appendingPathComponent("directory", isDirectory: true)
     83         try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: false)
     84         #expect(throws: RadrootsGovernedFileReadError.invalidObject) {
     85             _ = try RadrootsGovernedFileReader.read(
     86                 root: root,
     87                 relativePath: "directory",
     88                 maximumBytes: 16
     89             )
     90         }
     91 
     92         let fifo = root.appendingPathComponent("control.fifo")
     93         let fifoResult = fifo.path.withCString { Darwin.mkfifo($0, S_IRUSR | S_IWUSR) }
     94         #expect(fifoResult == 0)
     95         #expect(throws: RadrootsGovernedFileReadError.invalidObject) {
     96             _ = try RadrootsGovernedFileReader.read(
     97                 root: root,
     98                 relativePath: "control.fifo",
     99                 maximumBytes: 16
    100             )
    101         }
    102 
    103         try Data(repeating: 0x41, count: 17).write(to: root.appendingPathComponent("oversized"))
    104         #expect(throws: RadrootsGovernedFileReadError.tooLarge) {
    105             _ = try RadrootsGovernedFileReader.read(
    106                 root: root,
    107                 relativePath: "oversized",
    108                 maximumBytes: 16
    109             )
    110         }
    111     }
    112 }
    113 
    114 @Test func governedFileReaderRejectsLeafReplacementAfterAdmission() throws {
    115     try withGovernedFileFixture { root in
    116         let file = root.appendingPathComponent("control")
    117         let retained = root.appendingPathComponent("retained")
    118         try Data("original".utf8).write(to: file)
    119 
    120         #expect(throws: RadrootsGovernedFileReadError.changedDuringRead) {
    121             _ = try RadrootsGovernedFileReader.readForTesting(
    122                 root: root,
    123                 relativePath: "control",
    124                 maximumBytes: 16
    125             ) {
    126                 try FileManager.default.moveItem(at: file, to: retained)
    127                 try Data("foreign".utf8).write(to: file)
    128             }
    129         }
    130     }
    131 }
    132 
    133 @Test func governedFileReaderRejectsDirectoryReplacementAfterAdmission() throws {
    134     try withGovernedFileFixture { root in
    135         let directory = root.appendingPathComponent("config", isDirectory: true)
    136         let retained = root.appendingPathComponent("retained", isDirectory: true)
    137         try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: false)
    138         try Data("original".utf8).write(to: directory.appendingPathComponent("control"))
    139 
    140         #expect(throws: RadrootsGovernedFileReadError.changedDuringRead) {
    141             _ = try RadrootsGovernedFileReader.readForTesting(
    142                 root: root,
    143                 relativePath: "config/control",
    144                 maximumBytes: 16
    145             ) {
    146                 try FileManager.default.moveItem(at: directory, to: retained)
    147                 try FileManager.default.createDirectory(
    148                     at: directory, withIntermediateDirectories: false)
    149                 try Data("foreign".utf8).write(to: directory.appendingPathComponent("control"))
    150             }
    151         }
    152     }
    153 }
    154 
    155 @Test func governedFileReaderRejectsInPlaceMutationAfterAdmission() throws {
    156     try withGovernedFileFixture { root in
    157         let file = root.appendingPathComponent("control")
    158         try Data("original".utf8).write(to: file)
    159 
    160         #expect(throws: RadrootsGovernedFileReadError.changedDuringRead) {
    161             _ = try RadrootsGovernedFileReader.readForTesting(
    162                 root: root,
    163                 relativePath: "control",
    164                 maximumBytes: 16
    165             ) {
    166                 try Data("mutated-longer".utf8).write(to: file)
    167             }
    168         }
    169     }
    170 }
    171 
    172 @Test func governedFileReaderErrorsContainNoPathOrContent() throws {
    173     let canaries = ["/private/sensitive/control.json", "secret-canary"]
    174     let errors: [RadrootsGovernedFileReadError] = [
    175         .invalidRequest,
    176         .unavailable,
    177         .invalidObject,
    178         .tooLarge,
    179         .changedDuringRead,
    180         .ioFailure,
    181     ]
    182     for error in errors {
    183         let rendered = String(reflecting: error)
    184         #expect(canaries.allSatisfy { !rendered.contains($0) })
    185     }
    186 }
    187 
    188 private func withGovernedFileFixture(
    189     _ body: (URL) throws -> Void
    190 ) throws {
    191     let unresolvedRoot = FileManager.default.temporaryDirectory
    192         .appendingPathComponent("radroots-governed-file-\(UUID().uuidString)", isDirectory: true)
    193     try FileManager.default.createDirectory(at: unresolvedRoot, withIntermediateDirectories: false)
    194     guard let resolvedPointer = unresolvedRoot.path.withCString({ Darwin.realpath($0, nil) }) else {
    195         throw RadrootsGovernedFileReadError.ioFailure
    196     }
    197     defer { Darwin.free(resolvedPointer) }
    198     let root = URL(fileURLWithPath: String(cString: resolvedPointer), isDirectory: true)
    199     defer { try? FileManager.default.removeItem(at: root) }
    200     try body(root)
    201 }