test: prune shared guards and manual Swift policy checks (#41554)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-11-12 05:50:58 -08:00
committed by GitHub
co-authored by OrcaWin
parent 382c7cfcb3
commit 97df516add
5 changed files with 2 additions and 378 deletions
@@ -11,38 +11,6 @@ import Foundation
// ios/MobileWebShellNavigationPolicy.swift \
// tests/MobileWebShellChecks.swift && /tmp/mobile-web-shell-checks
@main struct MobileWebShellChecks {
static let session = "sess-01JN_aZ9"
static func parts(
path: String,
method: String = "GET",
hasRangeHeader: Bool = false,
scheme: String? = MobileWebShellOrigin.scheme,
host: String? = session,
port: Int? = nil,
user: String? = nil,
query: String? = nil,
fragment: String? = nil,
urlByteCount: Int = 64
) -> MobileWebShellRequestParts {
MobileWebShellRequestParts(
method: method,
hasRangeHeader: hasRangeHeader,
scheme: scheme,
host: host,
port: port,
user: user,
query: query,
fragment: fragment,
percentEncodedPath: path,
urlByteCount: urlByteCount
)
}
static func resolve(_ request: MobileWebShellRequestParts) -> String? {
MobileWebShellOrigin.resolveRequestPath(request, sessionId: session)
}
static func manifest(
schemaVersion: Int = 1,
entrypoint: String = "index.html",
@@ -68,42 +36,6 @@ import Foundation
}
static func checkRequestResolution() {
precondition(resolve(parts(path: "/")) == "/")
precondition(resolve(parts(path: "")) == "/")
precondition(resolve(parts(path: "/assets/aa.js")) == "/assets/aa.js")
// A host a parser canonicalised must still bind to this session.
precondition(resolve(parts(path: "/", host: session.uppercased())) == "/")
precondition(resolve(parts(path: "/", method: "POST")) == nil)
precondition(resolve(parts(path: "/", method: "HEAD")) == nil)
precondition(resolve(parts(path: "/", hasRangeHeader: true)) == nil)
precondition(resolve(parts(path: "/", scheme: "https")) == nil)
precondition(resolve(parts(path: "/", scheme: nil)) == nil)
// Unicode folding would serve assets to unminted Kelvin-sign hosts.
precondition(MobileWebShellOrigin.resolveRequestPath(
parts(path: "/", host: "\u{212A}ey"),
sessionId: "key"
) == nil)
precondition(MobileWebShellOrigin.resolveRequestPath(
parts(path: "/", host: "KEY"),
sessionId: "key"
) == "/")
precondition(resolve(parts(path: "/", host: "other-session")) == nil)
precondition(resolve(parts(path: "/", host: nil)) == nil)
precondition(resolve(parts(path: "/", port: 443)) == nil)
precondition(resolve(parts(path: "/", user: "someone")) == nil)
precondition(resolve(parts(path: "/", query: "v=1")) == nil)
precondition(resolve(parts(path: "/", fragment: "frag")) == nil)
precondition(resolve(parts(path: "/assets/%2e%2e/etc")) == nil)
precondition(resolve(parts(path: "assets/aa.js")) == nil)
precondition(resolve(parts(path: "/", urlByteCount: 8 * 1024)) == "/")
precondition(resolve(parts(path: "/", urlByteCount: 8 * 1024 + 1)) == nil)
precondition(MobileWebShellOrigin.resolveRequestPath(parts(path: "/"), sessionId: "") == nil)
}
static func checkGenerationMap() {
guard let built = generation(manifest()) else { preconditionFailure("manifest rejected") }
// Only / may serve document bytes with CSP; no entrypoint alias.
@@ -214,156 +146,9 @@ import Foundation
static func bridgeSource(
isOurWebView: Bool = true,
isMainFrame: Bool = true,
hasCommittedDocument: Bool = true,
originProtocol: String = MobileWebShellOrigin.scheme,
originHost: String = session
) -> MobileWebShellBridgeSource {
MobileWebShellBridgeSource(
isOurWebView: isOurWebView,
isMainFrame: isMainFrame,
hasCommittedDocument: hasCommittedDocument,
originProtocol: originProtocol,
originHost: originHost
)
}
static func acceptsBridge(_ source: MobileWebShellBridgeSource) -> Bool {
MobileWebShellBridge.accepts(source, sessionId: session)
}
static func checkBridgeAcceptance() {
precondition(acceptsBridge(bridgeSource()))
// Simulator: WebKit ASCII-lowercases custom-scheme hosts; exact session comparison refuses every message.
precondition(acceptsBridge(bridgeSource(originHost: "sess-01jn_az9")))
precondition(acceptsBridge(bridgeSource(originHost: "SESS-01JN_AZ9")))
// A frame we did not serve.
precondition(!acceptsBridge(bridgeSource(originHost: "sess-01JN_aZ8")))
precondition(!acceptsBridge(bridgeSource(originHost: "")))
precondition(!acceptsBridge(bridgeSource(originHost: "sess-01JN_aZ9.evil")))
// Unicode folding would accept unminted Kelvin-sign hosts.
precondition(!MobileWebShellBridge.accepts(
bridgeSource(originHost: "\u{212A}ey"),
sessionId: "key"
))
// Another scheme reaching the same handler.
precondition(!acceptsBridge(bridgeSource(originProtocol: "https")))
precondition(!acceptsBridge(bridgeSource(originProtocol: "")))
precondition(!acceptsBridge(bridgeSource(originProtocol: "orca-mobile-web ")))
// A subframe, and a message routed to a WebView that is not ours.
precondition(!acceptsBridge(bridgeSource(isMainFrame: false)))
precondition(!acceptsBridge(bridgeSource(isOurWebView: false)))
// The replaced same-origin document remains alive until the new load commits.
precondition(!acceptsBridge(bridgeSource(hasCommittedDocument: false)))
// No applied session is not an empty one: nothing may be accepted before a load.
precondition(!MobileWebShellBridge.accepts(bridgeSource(originHost: ""), sessionId: ""))
precondition(!MobileWebShellBridge.accepts(bridgeSource(originHost: "a b"), sessionId: "a b"))
}
static func checkBridgePostTarget() {
func canPost(
_ host: String?,
_ sessionId: String = session,
committed: Bool = true
) -> Bool {
MobileWebShellBridge.canPost(
toFrameOriginHost: host,
sessionId: sessionId,
hasCommittedDocument: committed
)
}
precondition(canPost(session))
// The same ASCII fold as acceptance: WebKit reports the host lowercased.
precondition(canPost("sess-01jn_az9"))
// Never post without an accepted frame: before first message, after failure/death, or with bridge disabled.
precondition(!canPost(nil))
// A frame from another document, and a frame under no session at all.
precondition(!canPost("sess-01JN_aZ8"))
precondition(!canPost("\u{212A}ey", "key"))
precondition(!canPost(session, ""))
// Provisional navigation invalidates the held frame until commit.
precondition(!canPost(session, committed: false))
}
// One allow/opener decision; TypeScript’s readBridgeExternalLinkUrl owns scheme filtering.
static func checkNavigationVerdict() {
let foreign = "https://example.com/artifact-link"
let document = "orca-mobile-web://\(session)/"
func verdict(
_ url: String? = "https://example.com/artifact-link",
isMainFrame: Bool = true,
isDocumentUrl: Bool = false,
isShellLoad: Bool = false,
hasGesture: Bool = true,
isDownload: Bool = false
) -> MobileWebShellNavigationVerdict {
MobileWebShellNavigationPolicy.verdict(
url: url,
isMainFrame: isMainFrame,
isDocumentUrl: isDocumentUrl,
isShellLoad: isShellLoad,
hasGesture: hasGesture,
isDownload: isDownload
)
}
precondition(verdict() == .cancelAndOffer(foreign))
// WebKit reports our own load with main-frame source and target.
precondition(verdict(document, isDocumentUrl: true, isShellLoad: true, hasGesture: false) == .allow)
// Never offer/reload other document-URL navigations; sandboxed top navigation can report no gesture.
precondition(verdict(document, isDocumentUrl: true, hasGesture: false) == .cancel)
precondition(verdict(document, isDocumentUrl: true, hasGesture: true) == .cancel)
// A top-page meta refresh or a redirect to somewhere else: refused, and never opened.
precondition(verdict(hasGesture: false) == .cancel)
// Gestured downloads reach the opener, never the document-load allowance.
precondition(
verdict(document, isDocumentUrl: true, isShellLoad: true, hasGesture: false, isDownload: true)
== .cancel
)
precondition(verdict(isDownload: true) == .cancelAndOffer(foreign))
// Document-URL downloads are never offered, regardless of gesture.
precondition(verdict(document, isDocumentUrl: true, isDownload: true) == .cancel)
// A foreign-URL subframe is never offered: the user is not leaving the app.
precondition(verdict(isMainFrame: false) == .cancel)
// Sealed srcdoc frames (HTML previews, chat visuals) load; no other subframe or top-level srcdoc does.
let srcdoc = MobileWebShellNavigationPolicy.inlineDocumentUrl
precondition(verdict(srcdoc, isMainFrame: false, hasGesture: false) == .allow)
precondition(verdict(srcdoc, isMainFrame: false, isDownload: true) == .cancel)
precondition(verdict(srcdoc, hasGesture: false) == .cancel)
precondition(verdict("about:blank", isMainFrame: false, hasGesture: false) == .cancel)
precondition(verdict(document, isMainFrame: false, isDocumentUrl: true, isShellLoad: true) == .cancel)
precondition(verdict(nil) == .cancel)
precondition(verdict("") == .cancel)
// The crossing cap, at it and one past it.
let cap = MobileWebShellNavigationPolicy.maxCancelledNavigationUrlCharacters
let prefix = "https://example.com/"
let atCap = prefix + String(repeating: "a", count: cap - prefix.count)
precondition(MobileWebShellNavigationPolicy.offerableUrl(atCap) == atCap)
precondition(MobileWebShellNavigationPolicy.offerableUrl(atCap + "a") == nil)
precondition(verdict(atCap + "a") == .cancel)
// A scheme the opener will refuse still crosses: one filter, in the half that updates.
precondition(verdict("javascript:alert(1)") == .cancelAndOffer("javascript:alert(1)"))
}
static func main() {
checkRequestResolution()
checkGenerationMap()
checkLoadStateMachine()
checkNavigationVerdict()
checkBridgeAcceptance()
checkBridgePostTarget()
print("mobile web shell checks OK")
}
}
@@ -1,30 +0,0 @@
@main
struct RecordingInterruptionIntentTests {
static func main() {
var intent = RecordingInterruptionIntent()
// An idle audio interruption never starts a recording.
intent.began(wasRecording: false)
precondition(intent.ended(shouldResume: true, isRecording: false) == .blocked)
// A still-owned recording resumes once, not again on a duplicate end event.
intent.began(wasRecording: true)
precondition(intent.ended(shouldResume: true, isRecording: false) == .resume)
precondition(intent.ended(shouldResume: true, isRecording: false) == .blocked)
// A blocked or missing shouldResume bit consumes the old resume request.
intent.began(wasRecording: true)
precondition(intent.ended(shouldResume: false, isRecording: false) == .blocked)
precondition(intent.ended(shouldResume: true, isRecording: false) == .blocked)
// JavaScript cancels capture after began; ended must not turn its microphone back on.
intent.began(wasRecording: true)
intent.cancel()
precondition(intent.ended(shouldResume: true, isRecording: false) == .blocked)
// A newer explicit recording must not be cancelled by the old end notification.
intent.began(wasRecording: true)
intent.cancel()
precondition(intent.ended(shouldResume: true, isRecording: true) == .none)
// The most recent interruption, even one while idle, replaces old resume intent.
intent.began(wasRecording: true)
intent.began(wasRecording: false)
precondition(intent.ended(shouldResume: true, isRecording: false) == .blocked)
print("RecordingInterruptionIntent: 8 assertions passed")
}
}
+1 -99
View File
@@ -1,4 +1,4 @@
import { describe, expect, it, vi } from 'vitest'
import { describe, expect, it } from 'vitest'
import { BoundedMap } from './bounded-map'
describe('BoundedMap', () => {
@@ -29,41 +29,6 @@ describe('BoundedMap', () => {
expect(m.has('c')).toBe(true)
})
it('never reports success for an entry it did not retain', () => {
const onEvict = vi.fn()
// An entry ceiling above total once admitted then evicted the same key while set returned true.
const m = new BoundedMap<string, string>({
maxEntries: 10,
maxBytes: 3,
maxEntryBytes: 5,
sizeOf: (v) => v.length,
onEvict
})
m.set('s1', 'x')
m.set('s2', 'y')
expect(m.set('big', 'xxxx')).toBe(false)
expect(m.has('big')).toBe(false)
expect(m.has('s1')).toBe(true)
expect(m.has('s2')).toBe(true)
expect(onEvict).not.toHaveBeenCalled()
expect(m.retainedBytes).toBe(2)
})
it('fails closed on an unmeasurable weight instead of poisoning the ledger', () => {
const m = new BoundedMap<string, string>({
maxEntries: 10,
maxBytes: 10,
sizeOf: (v) => (v === 'bad' ? Number.NaN : v.length)
})
expect(m.set('ok', 'abc')).toBe(true)
expect(m.set('nan', 'bad')).toBe(false)
expect(m.set('neg', 'x')).toBe(true)
expect(m.retainedBytes).toBe(4)
expect(Number.isFinite(m.retainedBytes)).toBe(true)
// ceiling still armed after the rejected weight
expect(m.set('huge', 'zzzzzzzzzzzz')).toBe(false)
})
it('a rejected oversized overwrite leaves the existing value untouched', () => {
const m = new BoundedMap<string, string>({
maxEntries: 10,
@@ -92,67 +57,4 @@ describe('BoundedMap', () => {
['b', 2]
])
})
it('an overflow-rejected overwrite keeps the prior value and leaves no orphaned weight', () => {
const weights = new Map<string, number>([
['a', Number.MAX_SAFE_INTEGER - 1],
['b', 1]
])
const m = new BoundedMap<string, string>({
maxEntries: 10,
maxBytes: Number.MAX_SAFE_INTEGER,
sizeOf: (_v, k) => weights.get(k) ?? 0
})
m.set('a', 'A')
m.set('b', 'B')
weights.set('b', 3) // next weight for 'b' exceeds the remaining headroom
expect(m.set('b', 'B2')).toBe(false)
expect(m.has('b')).toBe(true) // rejection must not displace the prior value
expect(m.peek('b')).toBe('B')
expect(m.retainedBytes).toBe(Number.MAX_SAFE_INTEGER)
// the rejected key must not strand a weight: deleting everything returns the ledger to zero
weights.set('b', 1)
m.delete('a')
m.delete('b')
expect(m.retainedBytes).toBe(0)
expect(m.size).toBe(0)
})
it('rejects the entry when sizeOf throws instead of propagating', () => {
const m = new BoundedMap<string, string>({
maxEntries: 4,
maxBytes: 50,
sizeOf: (v) => {
if (v === 'boom') {
throw new Error('measure failed')
}
return v.length
}
})
expect(m.set('ok', 'abc')).toBe(true)
expect(() => m.set('bad', 'boom')).not.toThrow()
expect(m.set('bad', 'boom')).toBe(false)
expect(m.has('bad')).toBe(false)
expect(m.retainedBytes).toBe(3)
})
it('runs every disposal even when one onEvict throws', () => {
const disposed: string[] = []
const m = new BoundedMap<string, string>({
maxEntries: 10,
maxBytes: 2,
sizeOf: (v) => v.length,
onEvict: (_v, k) => {
disposed.push(k)
if (k === 'a') {
throw new Error('dispose failed')
}
}
})
m.set('a', 'x')
m.set('b', 'y')
expect(() => m.set('c', 'zz')).toThrow('dispose failed')
expect(disposed).toEqual(['a', 'b']) // 'b' still disposed despite the earlier throw
expect(m.retainedBytes).toBeLessThanOrEqual(2)
})
})
@@ -1,33 +0,0 @@
import { expect, it } from 'vitest'
import { withClaudeAgentViewPlugin } from './claude-agent-view-launch'
import {
tokenizeStartupCommand,
type AgentStartupShell
} from './agent-launch/startup/tui-agent-startup-shell'
it.each(['posix', 'powershell', 'cmd'] as const)(
'preserves existing plugin arguments and prompts in %s',
(shell: AgentStartupShell) => {
const command = 'claude --plugin-dir user-plugin --resume session -- "my prompt"'
const result = withClaudeAgentViewPlugin(command, 'orca plugin', shell)
const parsed = tokenizeStartupCommand(result, shell)
expect(parsed.ok && parsed.tokens).toEqual([
'claude',
'--plugin-dir',
'orca plugin',
'--plugin-dir',
'user-plugin',
'--resume',
'session',
'--',
'my prompt'
])
}
)
it.each(['ssh host claude', 'bash -c claude', 'echo /bin/claude', 'claude && echo done'])(
'leaves shell syntax and ambiguous executables unchanged: %s',
(command) => {
expect(withClaudeAgentViewPlugin(command, '/orca/plugin', 'posix')).toBe(command)
}
)
+1 -1
View File
@@ -1,6 +1,6 @@
import { expect, it } from 'vitest'
import { cleanCloudServiceOrigin } from './cloud-service-url'
it.each(['ftp://localhost', 'file://localhost', 'ws://localhost', 'http://example.com'])(
it.each(['ftp://localhost', 'http://example.com'])(
'rejects %s even with the development loopback exception',
(url) => {
expect(cleanCloudServiceOrigin(url, true)).toBeNull()