commit 6713e02acef3a9eff59b8591d2ab212a2a8c5b98
parent d34c612ea36cd7e1530d67675236669c897460f9
Author: triesap <tyson@radroots.org>
Date: Sun, 9 Aug 2026 19:48:45 +0000
ios: expose physical SSRF qualification failure
- Surface stable runtime failure codes in the status view.
- Exercise strict public Blossom endpoint rejection through app startup.
- Automate identity and reconfiguration transitions in qualification UI tests.
- Assert private loopback origins fail closed before the app shell appears.
Diffstat:
2 files changed, 44 insertions(+), 0 deletions(-)
diff --git a/Radroots/Views/RuntimeStatusView.swift b/Radroots/Views/RuntimeStatusView.swift
@@ -22,6 +22,12 @@ struct RuntimeStatusView: View {
.font(.body)
.foregroundStyle(.secondary)
.multilineTextAlignment(.center)
+ if case let .failed(failure) = phase {
+ Text("Error code \(failure.code)")
+ .font(.caption.monospaced())
+ .foregroundStyle(.secondary)
+ .accessibilityIdentifier("radroots.runtime.failure_code")
+ }
if case .identityRequired = phase {
Button("Create identity", action: createIdentity)
.buttonStyle(.borderedProminent)
@@ -118,4 +124,5 @@ struct RuntimeStatusView: View {
"Your durable local work is safe."
}
}
+
}
diff --git a/RadrootsUITests/RadrootsRemoteQualificationUITests.swift b/RadrootsUITests/RadrootsRemoteQualificationUITests.swift
@@ -21,6 +21,43 @@ final class RadrootsRemoteQualificationUITests: XCTestCase {
}
@MainActor
+ func testStrictPublicEndpointRejectsPrivateAddress() throws {
+ let environment = try QualificationConfiguration.environment()
+ let configuration = QualificationConfiguration(
+ runID: environment.runID,
+ relayURLs: [],
+ blossomOrigins: ["https://127.0.0.1"]
+ )
+ let app = XCUIApplication()
+ app.launchEnvironment = configuration.launchEnvironment
+ app.launch()
+
+ let failureCode = app.descendants(matching: .any)["radroots.runtime.failure_code"]
+ for _ in 0 ..< 8 where !failureCode.exists {
+ for identifier in [
+ "radroots.identity.create",
+ "radroots.identity.unlock",
+ "radroots.configuration.reconfigure",
+ "radroots.identity.recover",
+ ] {
+ let action = app.descendants(matching: .any)[identifier]
+ if action.exists, action.isHittable {
+ action.tap()
+ break
+ }
+ }
+ _ = failureCode.waitForExistence(timeout: 4)
+ }
+ XCTAssertEqual(
+ failureCode.label,
+ "Error code runtime_failed",
+ "A private Blossom endpoint did not expose the typed fail-closed result; "
+ + "failure_code.label=\(failureCode.exists ? failureCode.label : "missing")"
+ )
+ XCTAssertFalse(app.tabBars.firstMatch.exists)
+ }
+
+ @MainActor
func testRemoteBlossomUploadAndRecovery() throws {
let configuration = try QualificationConfiguration.environment()
let app = launchToRoot(configuration)