fix(plugins): trust official signing keys for offline installation

This commit is contained in:
t8y2 committed 2026-09-17 18:03:47 +08:00
1 parent 65b820e6a9
commit fb25a66d7a
5 files changed
+146 -9

No files matched your search

@@ -24,6 +24,7 @@ import { useQueryStore } from "@/stores/queryStore";
import type { InstalledPlugin, PluginInstallResult, PluginRepository, PluginRepositoryCatalogResult, PluginTrustedKey } from "@/types/database";
import { useI18n } from "vue-i18n";
import { safeLocalStorageGet, safeLocalStorageSet } from "@/lib/backend/safeStorage";
import { translateBackendError } from "@/i18n/backend-errors";
const props = defineProps<{
focusTarget?: PluginCenterFocus | null;
@@ -389,7 +390,7 @@ async function installPlugin(source: string | File) {
const result = await api.installPluginPackage(source, allowUnsigned.value);
await finishInstall(result);
} catch (cause) {
toast(cause instanceof Error ? cause.message : String(cause), 8000);
toast(translateBackendError(t, cause), 8000);
} finally {
installing.value = false;
}
@@ -422,7 +423,7 @@ async function installPluginFromUrl() {
installUrl.value = "";
await finishInstall(result);
} catch (cause) {
toast(cause instanceof Error ? cause.message : String(cause), 8000);
toast(translateBackendError(t, cause), 8000);
} finally {
unlisten?.();
urlInstalling.value = false;
+20
View File
@@ -454,6 +454,26 @@ describe("backend error translation", () => {
expect(translateBackendError(t, error, "ClickHouse error: table analytics.events does not exist")).toBe(`${t("backendErrors.legacy")}\n\nClickHouse error: table analytics.events does not exist`);
});
test("renders plugin signature failures without exposing the JSON error envelope", () => {
const detail = "Plugin package is signed by untrusted key 'dbx-store-release-2026'";
const error = JSON.stringify({
version: 1,
code: "DBX-LEGACY-0001",
messageKey: "backendErrors.legacy",
messageParams: {},
source: "legacyBackend",
origin: { subsystem: "backend", adapter: "legacy" },
operationOutcome: "unknown",
detail,
});
for (const locale of ["zh-CN", "en"] as const) {
const t = translatorFor(locale);
expect(translateBackendError(t, error)).toBe(`${t("backendErrors.legacy")}\n\n${detail}`);
expect(translateBackendError(t, new Error(error))).toBe(`${t("backendErrors.legacy")}\n\n${detail}`);
}
});
test("does not append the generic transport fallback to a structured error", () => {
const t = translatorFor("zh-CN");
const error = {
@@ -26,6 +26,16 @@ describe("plugin center integration", () => {
expect(driverStoreSource).not.toContain("PluginContributionsPanel");
});
it("formats structured backend errors for local and URL package installs", () => {
const localInstall = pluginCenterSource.slice(pluginCenterSource.indexOf("async function installPlugin("), pluginCenterSource.indexOf("function isHttpPackageUrl("));
const urlInstall = pluginCenterSource.slice(pluginCenterSource.indexOf("async function installPluginFromUrl("), pluginCenterSource.indexOf("const urlProgressPercent"));
for (const installSource of [localInstall, urlInstall]) {
expect(installSource).toContain("toast(translateBackendError(t, cause), 8000)");
expect(installSource).not.toContain("String(cause)");
}
});
it("creates and edits plugin connections in the unified connection dialog", () => {
expect(appDialogsSource).toContain(':edit-config="editConfig"');
expect(appDialogsSource).toContain(':plugin-provider="connectionPluginProvider"');