bf9395e022
CI / license-header (push) Has been skipped
CI / e2e-dry-run (push) Has been skipped
CI / fast-gate (push) Failing after 0s
Test PR Label Logic / test-pr-labels (push) Failing after 1s
Skill Format Check / check-format (push) Failing after 2s
CI / security (push) Failing after 5s
CI / unit-test (push) Has been skipped
CI / lint (push) Has been skipped
CI / script-test (push) Has been skipped
CI / deterministic-gate (push) Has been skipped
CI / coverage (push) Has been skipped
CI / results (push) Has been cancelled
CI / deadcode (push) Has been cancelled
CI / e2e-live (push) Has been cancelled
164 lines
5.4 KiB
Go
164 lines
5.4 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package cmdpolicy_test
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/larksuite/cli/internal/cmdpolicy"
|
|
)
|
|
|
|
// cmdpolicy.Apply MUST NOT overwrite the denial annotation on a command
|
|
// already marked as strict-mode denied. strict-mode is a hard boundary
|
|
// (credential-derived); a user-layer rule cannot relabel or replace
|
|
// the error path.
|
|
//
|
|
// Without this invariant: when a user yaml rule happened to match the
|
|
// path of a strict-mode stub, Apply would change layer=strict_mode to
|
|
// layer=pruning, and the user-visible error would say "denied by yaml"
|
|
// instead of "strict mode". The hard-boundary contract demands
|
|
// strict_mode wins.
|
|
func TestApply_PreservesStrictModeAnnotation(t *testing.T) {
|
|
root := &cobra.Command{Use: "root"}
|
|
stub := &cobra.Command{
|
|
Use: "victim",
|
|
Hidden: true,
|
|
Annotations: map[string]string{
|
|
cmdpolicy.AnnotationDenialLayer: cmdpolicy.LayerStrictMode,
|
|
cmdpolicy.AnnotationDenialSource: "strict-mode",
|
|
},
|
|
RunE: func(*cobra.Command, []string) error { return nil },
|
|
}
|
|
root.AddCommand(stub)
|
|
|
|
// User-layer pruning denies the same path.
|
|
denied := map[string]cmdpolicy.Denial{
|
|
"victim": {
|
|
Layer: cmdpolicy.LayerPolicy,
|
|
PolicySource: "yaml",
|
|
Reason: "denied by user yaml",
|
|
ReasonCode: "command_denylisted",
|
|
},
|
|
}
|
|
cmdpolicy.Apply(root, denied)
|
|
|
|
if got := stub.Annotations[cmdpolicy.AnnotationDenialLayer]; got != cmdpolicy.LayerStrictMode {
|
|
t.Errorf("strict-mode layer overwritten by pruning: got %q want %q",
|
|
got, cmdpolicy.LayerStrictMode)
|
|
}
|
|
if got := stub.Annotations[cmdpolicy.AnnotationDenialSource]; got != "strict-mode" {
|
|
t.Errorf("strict-mode source overwritten: got %q", got)
|
|
}
|
|
}
|
|
|
|
// Regression for codex H13 / C6: a denied command that carries
|
|
// flag-like positional args (because DisableFlagParsing=true makes
|
|
// every `--doc xxx` look positional) MUST surface the pruning
|
|
// envelope, not a cobra usage error. Pre-fix, the original command's
|
|
// Args validator (e.g. cobra.NoArgs from shortcut registration) would
|
|
// fire BEFORE PersistentPreRunE / RunE and produce
|
|
// "Error: positional arguments are not supported".
|
|
//
|
|
// Fix: installDenyStub sets Args=ArbitraryArgs so cobra's validate
|
|
// step always passes, letting dispatch reach the wrapped RunE.
|
|
func TestApply_DenyStubBypassesArgsValidator(t *testing.T) {
|
|
root := &cobra.Command{Use: "root"}
|
|
leaf := &cobra.Command{
|
|
Use: "+update",
|
|
Args: cobra.NoArgs, // shortcut style: refuse all positional args
|
|
RunE: func(*cobra.Command, []string) error { return nil },
|
|
}
|
|
root.AddCommand(leaf)
|
|
|
|
denied := map[string]cmdpolicy.Denial{
|
|
"+update": {
|
|
Layer: cmdpolicy.LayerPolicy,
|
|
PolicySource: "yaml",
|
|
ReasonCode: "command_denylisted",
|
|
Reason: "denied by user yaml",
|
|
},
|
|
}
|
|
cmdpolicy.Apply(root, denied)
|
|
|
|
if leaf.Args == nil {
|
|
t.Fatal("denied command must have non-nil Args validator after Apply")
|
|
}
|
|
// ArbitraryArgs returns nil for every input -> Args validation no-ops.
|
|
if err := leaf.Args(leaf, []string{"--doc", "xxx", "--mode", "append"}); err != nil {
|
|
t.Errorf("denied command Args validator should accept any input, got %v", err)
|
|
}
|
|
}
|
|
|
|
// Regression for codex C11 / C13: a denied command whose PARENT
|
|
// declares a PersistentPreRunE (e.g. cmd/auth/auth.go's
|
|
// external_provider check) MUST surface the pruning envelope, not
|
|
// the parent's error. Cobra's "first PersistentPreRunE walking up
|
|
// from leaf wins" semantics will pick the parent's PersistentPreRunE
|
|
// unless the denied leaf carries its own.
|
|
//
|
|
// Fix: installDenyStub installs a no-op PersistentPreRunE on the leaf
|
|
// so cobra stops there and proceeds to the wrapped RunE (which holds
|
|
// the real pruning envelope).
|
|
func TestApply_DenyStubBypassesParentPersistentPreRunE(t *testing.T) {
|
|
root := &cobra.Command{Use: "root"}
|
|
parent := &cobra.Command{
|
|
Use: "auth",
|
|
PersistentPreRunE: func(*cobra.Command, []string) error {
|
|
return errors.New("parent PersistentPreRunE fired (would mask pruning)")
|
|
},
|
|
}
|
|
root.AddCommand(parent)
|
|
leaf := &cobra.Command{
|
|
Use: "login",
|
|
RunE: func(*cobra.Command, []string) error { return nil },
|
|
}
|
|
parent.AddCommand(leaf)
|
|
|
|
denied := map[string]cmdpolicy.Denial{
|
|
"auth/login": {
|
|
Layer: cmdpolicy.LayerPolicy,
|
|
PolicySource: "yaml",
|
|
ReasonCode: "identity_mismatch",
|
|
Reason: "denied",
|
|
},
|
|
}
|
|
cmdpolicy.Apply(root, denied)
|
|
|
|
if leaf.PersistentPreRunE == nil {
|
|
t.Fatal("denied command must have leaf-level PersistentPreRunE")
|
|
}
|
|
// Our PersistentPreRunE must NOT propagate the parent's error.
|
|
if err := leaf.PersistentPreRunE(leaf, nil); err != nil {
|
|
t.Errorf("denied command leaf PersistentPreRunE should be no-op, got %v", err)
|
|
}
|
|
}
|
|
|
|
// Sanity: a normal command (no prior annotation) still gets the
|
|
// pruning denial annotations after Apply.
|
|
func TestApply_NonStrictCommandStillGetsPruningAnnotation(t *testing.T) {
|
|
root := &cobra.Command{Use: "root"}
|
|
leaf := &cobra.Command{
|
|
Use: "normal",
|
|
RunE: func(*cobra.Command, []string) error { return nil },
|
|
}
|
|
root.AddCommand(leaf)
|
|
|
|
denied := map[string]cmdpolicy.Denial{
|
|
"normal": {
|
|
Layer: cmdpolicy.LayerPolicy,
|
|
PolicySource: "yaml",
|
|
Reason: "denied",
|
|
ReasonCode: "command_denylisted",
|
|
},
|
|
}
|
|
cmdpolicy.Apply(root, denied)
|
|
|
|
if got := leaf.Annotations[cmdpolicy.AnnotationDenialLayer]; got != cmdpolicy.LayerPolicy {
|
|
t.Errorf("expected pruning layer annotation, got %q", got)
|
|
}
|
|
}
|