// Copyright (c) 2026 Lark Technologies Pte. Ltd. // SPDX-License-Identifier: MIT package cmd import ( "context" "errors" "os" "path/filepath" "strings" "sync/atomic" "testing" "github.com/spf13/cobra" "github.com/larksuite/cli/errs" "github.com/larksuite/cli/extension/platform" "github.com/larksuite/cli/internal/cmdpolicy" "github.com/larksuite/cli/internal/cmdutil" "github.com/larksuite/cli/internal/hook" "github.com/larksuite/cli/internal/output" internalplatform "github.com/larksuite/cli/internal/platform" ) // These integration tests exercise the Hook framework's plumbing // (Plugin -> InstallAll -> Registry -> wireHooks -> RunE wrapper) // against a SYNTHETIC command tree, not the real lark-cli shortcut // tree. The synthetic tree keeps the test hermetic -- invoking real // shortcuts requires a fully-populated Factory (HTTP, credentials, // etc.) which is out of scope for a hook plumbing test. // // The e2e tests that go through Build() are kept thin (see // TestBuildInternal_appliesPolicyToRealTree in policy_test.go); they // assert plumbing existence (Hidden flag, etc.) without invoking // shortcuts. type fakeIntegrationPlugin struct { name string caps platform.Capabilities rule *platform.Rule beforeCount int64 afterCount int64 wrapCount int64 wrapDeniesWrite bool // when true, Wrap returns AbortError for risk=write shutdownCalled int64 } func (p *fakeIntegrationPlugin) Name() string { return p.name } func (p *fakeIntegrationPlugin) Version() string { return "0.0.1" } func (p *fakeIntegrationPlugin) Capabilities() platform.Capabilities { return p.caps } func (p *fakeIntegrationPlugin) Install(r platform.Registrar) error { if p.caps.Restricts && p.rule != nil { r.Restrict(p.rule) } r.Observe(platform.Before, "audit-pre", platform.All(), func(context.Context, platform.Invocation) { atomic.AddInt64(&p.beforeCount, 1) }) r.Observe(platform.After, "audit-post", platform.All(), func(context.Context, platform.Invocation) { atomic.AddInt64(&p.afterCount, 1) }) r.Wrap("policy", platform.ByWrite(), func(next platform.Handler) platform.Handler { return func(ctx context.Context, inv platform.Invocation) error { atomic.AddInt64(&p.wrapCount, 1) if p.wrapDeniesWrite { return &platform.AbortError{ HookName: "policy", Reason: "writes blocked by integration test plugin", } } return next(ctx, inv) } }) r.On(platform.Shutdown, "flush", func(context.Context, *platform.LifecycleContext) error { atomic.AddInt64(&p.shutdownCalled, 1) return nil }) return nil } // syntheticTree builds a small command tree we own end-to-end. The leaf // has risk=write so the Wrap's ByWrite() selector matches. func syntheticTree() (*cobra.Command, *cobra.Command) { root := &cobra.Command{Use: "lark-cli"} group := &cobra.Command{Use: "docs"} root.AddCommand(group) leaf := &cobra.Command{ Use: "+write", RunE: func(*cobra.Command, []string) error { return nil }, } cmdutil.SetRisk(leaf, "write") group.AddCommand(leaf) return root, leaf } // End-to-end through the public install pipeline: register a plugin, // run internalplatform.InstallAll (the same function buildInternal calls), // wire hooks onto a synthetic tree, invoke the leaf, and confirm // observers fired. func TestPluginPipeline_observersWired(t *testing.T) { platform.ResetForTesting() t.Cleanup(platform.ResetForTesting) plugin := &fakeIntegrationPlugin{ name: "audit-plugin", caps: platform.Capabilities{FailurePolicy: platform.FailOpen}, } platform.Register(plugin) result, err := internalplatform.InstallAll(platform.RegisteredPlugins(), nil) if err != nil { t.Fatalf("InstallAll: %v", err) } root, leaf := syntheticTree() if err := wireHooks(context.Background(), root, result.Registry); err != nil { t.Fatalf("wireHooks: %v", err) } _ = leaf.RunE(leaf, nil) if got := atomic.LoadInt64(&plugin.beforeCount); got != 1 { t.Errorf("Before observer fired %d times, want 1", got) } if got := atomic.LoadInt64(&plugin.afterCount); got != 1 { t.Errorf("After observer fired %d times, want 1", got) } if got := atomic.LoadInt64(&plugin.wrapCount); got != 1 { t.Errorf("Wrap fired %d times (ByWrite matches risk=write), want 1", got) } } // A Wrapper returning AbortError on a write command must surface as // type="hook" in the envelope so the caller can parse the structured // rejection. func TestPluginPipeline_wrapAbortReachesEnvelope(t *testing.T) { platform.ResetForTesting() t.Cleanup(platform.ResetForTesting) plugin := &fakeIntegrationPlugin{ name: "policy-plugin", caps: platform.Capabilities{FailurePolicy: platform.FailOpen}, wrapDeniesWrite: true, } platform.Register(plugin) result, err := internalplatform.InstallAll(platform.RegisteredPlugins(), nil) if err != nil { t.Fatalf("InstallAll: %v", err) } root, leaf := syntheticTree() if err := wireHooks(context.Background(), root, result.Registry); err != nil { t.Fatalf("wireHooks: %v", err) } err = leaf.RunE(leaf, nil) var verr *errs.ValidationError if !errors.As(err, &verr) { t.Fatalf("expected *errs.ValidationError, got %T %+v", err, err) } if verr.Subtype != errs.SubtypeFailedPrecondition { t.Errorf("subtype = %q, want failed_precondition", verr.Subtype) } if code := output.ExitCodeOf(err); code != output.ExitValidation { t.Errorf("exit code = %d, want %d (ExitValidation)", code, output.ExitValidation) } // The namespaced hook name and the abort semantics are preserved in the // message so a caller can identify which plugin hook rejected the call. if !strings.Contains(verr.Message, "policy-plugin.policy") { t.Errorf("message should name the aborting hook policy-plugin.policy, got %q", verr.Message) } if !strings.Contains(verr.Message, "aborted") { t.Errorf("message should describe the abort, got %q", verr.Message) } // errors.As must still reach the original AbortError so consumers // can inspect the typed cause. var ab *platform.AbortError if !errors.As(err, &ab) { t.Errorf("error chain should expose *platform.AbortError") } } // Plugin.Restrict() contribution must reach the pruning resolver and // take precedence over a yaml file (single-rule, plugin wins). This // goes through the REAL Build() pipeline so the wiring between // installPluginsAndHooks -> applyUserPolicyPruning -> cmdpolicy.Resolve // is covered. func TestPluginPipeline_restrictBeatsYaml(t *testing.T) { cfgDir := tmpHome(t) // yaml says allow everything; plugin says deny everything. Plugin // should win and a command should be denied. if err := os.WriteFile(filepath.Join(cfgDir, "policy.yml"), []byte("name: yaml-allow\nallow: [\"**\"]\n"), 0o644); err != nil { t.Fatalf("write yaml: %v", err) } platform.ResetForTesting() t.Cleanup(platform.ResetForTesting) plugin := &fakeIntegrationPlugin{ name: "restricter", caps: platform.Capabilities{ Restricts: true, FailurePolicy: platform.FailClosed, }, rule: &platform.Rule{Name: "deny-all", Deny: []string{"**"}}, } platform.Register(plugin) root := Build(context.Background(), buildInvocationForTest(t)) // At least one runnable command must end up Hidden because of the // plugin Restrict (yaml had been allow-all and would have left // everything visible). var foundHidden bool walk(root, func(c *cobra.Command) { if c.HasParent() && c.Runnable() && c.Hidden { foundHidden = true } }) if !foundHidden { t.Fatalf("plugin Restrict should have denied at least one command despite yaml allow-all") } } // Denial-guard end-to-end: register a plugin with a Wrap that would // SILENTLY suppress denial (return nil without calling next). After // installing pruning (which marks a command as denied) and wiring // hooks, calling the denied command must STILL produce the denial // error -- the Wrap must never run on the denied path. func TestPluginPipeline_denialGuardIntegrated(t *testing.T) { platform.ResetForTesting() t.Cleanup(platform.ResetForTesting) wrapCalled := false plugin := &fakeIntegrationPlugin{ name: "policy-plugin", caps: platform.Capabilities{FailurePolicy: platform.FailOpen}, wrapDeniesWrite: false, // wrap would normally allow } // Override Wrap with a malicious behavior: return nil (silence the // denial). We do this by wrapping the install: register a // second Wrap that suppresses errors. platform.Register(plugin) // Add another plugin with a malicious wrap. malicious := &mockMaliciousPlugin{ name: "malicious", invokedFlag: &wrapCalled, } platform.Register(malicious) result, err := internalplatform.InstallAll(platform.RegisteredPlugins(), nil) if err != nil { t.Fatalf("InstallAll: %v", err) } root, leaf := syntheticTree() // Simulate cmdpolicy.Apply marking leaf as denied. leaf.Hidden = true leaf.DisableFlagParsing = true if leaf.Annotations == nil { leaf.Annotations = map[string]string{} } leaf.Annotations["lark:policy_denied_layer"] = "policy" leaf.Annotations["lark:policy_denied_source"] = "plugin:other" denyStubCalled := false leaf.RunE = func(*cobra.Command, []string) error { denyStubCalled = true return errors.New("CommandPruned (denyStub)") } if err := wireHooks(context.Background(), root, result.Registry); err != nil { t.Fatalf("wireHooks: %v", err) } err = leaf.RunE(leaf, nil) if wrapCalled { t.Errorf("denial guard violated: malicious Wrap ran on a denied command") } if !denyStubCalled { t.Errorf("denyStub should run on the denial path even when a Wrap is registered") } if err == nil { t.Errorf("denial error must propagate, got nil") } } // mockMaliciousPlugin registers a Wrap that returns nil unconditionally // -- exactly the kind of plugin the denial guard defends against. type mockMaliciousPlugin struct { name string invokedFlag *bool } func (p *mockMaliciousPlugin) Name() string { return p.name } func (p *mockMaliciousPlugin) Version() string { return "0.0.1" } func (p *mockMaliciousPlugin) Capabilities() platform.Capabilities { return platform.Capabilities{FailurePolicy: platform.FailOpen} } func (p *mockMaliciousPlugin) Install(r platform.Registrar) error { r.Wrap("hijack", platform.All(), func(_ platform.Handler) platform.Handler { return func(context.Context, platform.Invocation) error { if p.invokedFlag != nil { *p.invokedFlag = true } return nil // silence everything } }) return nil } // Verifies buildInternal returns a non-nil *hook.Registry when a plugin // is registered and Emit(Shutdown) on that registry fires the plugin's // On(Shutdown) handler. This is the contract Execute relies on to fire // Shutdown after rootCmd.Execute returns. func TestBuildInternal_returnsRegistryForShutdownEmit(t *testing.T) { tmpHome(t) platform.ResetForTesting() t.Cleanup(platform.ResetForTesting) plugin := &fakeIntegrationPlugin{ name: "shutdown-test", caps: platform.Capabilities{FailurePolicy: platform.FailOpen}, } platform.Register(plugin) _, _, reg := buildInternal(context.Background(), buildInvocationForTest(t)) if reg == nil { t.Fatalf("buildInternal returned nil registry; plugin's Shutdown handler is unreachable") } if err := hook.Emit(context.Background(), reg, platform.Shutdown, nil); err != nil { t.Fatalf("Emit(Shutdown): %v", err) } if got := atomic.LoadInt64(&plugin.shutdownCalled); got != 1 { t.Errorf("On(Shutdown) handler fired %d times, want 1", got) } } // When plugin install fails (FailClosed), buildInternal returns nil // registry. Execute must nil-check before calling Emit so we don't fault // on the FailClosed bypass-guard path. func TestBuildInternal_failClosedYieldsNilRegistry(t *testing.T) { tmpHome(t) platform.ResetForTesting() t.Cleanup(platform.ResetForTesting) // A plugin that fails install and is FailClosed -> InstallAll // returns an error, buildInternal installs the guard and returns // early with nil registry. plugin := &failingPlugin{ name: "fail-closed", caps: platform.Capabilities{FailurePolicy: platform.FailClosed}, err: errors.New("install failure simulated"), } platform.Register(plugin) _, _, reg := buildInternal(context.Background(), buildInvocationForTest(t)) if reg != nil { t.Errorf("buildInternal returned non-nil registry on FailClosed install error") } } type failingPlugin struct { name string caps platform.Capabilities err error } func (p *failingPlugin) Name() string { return p.name } func (p *failingPlugin) Version() string { return "0.0.1" } func (p *failingPlugin) Capabilities() platform.Capabilities { return p.caps } func (p *failingPlugin) Install(platform.Registrar) error { return p.err } // === Plugin Restrict conflict guard === // // Two plugins both calling r.Restrict must surface as a structured // plugin_conflict envelope (reason_code multiple_restrict_plugins) at // dispatch time, NOT as a silent stderr warning. Otherwise a // safety-sensitive operator could miss that their policy never took // effect. func TestPluginConflictGuard_MultipleRestrictAbortsCLI(t *testing.T) { tmpHome(t) platform.ResetForTesting() t.Cleanup(platform.ResetForTesting) cmdpolicy.ResetActiveForTesting() t.Cleanup(cmdpolicy.ResetActiveForTesting) rule := &platform.Rule{Name: "any", Allow: []string{"**"}} platform.Register(&fakeIntegrationPlugin{ name: "plugin-a", caps: platform.Capabilities{Restricts: true, FailurePolicy: platform.FailClosed}, rule: rule, }) platform.Register(&fakeIntegrationPlugin{ name: "plugin-b", caps: platform.Capabilities{Restricts: true, FailurePolicy: platform.FailClosed}, rule: rule, }) _, root, reg := buildInternal(context.Background(), buildInvocationForTest(t)) if reg != nil { t.Errorf("conflict guard path should yield nil registry") } // Pick any leaf and verify it returns the structured envelope. leaf := findRunnableLeaf(root) if leaf == nil { t.Fatalf("no runnable leaf in command tree") } err := leaf.RunE(leaf, nil) var verr *errs.ValidationError if !errors.As(err, &verr) { t.Fatalf("expected *errs.ValidationError, got %T %+v", err, err) } if verr.Subtype != errs.SubtypeFailedPrecondition { t.Errorf("subtype = %q, want failed_precondition", verr.Subtype) } if code := output.ExitCodeOf(err); code != output.ExitValidation { t.Errorf("exit code = %d, want %d (ExitValidation)", code, output.ExitValidation) } // reason_code multiple_restrict_plugins is folded into the hint so the // operator can distinguish a multi-Restrict conflict from a bad rule. if !strings.Contains(verr.Hint, "multiple_restrict_plugins") { t.Errorf("hint should surface reason_code multiple_restrict_plugins, got %q", verr.Hint) } } // Single plugin with an invalid Rule must surface as plugin_install / // invalid_rule envelope (distinct error.type from multi-Restrict). func TestPluginConflictGuard_InvalidRuleAbortsCLI(t *testing.T) { tmpHome(t) platform.ResetForTesting() t.Cleanup(platform.ResetForTesting) cmdpolicy.ResetActiveForTesting() t.Cleanup(cmdpolicy.ResetActiveForTesting) // MaxRisk "nukem" is rejected by ValidateRule -> Resolve returns // an error that is NOT ErrMultipleRestricts. platform.Register(&fakeIntegrationPlugin{ name: "bad", caps: platform.Capabilities{Restricts: true, FailurePolicy: platform.FailClosed}, rule: &platform.Rule{Name: "bad", MaxRisk: "nukem"}, }) _, root, reg := buildInternal(context.Background(), buildInvocationForTest(t)) if reg != nil { t.Errorf("conflict guard path should yield nil registry") } leaf := findRunnableLeaf(root) if leaf == nil { t.Fatalf("no runnable leaf in command tree") } err := leaf.RunE(leaf, nil) var verr *errs.ValidationError if !errors.As(err, &verr) { t.Fatalf("expected *errs.ValidationError, got %T %+v", err, err) } if verr.Subtype != errs.SubtypeFailedPrecondition { t.Errorf("subtype = %q, want failed_precondition", verr.Subtype) } if code := output.ExitCodeOf(err); code != output.ExitValidation { t.Errorf("exit code = %d, want %d (ExitValidation)", code, output.ExitValidation) } // reason_code invalid_rule is folded into the hint, distinct from the // multiple_restrict_plugins conflict path. if !strings.Contains(verr.Hint, "invalid_rule") { t.Errorf("hint should surface reason_code invalid_rule, got %q", verr.Hint) } } // === Startup lifecycle guard === // // Plugin On(Startup) handler returning error must abort startup with // a plugin_lifecycle envelope (reason_code lifecycle_failed). Silently // continuing would leave the plugin's invariants violated while the // rest of its hooks still fire. func TestPluginLifecycleGuard_StartupErrorAbortsCLI(t *testing.T) { tmpHome(t) platform.ResetForTesting() t.Cleanup(platform.ResetForTesting) cmdpolicy.ResetActiveForTesting() t.Cleanup(cmdpolicy.ResetActiveForTesting) platform.Register(&startupFailingPlugin{ name: "lc", failErr: errors.New("backend unreachable"), }) _, root, reg := buildInternal(context.Background(), buildInvocationForTest(t)) if reg != nil { t.Errorf("lifecycle guard path should yield nil registry") } leaf := findRunnableLeaf(root) err := leaf.RunE(leaf, nil) var verr *errs.ValidationError if !errors.As(err, &verr) { t.Fatalf("expected *errs.ValidationError, got %T %+v", err, err) } if verr.Subtype != errs.SubtypeFailedPrecondition { t.Errorf("subtype = %q, want failed_precondition", verr.Subtype) } if code := output.ExitCodeOf(err); code != output.ExitValidation { t.Errorf("exit code = %d, want %d (ExitValidation)", code, output.ExitValidation) } // reason_code lifecycle_failed (vs lifecycle_panic) and the failing // hook name are folded into the hint so audit / on-call can tell the // failure mode and which hook failed. if !strings.Contains(verr.Hint, "lifecycle_failed") { t.Errorf("hint should surface reason_code lifecycle_failed, got %q", verr.Hint) } if !strings.Contains(verr.Hint, "lc.start") { t.Errorf("hint should name the failing hook lc.start, got %q", verr.Hint) } } // Same path but the handler panics -> reason_code lifecycle_panic. func TestPluginLifecycleGuard_StartupPanicAbortsCLI(t *testing.T) { tmpHome(t) platform.ResetForTesting() t.Cleanup(platform.ResetForTesting) cmdpolicy.ResetActiveForTesting() t.Cleanup(cmdpolicy.ResetActiveForTesting) platform.Register(&startupFailingPlugin{ name: "lc", doPanic: true, panicMsg: "kaboom", }) _, root, reg := buildInternal(context.Background(), buildInvocationForTest(t)) if reg != nil { t.Errorf("lifecycle guard path should yield nil registry") } leaf := findRunnableLeaf(root) err := leaf.RunE(leaf, nil) var verr *errs.ValidationError if !errors.As(err, &verr) { t.Fatalf("expected *errs.ValidationError, got %T", err) } if verr.Subtype != errs.SubtypeFailedPrecondition { t.Errorf("subtype = %q, want failed_precondition", verr.Subtype) } if code := output.ExitCodeOf(err); code != output.ExitValidation { t.Errorf("exit code = %d, want %d (ExitValidation)", code, output.ExitValidation) } // A panicking startup hook is distinguished from a returned error by // reason_code lifecycle_panic in the hint. if !strings.Contains(verr.Hint, "lifecycle_panic") { t.Errorf("hint should surface reason_code lifecycle_panic, got %q", verr.Hint) } } type startupFailingPlugin struct { name string failErr error // when set, handler returns this doPanic bool // when true, handler panics with panicMsg panicMsg string } func (p *startupFailingPlugin) Name() string { return p.name } func (p *startupFailingPlugin) Version() string { return "0.0.1" } func (p *startupFailingPlugin) Capabilities() platform.Capabilities { return platform.Capabilities{FailurePolicy: platform.FailClosed} } func (p *startupFailingPlugin) Install(r platform.Registrar) error { r.On(platform.Startup, "start", func(context.Context, *platform.LifecycleContext) error { if p.doPanic { panic(p.panicMsg) } return p.failErr }) return nil } // === Wrapper panic recovery === // // A Wrapper that panics must NOT crash the process. The framework // recovers and converts to a structured envelope: // // type="hook", reason_code="panic", hook_name= func TestWrapperPanic_BecomesHookPanicEnvelope(t *testing.T) { platform.ResetForTesting() t.Cleanup(platform.ResetForTesting) platform.Register(&panickingWrapPlugin{name: "p"}) result, err := internalplatform.InstallAll(platform.RegisteredPlugins(), nil) if err != nil { t.Fatalf("InstallAll: %v", err) } root, leaf := syntheticTree() if err := wireHooks(context.Background(), root, result.Registry); err != nil { t.Fatalf("wireHooks: %v", err) } defer func() { if r := recover(); r != nil { t.Fatalf("Wrapper panic must be recovered, but it escaped: %v", r) } }() err = leaf.RunE(leaf, nil) var verr *errs.ValidationError if !errors.As(err, &verr) { t.Fatalf("expected *errs.ValidationError, got %T %+v", err, err) } if verr.Subtype != errs.SubtypeFailedPrecondition { t.Errorf("subtype = %q, want failed_precondition", verr.Subtype) } if code := output.ExitCodeOf(err); code != output.ExitValidation { t.Errorf("exit code = %d, want %d (ExitValidation)", code, output.ExitValidation) } // The recovered panic surfaces as a structured error naming the // namespaced hook (p.boom) and describing the panic, so the process // never crashes and the caller can attribute the failure. if !strings.Contains(verr.Message, "p.boom") { t.Errorf("message should name the namespaced hook p.boom, got %q", verr.Message) } if !strings.Contains(verr.Message, "panic") { t.Errorf("message should describe the panic, got %q", verr.Message) } } type panickingWrapPlugin struct{ name string } func (p *panickingWrapPlugin) Name() string { return p.name } func (p *panickingWrapPlugin) Version() string { return "0.0.1" } func (p *panickingWrapPlugin) Capabilities() platform.Capabilities { return platform.Capabilities{} } func (p *panickingWrapPlugin) Install(r platform.Registrar) error { r.Wrap("boom", platform.All(), func(_ platform.Handler) platform.Handler { return func(context.Context, platform.Invocation) error { panic("intentional panic for test") } }) return nil } // findRunnableLeaf walks the tree and returns the first command with a // RunE so tests can synthesize a dispatch without going through cobra. func findRunnableLeaf(c *cobra.Command) *cobra.Command { if c.RunE != nil && c.HasParent() { return c } for _, child := range c.Commands() { if l := findRunnableLeaf(child); l != nil { return l } } return nil } // B2 regression: a plugin Wrapper whose FACTORY function (the // `func(next Handler) Handler` itself) panics must not crash the // process. The framework recovers and returns the same panic envelope // it produces for runtime panics inside the inner Handler. // // Pre-fix code path: recoverWrap had `inner := w(next)` outside the // deferred recover, so a factory panic escaped. func TestWrapperFactoryPanic_BecomesHookPanicEnvelope(t *testing.T) { platform.ResetForTesting() t.Cleanup(platform.ResetForTesting) platform.Register(&factoryPanicWrapPlugin{name: "fac"}) result, err := internalplatform.InstallAll(platform.RegisteredPlugins(), nil) if err != nil { t.Fatalf("InstallAll: %v", err) } root, leaf := syntheticTree() if err := wireHooks(context.Background(), root, result.Registry); err != nil { t.Fatalf("wireHooks: %v", err) } defer func() { if r := recover(); r != nil { t.Fatalf("factory panic must be recovered, but it escaped: %v", r) } }() err = leaf.RunE(leaf, nil) var verr *errs.ValidationError if !errors.As(err, &verr) { t.Fatalf("expected *errs.ValidationError, got %T %+v", err, err) } if verr.Subtype != errs.SubtypeFailedPrecondition { t.Errorf("subtype = %q, want failed_precondition", verr.Subtype) } if code := output.ExitCodeOf(err); code != output.ExitValidation { t.Errorf("exit code = %d, want %d (ExitValidation)", code, output.ExitValidation) } // A panic in the wrapper FACTORY (not just the inner handler) is // recovered into the same structured panic error, naming the // namespaced hook fac.bad-factory. if !strings.Contains(verr.Message, "fac.bad-factory") { t.Errorf("message should name the namespaced hook fac.bad-factory, got %q", verr.Message) } if !strings.Contains(verr.Message, "panic") { t.Errorf("message should describe the panic, got %q", verr.Message) } } type factoryPanicWrapPlugin struct{ name string } func (p *factoryPanicWrapPlugin) Name() string { return p.name } func (p *factoryPanicWrapPlugin) Version() string { return "0.0.1" } func (p *factoryPanicWrapPlugin) Capabilities() platform.Capabilities { return platform.Capabilities{} } func (p *factoryPanicWrapPlugin) Install(r platform.Registrar) error { r.Wrap("bad-factory", platform.All(), // The factory itself panics; the returned Handler is never reached. func(_ platform.Handler) platform.Handler { panic("factory blew up") }) return nil }