























@@ -53,6 +53,7 @@ import {
5353claimPluginInteractiveCallbackDedupe,
5454commitPluginInteractiveCallbackDedupe,
5555} from "./interactive-state.js";
56+import { warnWhenAllowlistIsOpen } from "./loader-provenance.js";
5657import {
5758testing,
5859clearPluginLoaderCache,
@@ -8724,6 +8725,109 @@ module.exports = {
87248725).toHaveLength(0);
87258726});
872687278728+it("includes actionable plugins.allow remediation hints in the open allowlist warning", () => {
8729+useNoBundledPlugins();
8730+clearPluginLoaderCache();
8731+8732+const { workspaceDir } = writeWorkspacePlugin({
8733+id: "warn-open-allow-remediation",
8734+});
8735+const warnings: string[] = [];
8736+loadOpenClawPlugins({
8737+cache: false,
8738+ workspaceDir,
8739+logger: createWarningLogger(warnings),
8740+config: {
8741+plugins: {
8742+enabled: true,
8743+},
8744+},
8745+});
8746+8747+const openAllowWarning = warnings.find((msg) => msg.includes("plugins.allow is empty"));
8748+expect(openAllowWarning).toBeDefined();
8749+expect(openAllowWarning).toContain('"warn-open-allow-remediation"');
8750+expect(openAllowWarning).toContain('"plugins": { "allow": [');
8751+expect(openAllowWarning).toContain("openclaw plugins list --enabled --verbose");
8752+expect(openAllowWarning).toContain("openclaw plugins inspect warn-open-allow-remediation");
8753+});
8754+8755+it("includes actionable plugins.allow remediation hints in the untracked-provenance warning", () => {
8756+useNoBundledPlugins();
8757+const stateDir = makeTempDir();
8758+withEnv({ OPENCLAW_STATE_DIR: stateDir }, () => {
8759+const globalDir = path.join(stateDir, "extensions", "warn-untracked-remediation");
8760+mkdirSafe(globalDir);
8761+writePlugin({
8762+id: "warn-untracked-remediation",
8763+body: simplePluginBody("warn-untracked-remediation"),
8764+dir: globalDir,
8765+filename: "index.cjs",
8766+});
8767+8768+const warnings: string[] = [];
8769+const registry = loadOpenClawPlugins({
8770+cache: false,
8771+logger: createWarningLogger(warnings),
8772+config: {
8773+plugins: {
8774+enabled: true,
8775+},
8776+},
8777+});
8778+8779+const untrackedWarning = warnings.find(
8780+(msg) =>
8781+msg.includes("warn-untracked-remediation") &&
8782+msg.includes("loaded without install/load-path provenance"),
8783+);
8784+expect(untrackedWarning).toBeDefined();
8785+expect(untrackedWarning).toContain('"warn-untracked-remediation"');
8786+expect(untrackedWarning).toContain("openclaw plugins inspect warn-untracked-remediation");
8787+expect(untrackedWarning).toContain("reinstall from a trusted source");
8788+8789+const diagnostic = registry.diagnostics.find(
8790+(entry) =>
8791+entry.pluginId === "warn-untracked-remediation" &&
8792+entry.message.includes("loaded without install/load-path provenance"),
8793+);
8794+expect(diagnostic?.message).toContain('"warn-untracked-remediation"');
8795+expect(diagnostic?.message).toContain("openclaw plugins inspect warn-untracked-remediation");
8796+expect(diagnostic?.message).toContain("reinstall from a trusted source");
8797+});
8798+});
8799+8800+it("omits the truncated plugins.allow snippet when more than six plugins are discovered", () => {
8801+const ids = Array.from({ length: 8 }, (_, index) => `discovered-plugin-${index + 1}`);
8802+const warnings: string[] = [];
8803+const seenWarningKeys = new Set<string>();
8804+const cache = {
8805+hasOpenAllowlistWarning(key: string) {
8806+return seenWarningKeys.has(key);
8807+},
8808+recordOpenAllowlistWarning(key: string) {
8809+seenWarningKeys.add(key);
8810+},
8811+};
8812+warnWhenAllowlistIsOpen({
8813+emitWarning: true,
8814+logger: createWarningLogger(warnings),
8815+pluginsEnabled: true,
8816+allow: [],
8817+warningCacheKey: "truncated",
8818+warningCache: cache,
8819+discoverablePlugins: ids.map((id) => ({ id, source: `/tmp/${id}`, origin: "global" })),
8820+});
8821+8822+expect(warnings).toHaveLength(1);
8823+const message = warnings[0] ?? "";
8824+expect(message).toContain("plugins.allow is empty");
8825+expect(message).toContain("(+2 more)");
8826+expect(message).not.toContain('"plugins": { "allow": [');
8827+expect(message).toContain("openclaw plugins list --enabled --verbose");
8828+expect(message).toContain("openclaw plugins inspect <id>");
8829+});
8830+87278831it("handles workspace-discovered plugins according to trust and precedence", () => {
87288832useNoBundledPlugins();
87298833const scenarios = [
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。