mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 02:06:43 +00:00
fix(macos): show local ClickClack session discussions (#111883)
* fix(macos): show local ClickClack session discussions * fix(macos): support loopback aliases in discussion embeds * fix(macos): allow trusted hosted ClickClack embeds * fix(macos): trust only dashboard-initiated discussion frames
This commit is contained in:
@@ -903,15 +903,35 @@ final class DashboardWindowController: NSWindowController, WKNavigationDelegate,
|
||||
return String(raw.dropFirst().dropLast())
|
||||
}
|
||||
|
||||
static func shouldAllowNavigation(to url: URL, dashboardURL: URL) -> Bool {
|
||||
static func shouldAllowNavigation(
|
||||
to url: URL,
|
||||
dashboardURL: URL,
|
||||
isMainFrame: Bool,
|
||||
isTrustedDashboardSource: Bool = false) -> Bool
|
||||
{
|
||||
guard let scheme = url.scheme?.lowercased() else { return true }
|
||||
if scheme == "about" || scheme == "blob" || scheme == "data" {
|
||||
return true
|
||||
}
|
||||
guard scheme == "http" || scheme == "https" else { return false }
|
||||
return url.scheme?.lowercased() == dashboardURL.scheme?.lowercased() &&
|
||||
url.host?.lowercased() == dashboardURL.host?.lowercased() &&
|
||||
url.port == dashboardURL.port
|
||||
let dashboardScheme = dashboardURL.scheme?.lowercased()
|
||||
let dashboardHost = dashboardURL.host?.lowercased()
|
||||
let host = url.host?.lowercased()
|
||||
if scheme == dashboardScheme, host == dashboardHost, url.port == dashboardURL.port {
|
||||
return true
|
||||
}
|
||||
guard !isMainFrame,
|
||||
isTrustedDashboardSource,
|
||||
host?.isEmpty == false,
|
||||
url.user == nil,
|
||||
url.password == nil
|
||||
else {
|
||||
return false
|
||||
}
|
||||
let components = url.path.split(separator: "/", omittingEmptySubsequences: true)
|
||||
return components.count == 4 &&
|
||||
components[0] == "embed" &&
|
||||
(components[1] == "channel" || components[1] == "thread")
|
||||
}
|
||||
|
||||
static func shouldAllowBrowserNavigation(to url: URL, isMainFrame: Bool) -> Bool {
|
||||
@@ -1118,7 +1138,15 @@ extension DashboardWindowController {
|
||||
decisionHandler: decisionHandler)
|
||||
return
|
||||
}
|
||||
if Self.shouldAllowNavigation(to: url, dashboardURL: self.currentURL) {
|
||||
if Self.shouldAllowNavigation(
|
||||
to: url,
|
||||
dashboardURL: self.currentURL,
|
||||
isMainFrame: navigationAction.targetFrame?.isMainFrame == true,
|
||||
isTrustedDashboardSource: navigationAction.sourceFrame.isMainFrame &&
|
||||
Self.isTrustedLinkSource(
|
||||
navigationAction.sourceFrame.request.url,
|
||||
dashboardURL: self.currentURL))
|
||||
{
|
||||
decisionHandler(.allow)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -188,19 +188,58 @@ struct DashboardWindowSmokeTests {
|
||||
let staleEndpoint = try #require(URL(string: "http://127.0.0.1:18790/control/chat"))
|
||||
#expect(try DashboardWindowController.shouldAllowNavigation(
|
||||
to: #require(URL(string: "http://127.0.0.1:18789/control/chat")),
|
||||
dashboardURL: dashboard))
|
||||
dashboardURL: dashboard,
|
||||
isMainFrame: true))
|
||||
#expect(try !DashboardWindowController.shouldAllowNavigation(
|
||||
to: #require(URL(string: "https://docs.openclaw.ai/")),
|
||||
dashboardURL: dashboard))
|
||||
dashboardURL: dashboard,
|
||||
isMainFrame: true))
|
||||
#expect(!DashboardWindowController.shouldAllowNavigation(
|
||||
to: staleEndpoint,
|
||||
dashboardURL: dashboard))
|
||||
dashboardURL: dashboard,
|
||||
isMainFrame: true))
|
||||
#expect(!DashboardWindowController.shouldOpenExternalDashboardNavigation(
|
||||
staleEndpoint,
|
||||
navigationType: .backForward,
|
||||
buttonNumber: 1))
|
||||
}
|
||||
|
||||
@Test func `dashboard permits only trusted ClickClack discussion subframes`() throws {
|
||||
let dashboard = try #require(URL(string: "http://127.0.0.1:18789/control/"))
|
||||
let channel = try #require(URL(string: "http://127.0.0.1:18890/embed/channel/T01/C01"))
|
||||
let thread = try #require(URL(string: "http://127.0.0.1:18890/embed/thread/T01/M01"))
|
||||
let hostnameAlias = try #require(URL(string: "http://localhost:18890/embed/channel/T01/C01"))
|
||||
let ipv6Alias = try #require(URL(string: "http://[::1]:18890/embed/thread/T01/M01"))
|
||||
let credentialedFrame = try #require(URL(string: "http://user:pass@localhost:18890/embed/channel/T01/C01"))
|
||||
let unrelatedPath = try #require(URL(string: "http://127.0.0.1:18890/admin"))
|
||||
let externalFrame = try #require(URL(string: "https://clickclack.example/embed/channel/T01/C01"))
|
||||
let externalHTTPFrame = try #require(URL(string: "http://clickclack.example/embed/thread/T01/M01"))
|
||||
let localFile = try #require(URL(string: "file:///tmp/discussion.html"))
|
||||
|
||||
#expect(DashboardWindowController.shouldAllowNavigation(
|
||||
to: channel, dashboardURL: dashboard, isMainFrame: false, isTrustedDashboardSource: true))
|
||||
#expect(DashboardWindowController.shouldAllowNavigation(
|
||||
to: thread, dashboardURL: dashboard, isMainFrame: false, isTrustedDashboardSource: true))
|
||||
#expect(DashboardWindowController.shouldAllowNavigation(
|
||||
to: hostnameAlias, dashboardURL: dashboard, isMainFrame: false, isTrustedDashboardSource: true))
|
||||
#expect(DashboardWindowController.shouldAllowNavigation(
|
||||
to: ipv6Alias, dashboardURL: dashboard, isMainFrame: false, isTrustedDashboardSource: true))
|
||||
#expect(DashboardWindowController.shouldAllowNavigation(
|
||||
to: externalFrame, dashboardURL: dashboard, isMainFrame: false, isTrustedDashboardSource: true))
|
||||
#expect(DashboardWindowController.shouldAllowNavigation(
|
||||
to: externalHTTPFrame, dashboardURL: dashboard, isMainFrame: false, isTrustedDashboardSource: true))
|
||||
#expect(!DashboardWindowController.shouldAllowNavigation(
|
||||
to: channel, dashboardURL: dashboard, isMainFrame: true))
|
||||
#expect(!DashboardWindowController.shouldAllowNavigation(
|
||||
to: credentialedFrame, dashboardURL: dashboard, isMainFrame: false, isTrustedDashboardSource: true))
|
||||
#expect(!DashboardWindowController.shouldAllowNavigation(
|
||||
to: unrelatedPath, dashboardURL: dashboard, isMainFrame: false, isTrustedDashboardSource: true))
|
||||
#expect(!DashboardWindowController.shouldAllowNavigation(
|
||||
to: externalFrame, dashboardURL: dashboard, isMainFrame: false, isTrustedDashboardSource: false))
|
||||
#expect(!DashboardWindowController.shouldAllowNavigation(
|
||||
to: localFile, dashboardURL: dashboard, isMainFrame: false, isTrustedDashboardSource: true))
|
||||
}
|
||||
|
||||
@Test func `dashboard navigation shortcuts target the focused browser`() throws {
|
||||
let dashboard = try #require(URL(string: "http://127.0.0.1:18789/control/"))
|
||||
let controller = DashboardWindowController(
|
||||
|
||||
Reference in New Issue
Block a user