fix: harden external protocol handling and unblock dev:electron (#4863)
* fix(desktop): harden external protocol handling * fix(build): avoid optional peer resolution in dev
This commit is contained in:
parent
8963930f29
commit
b465ec1463
|
|
@ -18,6 +18,7 @@ export default defineConfig({
|
|||
"@pkg": resolve("./package.json"),
|
||||
"@locales": resolve("../../locales"),
|
||||
"~": resolve("./layer/main/src"),
|
||||
"utf-8-validate": resolve("./layer/main/src/shims/utf-8-validate.cjs"),
|
||||
},
|
||||
},
|
||||
define: {
|
||||
|
|
|
|||
|
|
@ -82,19 +82,24 @@ class WindowManagerStatic {
|
|||
refreshBound(window, this.config.refreshBoundDelay)
|
||||
})
|
||||
|
||||
window.webContents.setWindowOpenHandler((details) => {
|
||||
shell.openExternal(details.url)
|
||||
return { action: "deny" }
|
||||
})
|
||||
|
||||
const handleExternalProtocol = async (e: Event, url: string, window: BrowserWindow) => {
|
||||
const { protocol } = new URL(url)
|
||||
|
||||
if (this.config.ignoreProtocols.includes(protocol.slice(0, -1) as any)) {
|
||||
return
|
||||
const parseProtocol = (url: string) => {
|
||||
try {
|
||||
return new URL(url).protocol.slice(0, -1)
|
||||
} catch {
|
||||
logger.warn("Blocked external URL with invalid format", { url })
|
||||
return null
|
||||
}
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
const isIgnoredProtocol = (
|
||||
protocol: string,
|
||||
): protocol is (typeof this.config.ignoreProtocols)[number] => {
|
||||
return this.config.ignoreProtocols.includes(
|
||||
protocol as (typeof this.config.ignoreProtocols)[number],
|
||||
)
|
||||
}
|
||||
|
||||
const confirmAndOpenExternalProtocol = async (url: string) => {
|
||||
const caller = callWindowExpose(window)
|
||||
const confirm = await caller.dialog.ask({
|
||||
title: t("dialog.openExternalApp.title"),
|
||||
|
|
@ -108,15 +113,57 @@ class WindowManagerStatic {
|
|||
if (!confirm) {
|
||||
return
|
||||
}
|
||||
shell.openExternal(url)
|
||||
void shell.openExternal(url)
|
||||
}
|
||||
|
||||
window.webContents.setWindowOpenHandler((details) => {
|
||||
const protocol = parseProtocol(details.url)
|
||||
if (!protocol) {
|
||||
return { action: "deny" }
|
||||
}
|
||||
|
||||
if (protocol === "http" || protocol === "https") {
|
||||
void shell.openExternal(details.url)
|
||||
return { action: "deny" }
|
||||
}
|
||||
|
||||
if (isIgnoredProtocol(protocol)) {
|
||||
logger.warn("Blocked window.open for ignored protocol", {
|
||||
protocol,
|
||||
url: details.url,
|
||||
})
|
||||
return { action: "deny" }
|
||||
}
|
||||
|
||||
void confirmAndOpenExternalProtocol(details.url)
|
||||
return { action: "deny" }
|
||||
})
|
||||
|
||||
const handleExternalProtocol = async (e: Event, url: string) => {
|
||||
const protocol = parseProtocol(url)
|
||||
if (!protocol) {
|
||||
e.preventDefault()
|
||||
return
|
||||
}
|
||||
|
||||
if (isIgnoredProtocol(protocol)) {
|
||||
return
|
||||
}
|
||||
e.preventDefault()
|
||||
|
||||
await confirmAndOpenExternalProtocol(url)
|
||||
}
|
||||
|
||||
// Handle main window external links
|
||||
window.webContents.on("will-navigate", (e, url) => handleExternalProtocol(e, url, window))
|
||||
window.webContents.on("will-navigate", (e, url) => {
|
||||
void handleExternalProtocol(e, url)
|
||||
})
|
||||
|
||||
// Handle webview external links
|
||||
window.webContents.on("did-attach-webview", (_, webContents) => {
|
||||
webContents.on("will-navigate", (e, url) => handleExternalProtocol(e, url, window))
|
||||
webContents.on("will-navigate", (e, url) => {
|
||||
void handleExternalProtocol(e, url)
|
||||
})
|
||||
})
|
||||
|
||||
if (isWindows) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
"use strict"
|
||||
|
||||
const { isUtf8 } = require("node:buffer")
|
||||
|
||||
module.exports = function isValidUTF8(buffer) {
|
||||
if (typeof isUtf8 === "function") {
|
||||
return isUtf8(buffer)
|
||||
}
|
||||
|
||||
try {
|
||||
new TextDecoder("utf-8", { fatal: true }).decode(buffer)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import type { env as EnvType } from "@follow/shared/env.desktop"
|
|||
import legacy from "@vitejs/plugin-legacy"
|
||||
import { minify as htmlMinify } from "html-minifier-terser"
|
||||
import { cyan, dim, green } from "kolorist"
|
||||
import { parseHTML } from "linkedom"
|
||||
import { parseHTML } from "linkedom/worker"
|
||||
import { join, resolve } from "pathe"
|
||||
import type { PluginOption, ResolvedConfig, ViteDevServer } from "vite"
|
||||
import { defineConfig, loadEnv } from "vite"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Readability } from "@mozilla/readability"
|
||||
import chardet from "chardet"
|
||||
import DOMPurify from "dompurify"
|
||||
import { parseHTML } from "linkedom"
|
||||
import { parseHTML } from "linkedom/worker"
|
||||
|
||||
const isDev = process.env.NODE_ENV === "development"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue