























@@ -301,6 +301,25 @@ function createManifestRegistryFixture(): PluginManifestRegistry {
301301providers: [],
302302cliBackends: [],
303303},
304+{
305+id: "external-hook-capability",
306+channels: [],
307+activation: {
308+onCapabilities: ["hook"],
309+},
310+origin: "global",
311+enabledByDefault: undefined,
312+providers: [],
313+cliBackends: [],
314+},
315+{
316+id: "external-hook-policy",
317+channels: [],
318+origin: "global",
319+enabledByDefault: undefined,
320+providers: [],
321+cliBackends: [],
322+},
304323{
305324id: "lossless-claw",
306325kind: "context-engine",
@@ -847,6 +866,173 @@ describe("resolveGatewayStartupPluginIds", () => {
847866});
848867});
849868869+it("loads explicit hook-capability plugins at startup", () => {
870+expectStartupPluginIdsCase({
871+config: createStartupConfig({
872+enabledPluginIds: ["external-hook-capability"],
873+allowPluginIds: ["external-hook-capability"],
874+noConfiguredChannels: true,
875+memorySlot: "none",
876+}),
877+expected: ["external-hook-capability"],
878+});
879+});
880+881+it("does not ambient-load hook-capability plugins at startup", () => {
882+expectStartupPluginIdsCase({
883+config: createStartupConfig({
884+noConfiguredChannels: true,
885+memorySlot: "none",
886+}),
887+expected: ["browser"],
888+});
889+});
890+891+it("blocks hook-capability plugins when plugins are globally disabled", () => {
892+expectStartupPluginIdsCase({
893+config: {
894+channels: {},
895+plugins: {
896+enabled: false,
897+allow: ["external-hook-capability"],
898+slots: { memory: "none" },
899+entries: {
900+"external-hook-capability": {
901+enabled: true,
902+},
903+},
904+},
905+},
906+expected: [],
907+});
908+});
909+910+it("blocks hook-capability plugins when explicitly denied", () => {
911+expectStartupPluginIdsCase({
912+config: {
913+channels: {},
914+plugins: {
915+allow: ["external-hook-capability"],
916+deny: ["external-hook-capability"],
917+slots: { memory: "none" },
918+entries: {
919+"external-hook-capability": {
920+enabled: true,
921+},
922+},
923+},
924+},
925+expected: [],
926+});
927+});
928+929+it("loads explicit hook-policy plugins at startup", () => {
930+expectStartupPluginIdsCase({
931+config: {
932+channels: {},
933+plugins: {
934+slots: { memory: "none" },
935+entries: {
936+browser: {
937+enabled: false,
938+},
939+"external-hook-policy": {
940+hooks: {
941+allowConversationAccess: true,
942+allowPromptInjection: true,
943+},
944+},
945+},
946+},
947+},
948+expected: ["external-hook-policy"],
949+});
950+});
951+952+it.each([
953+["conversation access", { allowConversationAccess: true }],
954+["prompt injection", { allowPromptInjection: true }],
955+] as const)("loads hook-policy plugins with only %s enabled", (_name, hooks) => {
956+expectStartupPluginIdsCase({
957+config: {
958+channels: {},
959+plugins: {
960+slots: { memory: "none" },
961+entries: {
962+browser: {
963+enabled: false,
964+},
965+"external-hook-policy": {
966+ hooks,
967+},
968+},
969+},
970+},
971+expected: ["external-hook-policy"],
972+});
973+});
974+975+it("keeps hook-policy plugins behind restrictive allowlists", () => {
976+expectStartupPluginIdsCase({
977+config: {
978+channels: {},
979+plugins: {
980+allow: ["browser"],
981+slots: { memory: "none" },
982+entries: {
983+browser: {
984+enabled: false,
985+},
986+"external-hook-policy": {
987+hooks: {
988+allowPromptInjection: true,
989+},
990+},
991+},
992+},
993+},
994+expected: [],
995+});
996+});
997+998+it("does not let effective-only hook policy bypass the authored startup allowlist", () => {
999+const activationSourceConfig = {
1000+channels: {},
1001+plugins: {
1002+allow: ["browser"],
1003+slots: { memory: "none" },
1004+entries: {
1005+browser: {
1006+enabled: false,
1007+},
1008+},
1009+},
1010+} as OpenClawConfig;
1011+const runtimeConfig = {
1012+channels: {},
1013+plugins: {
1014+allow: ["browser", "external-hook-policy"],
1015+slots: { memory: "none" },
1016+entries: {
1017+browser: {
1018+enabled: false,
1019+},
1020+"external-hook-policy": {
1021+hooks: {
1022+allowPromptInjection: true,
1023+},
1024+},
1025+},
1026+},
1027+} as OpenClawConfig;
1028+1029+expectStartupPluginIdsCase({
1030+config: runtimeConfig,
1031+ activationSourceConfig,
1032+expected: [],
1033+});
1034+});
1035+8501036it("starts bundled sidecars selected by root config activation paths", () => {
8511037const rawConfig = {
8521038browser: {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。