fix(plugins): trust official signing keys for offline installation
This commit is contained in:
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;
|
||||
|
||||
@@ -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"');
|
||||
|
||||
Reference in new issue
Block a user