Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
481 changes: 399 additions & 82 deletions Sources/BridgeGeneratorTool/main.swift

Large diffs are not rendered by default.

40 changes: 38 additions & 2 deletions Sources/SwiftScriptInterpreter/API/HostHooks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,33 @@ public func authorizeURL(
}
}

// MARK: Subprocess deny

/// Thrown by every `Process` bridge entry when a sandbox is bound.
/// `Foundation.Process` spawns a real OS subprocess that escapes
/// every host gate (path, network, identity), so the policy is
/// "denied entirely whenever a sandbox is configured". Embedders
/// that want subprocess execution under a sandbox must register
/// SwiftBash's virtual-process table instead.
public struct ProcessSandboxDenied: Error, CustomStringConvertible {
public let reason: String
public init(reason: String = "Process is denied when a sandbox is active") {
self.reason = reason
}
public var description: String { reason }
}

/// Throw ``ProcessSandboxDenied`` when a sandbox is bound on the
/// current shell. Generated `Process(...)` bridges call this before
/// touching any Foundation API. Returns silently in the standalone
/// passthrough case (no sandbox configured).
@inlinable
public func denyProcessIfSandboxed() throws {
if ShellKit.Shell.current.sandbox != nil {
throw ProcessSandboxDenied()
}
}

// MARK: Identity

/// Synthetic user-name override — script-side `ProcessInfo
Expand Down Expand Up @@ -157,9 +184,18 @@ extension ShellKit.NetworkConfig {
@usableFromInline
func checkAllowed(url: URL, method: String) throws {
if dangerouslyAllowFullInternetAccess { return }
// Method gate.
// Method gate. Unknown verbs (LINK, UNLINK, custom WebDAV, …)
// must be rejected outright — falling back to `.GET` would
// silently grant a method the embedder never approved, since
// a request with `httpMethod = "FOO"` would be evaluated
// against GET permissions while the actual request method
// stayed `FOO`.
let normalised = method.uppercased()
let knownMethod = HTTPMethod(rawValue: normalised) ?? .GET
guard let knownMethod = HTTPMethod(rawValue: normalised) else {
throw NetworkAccessDenied(
url: url,
reason: "HTTP method \(normalised) not supported by network gate")
}
if !allowedMethods.contains(knownMethod) {
throw NetworkAccessDenied(
url: url,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
// AUTO-GENERATED by BridgeGeneratorTool. Do not edit by hand.
// Regenerate with: bash Tools/regen-foundation-bridge.sh
import Foundation
import ShellKit
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

extension FoundationBridges {
nonisolated(unsafe) static let bundle: [String: Bridge] = {
var d: [String: Bridge] = [
"static let Bundle.main": .staticValue(boxOpaque(Bundle.main, typeName: "Bundle")),
"func Bundle.load()": .method { receiver, args in
guard args.count == 0 else {
throw RuntimeError.invalid("Bundle.load: expected 0 argument(s), got \(args.count)")
}
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
return .bool(recv.load())
},
"var Bundle.isLoaded: Bool": .computed { receiver in
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
return .bool(recv.isLoaded)
},
"func Bundle.preflight()": .method { receiver, args in
guard args.count == 0 else {
throw RuntimeError.invalid("Bundle.preflight: expected 0 argument(s), got \(args.count)")
}
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
do {
try recv.preflight()
return .void
} catch {
throw UserThrowSignal(value: .opaque(typeName: "Error", value: error))
}
},
"func Bundle.loadAndReturnError()": .method { receiver, args in
guard args.count == 0 else {
throw RuntimeError.invalid("Bundle.loadAndReturnError: expected 0 argument(s), got \(args.count)")
}
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
do {
try recv.loadAndReturnError()
return .void
} catch {
throw UserThrowSignal(value: .opaque(typeName: "Error", value: error))
}
},
"var Bundle.bundleURL: URL": .computed { receiver in
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
return boxOpaque(recv.bundleURL, typeName: "URL")
},
"var Bundle.resourceURL: URL?": .computed { receiver in
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
if let _v = recv.resourceURL {
return .optional(boxOpaque(_v, typeName: "URL"))
}
return .optional(nil)
},
"var Bundle.executableURL: URL?": .computed { receiver in
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
if let _v = recv.executableURL {
return .optional(boxOpaque(_v, typeName: "URL"))
}
return .optional(nil)
},
"var Bundle.privateFrameworksURL: URL?": .computed { receiver in
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
if let _v = recv.privateFrameworksURL {
return .optional(boxOpaque(_v, typeName: "URL"))
}
return .optional(nil)
},
"var Bundle.sharedFrameworksURL: URL?": .computed { receiver in
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
if let _v = recv.sharedFrameworksURL {
return .optional(boxOpaque(_v, typeName: "URL"))
}
return .optional(nil)
},
"var Bundle.sharedSupportURL: URL?": .computed { receiver in
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
if let _v = recv.sharedSupportURL {
return .optional(boxOpaque(_v, typeName: "URL"))
}
return .optional(nil)
},
"var Bundle.builtInPlugInsURL: URL?": .computed { receiver in
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
if let _v = recv.builtInPlugInsURL {
return .optional(boxOpaque(_v, typeName: "URL"))
}
return .optional(nil)
},
"var Bundle.appStoreReceiptURL: URL?": .computed { receiver in
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
if let _v = recv.appStoreReceiptURL {
return .optional(boxOpaque(_v, typeName: "URL"))
}
return .optional(nil)
},
"var Bundle.bundlePath: String": .computed { receiver in
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
return .string(recv.bundlePath)
},
"var Bundle.resourcePath: String?": .computed { receiver in
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
if let _v = recv.resourcePath {
return .optional(.string(_v))
}
return .optional(nil)
},
"var Bundle.executablePath: String?": .computed { receiver in
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
if let _v = recv.executablePath {
return .optional(.string(_v))
}
return .optional(nil)
},
"var Bundle.privateFrameworksPath: String?": .computed { receiver in
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
if let _v = recv.privateFrameworksPath {
return .optional(.string(_v))
}
return .optional(nil)
},
"var Bundle.sharedFrameworksPath: String?": .computed { receiver in
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
if let _v = recv.sharedFrameworksPath {
return .optional(.string(_v))
}
return .optional(nil)
},
"var Bundle.sharedSupportPath: String?": .computed { receiver in
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
if let _v = recv.sharedSupportPath {
return .optional(.string(_v))
}
return .optional(nil)
},
"var Bundle.builtInPlugInsPath: String?": .computed { receiver in
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
if let _v = recv.builtInPlugInsPath {
return .optional(.string(_v))
}
return .optional(nil)
},
"var Bundle.bundleIdentifier: String?": .computed { receiver in
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
if let _v = recv.bundleIdentifier {
return .optional(.string(_v))
}
return .optional(nil)
},
"var Bundle.developmentLocalization: String?": .computed { receiver in
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
if let _v = recv.developmentLocalization {
return .optional(.string(_v))
}
return .optional(nil)
},
"init Bundle(path:)": .`init` { args in
guard args.count == 1 else {
throw RuntimeError.invalid("init Bundle(path:): expected 1 argument(s), got \(args.count)")
}
let arg0 = try unboxString(args[0])
do {
try await authorizePath(arg0, for: .read)
} catch {
throw UserThrowSignal(value: .opaque(typeName: "Error", value: error))
}
if let _v = await Bundle(path: arg0) {
return .optional(boxOpaque(_v, typeName: "Bundle"))
}
return .optional(nil)
},
"init Bundle(url:)": .`init` { args in
guard args.count == 1 else {
throw RuntimeError.invalid("init Bundle(url:): expected 1 argument(s), got \(args.count)")
}
let arg0 = try unboxOpaque(args[0], as: URL.self, typeName: "URL")
do {
try await authorizePath(arg0, for: .read)
} catch {
throw UserThrowSignal(value: .opaque(typeName: "Error", value: error))
}
if let _v = await Bundle(url: arg0) {
return .optional(boxOpaque(_v, typeName: "Bundle"))
}
return .optional(nil)
},
"init Bundle(identifier:)": .`init` { args in
guard args.count == 1 else {
throw RuntimeError.invalid("init Bundle(identifier:): expected 1 argument(s), got \(args.count)")
}
if let _v = Bundle(identifier: try unboxString(args[0])) {
return .optional(boxOpaque(_v, typeName: "Bundle"))
}
return .optional(nil)
},
"func Bundle.url()": .method { receiver, args in
guard args.count == 1 else {
throw RuntimeError.invalid("Bundle.url: expected 1 argument(s), got \(args.count)")
}
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
if let _v = recv.url(forAuxiliaryExecutable: try unboxString(args[0])) {
return .optional(boxOpaque(_v, typeName: "URL"))
}
return .optional(nil)
},
"func Bundle.path()": .method { receiver, args in
guard args.count == 1 else {
throw RuntimeError.invalid("Bundle.path: expected 1 argument(s), got \(args.count)")
}
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
if let _v = recv.path(forAuxiliaryExecutable: try unboxString(args[0])) {
return .optional(.string(_v))
}
return .optional(nil)
},
]
#if canImport(Darwin)
d["func Bundle.unload()"] = .method { receiver, args in
guard args.count == 0 else {
throw RuntimeError.invalid("Bundle.unload: expected 0 argument(s), got \(args.count)")
}
let recv: Bundle = try unboxOpaque(receiver, as: Bundle.self, typeName: "Bundle")
return .bool(recv.unload())
}
d["static let Bundle.didLoadNotification"] = .staticValue(boxOpaque(Bundle.didLoadNotification, typeName: "NSNotification.Name"))
#endif
return d
}()
}
Loading
Loading