mirror of
https://github.com/block/buzz.git
synced 2026-10-29 13:31:44 +00:00
Age-check errors previously blocked Buzz startup. Normal app startup now proceeds immediately; only a valid under-18 response from the current launch request can restrict access. Age check enforcement remains disabled at compile time by default. Builds can opt in with `--dart-define=BUZZ_AGE_GATING_ENABLED=true`. --------- Signed-off-by: Tom Brow <tomb@block.xyz>
139 lines
6.0 KiB
Swift
139 lines
6.0 KiB
Swift
import Foundation
|
|
import Testing
|
|
|
|
@testable import BuzzPushKit
|
|
|
|
struct BuzzAgeRestrictionSessionTests {
|
|
private func directory() throws -> URL {
|
|
let url = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
|
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
|
|
return url
|
|
}
|
|
|
|
@Test func confirmedRestrictionIsReleasedWithoutAStorageWrite() throws {
|
|
let url = try directory()
|
|
defer { try? FileManager.default.removeItem(at: url) }
|
|
let session = BuzzAgeRestrictionSession()
|
|
#expect(!BuzzAgeRestrictionSession.isRestricted(containerURL: url))
|
|
try session.restrict(containerURL: url)
|
|
#expect(BuzzAgeRestrictionSession.isRestricted(containerURL: url))
|
|
try session.restrict(containerURL: url)
|
|
session.release()
|
|
#expect(!BuzzAgeRestrictionSession.isRestricted(containerURL: url))
|
|
}
|
|
|
|
@Test func missingOrStaleReadOnlyFilesCannotRestrict() throws {
|
|
let url = try directory()
|
|
defer { try? FileManager.default.removeItem(at: url) }
|
|
#expect(!BuzzAgeRestrictionSession.isRestricted(containerURL: nil))
|
|
#expect(!BuzzAgeRestrictionSession.isRestricted(containerURL: url))
|
|
let file = url.appendingPathComponent(BuzzAgeRestrictionSession.fileName)
|
|
try Data("old or malformed restriction".utf8).write(to: file)
|
|
try FileManager.default.setAttributes([.posixPermissions: 0o400], ofItemAtPath: file.path)
|
|
#expect(!BuzzAgeRestrictionSession.isRestricted(containerURL: url))
|
|
}
|
|
|
|
@Test func acquisitionFailureCannotCreateAuthority() {
|
|
let missing = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
|
let session = BuzzAgeRestrictionSession()
|
|
#expect(throws: (any Error).self) { try session.restrict(containerURL: missing) }
|
|
#expect(!BuzzAgeRestrictionSession.isRestricted(containerURL: missing))
|
|
}
|
|
|
|
@Test func `Pending restriction prevents newer handoffs before the current delivery finishes`() throws {
|
|
let url = try directory()
|
|
defer { try? FileManager.default.removeItem(at: url) }
|
|
let session = BuzzAgeRestrictionSession()
|
|
var delivered = false
|
|
let allowed = BuzzAgeRestrictionSession.handoffIfAllowed(containerURL: url) {
|
|
#expect(throws: (any Error).self) { try session.restrict(containerURL: url) }
|
|
#expect(BuzzAgeRestrictionSession.isRestricted(containerURL: url))
|
|
#expect(!BuzzAgeRestrictionSession.handoffIfAllowed(containerURL: url) {
|
|
Issue.record("Pending restriction must prevent a newer handoff")
|
|
})
|
|
delivered = true
|
|
}
|
|
#expect(allowed)
|
|
#expect(delivered)
|
|
try session.restrict(containerURL: url)
|
|
#expect(!BuzzAgeRestrictionSession.handoffIfAllowed(containerURL: url) {
|
|
Issue.record("A confirmed restriction must suppress ordinary content")
|
|
})
|
|
session.release()
|
|
}
|
|
|
|
@Test func `Allowed restoration does not wait for a suspended handoff`() throws {
|
|
let url = try directory()
|
|
defer { try? FileManager.default.removeItem(at: url) }
|
|
let session = BuzzAgeRestrictionSession()
|
|
let allowed = BuzzAgeRestrictionSession.handoffIfAllowed(containerURL: url) {
|
|
#expect(throws: (any Error).self) { try session.restrict(containerURL: url) }
|
|
session.release()
|
|
#expect(!BuzzAgeRestrictionSession.isRestricted(containerURL: url))
|
|
#expect(BuzzAgeRestrictionSession.handoffIfAllowed(containerURL: url) {})
|
|
}
|
|
#expect(allowed)
|
|
}
|
|
|
|
@Test func `Handoff delivers when restriction storage is unavailable`() {
|
|
var deliveries = 0
|
|
#expect(BuzzAgeRestrictionSession.handoffIfAllowed(containerURL: nil) { deliveries += 1 })
|
|
let missing = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
|
#expect(BuzzAgeRestrictionSession.handoffIfAllowed(containerURL: missing) { deliveries += 1 })
|
|
#expect(deliveries == 2)
|
|
}
|
|
|
|
#if os(macOS)
|
|
@Test(.timeLimit(.minutes(1))) func processExitReleasesAuthorityWithoutCleanup() throws {
|
|
let url = try directory()
|
|
defer { try? FileManager.default.removeItem(at: url) }
|
|
let process = Process()
|
|
let input = Pipe()
|
|
let output = Pipe()
|
|
process.executableURL = URL(fileURLWithPath: "/usr/bin/python3")
|
|
process.arguments = ["-c", """
|
|
import fcntl, os, sys
|
|
fd = os.open(sys.argv[1], os.O_CREAT | os.O_RDWR, 0o600)
|
|
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
os.write(1, b'1')
|
|
os.read(0, 1)
|
|
os._exit(0)
|
|
""", url.appendingPathComponent(BuzzAgeRestrictionSession.fileName).path]
|
|
process.standardInput = input
|
|
process.standardOutput = output
|
|
try process.run()
|
|
defer { if process.isRunning { process.terminate() } }
|
|
#expect(try output.fileHandleForReading.read(upToCount: 1) == Data("1".utf8))
|
|
#expect(BuzzAgeRestrictionSession.isRestricted(containerURL: url))
|
|
try input.fileHandleForWriting.close()
|
|
process.waitUntilExit()
|
|
#expect(process.terminationStatus == 0)
|
|
#expect(!BuzzAgeRestrictionSession.isRestricted(containerURL: url))
|
|
#expect(FileManager.default.fileExists(atPath: url.appendingPathComponent(BuzzAgeRestrictionSession.fileName).path))
|
|
}
|
|
|
|
@Test(.timeLimit(.minutes(1))) func nativeRestrictionIsVisibleToAnotherProcess() throws {
|
|
let url = try directory()
|
|
defer { try? FileManager.default.removeItem(at: url) }
|
|
let session = BuzzAgeRestrictionSession()
|
|
try session.restrict(containerURL: url)
|
|
defer { session.release() }
|
|
let process = Process()
|
|
process.executableURL = URL(fileURLWithPath: "/usr/bin/python3")
|
|
process.arguments = ["-c", """
|
|
import fcntl, os, sys
|
|
fd = os.open(sys.argv[1], os.O_RDONLY)
|
|
try:
|
|
fcntl.flock(fd, fcntl.LOCK_SH | fcntl.LOCK_NB)
|
|
except BlockingIOError:
|
|
sys.exit(0)
|
|
sys.exit(1)
|
|
""", url.appendingPathComponent(BuzzAgeRestrictionSession.fileName).path]
|
|
try process.run()
|
|
defer { if process.isRunning { process.terminate() } }
|
|
process.waitUntilExit()
|
|
#expect(process.terminationStatus == 0)
|
|
}
|
|
#endif
|
|
}
|