mirror of
https://github.com/larksuite/cli.git
synced 2026-07-06 16:18:05 +08:00
* feat(risk): implement confirmation for high-risk write operations * feat(risk): streamline confirmation for high-risk write operations * feat(risk): document approval protocol for high-risk write operations * feat(risk): refine confirmation protocol for high-risk write operations * feat(risk): remove redundant variable declaration in risk test * feat(risk): add 'Yes' flag to various test cases for confirmation
115 lines
3.6 KiB
Go
115 lines
3.6 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package service
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/larksuite/cli/internal/cmdutil"
|
|
)
|
|
|
|
// highRiskDeleteMethod mirrors a simple DELETE API with a required path
|
|
// parameter and risk metadata. The returned map is what service registration
|
|
// reads; the test exercises --yes registration and the gate behavior.
|
|
func highRiskDeleteMethod() map[string]interface{} {
|
|
return map[string]interface{}{
|
|
"path": "files/{file_token}",
|
|
"httpMethod": "DELETE",
|
|
"risk": "high-risk-write",
|
|
"parameters": map[string]interface{}{
|
|
"file_token": map[string]interface{}{
|
|
"type": "string", "location": "path", "required": true,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func writeMethodNoRisk() map[string]interface{} {
|
|
return map[string]interface{}{
|
|
"path": "files/{file_token}",
|
|
"httpMethod": "DELETE",
|
|
"parameters": map[string]interface{}{
|
|
"file_token": map[string]interface{}{
|
|
"type": "string", "location": "path", "required": true,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestServiceMethod_YesFlagRegisteredForHighRisk(t *testing.T) {
|
|
f, _, _, _ := cmdutil.TestFactory(t, testConfig)
|
|
cmd := NewCmdServiceMethod(f, driveSpec(), highRiskDeleteMethod(), "delete", "files", nil)
|
|
|
|
if cmd.Flags().Lookup("yes") == nil {
|
|
t.Error("expected --yes flag registered for risk=high-risk-write")
|
|
}
|
|
}
|
|
|
|
func TestServiceMethod_YesFlagNotRegisteredForWrite(t *testing.T) {
|
|
f, _, _, _ := cmdutil.TestFactory(t, testConfig)
|
|
cmd := NewCmdServiceMethod(f, driveSpec(), writeMethodNoRisk(), "delete", "files", nil)
|
|
|
|
if cmd.Flags().Lookup("yes") != nil {
|
|
t.Error("expected --yes flag NOT registered when risk is unset")
|
|
}
|
|
}
|
|
|
|
func TestServiceMethod_RiskAnnotationSet(t *testing.T) {
|
|
f, _, _, _ := cmdutil.TestFactory(t, testConfig)
|
|
cmd := NewCmdServiceMethod(f, driveSpec(), highRiskDeleteMethod(), "delete", "files", nil)
|
|
|
|
level, ok := cmdutil.GetRisk(cmd)
|
|
if !ok {
|
|
t.Fatal("expected Risk annotation to be set")
|
|
}
|
|
if level != "high-risk-write" {
|
|
t.Errorf("level = %q, want high-risk-write", level)
|
|
}
|
|
}
|
|
|
|
func TestServiceMethod_RiskAnnotationAbsentForUnsetRisk(t *testing.T) {
|
|
f, _, _, _ := cmdutil.TestFactory(t, testConfig)
|
|
cmd := NewCmdServiceMethod(f, driveSpec(), writeMethodNoRisk(), "delete", "files", nil)
|
|
|
|
if _, ok := cmdutil.GetRisk(cmd); ok {
|
|
t.Error("expected no Risk annotation when meta risk is unset")
|
|
}
|
|
}
|
|
|
|
func TestServiceMethod_GateBlocksWithoutYes(t *testing.T) {
|
|
f, _, _, _ := cmdutil.TestFactory(t, testConfig)
|
|
cmd := NewCmdServiceMethod(f, driveSpec(), highRiskDeleteMethod(), "delete", "files", nil)
|
|
// --as bot skips the scope check so we reach the gate without external creds.
|
|
cmd.SetArgs([]string{"--as", "bot", "--params", `{"file_token":"tok_abc"}`})
|
|
|
|
err := cmd.Execute()
|
|
if err == nil {
|
|
t.Fatal("expected confirmation error, got nil")
|
|
}
|
|
if !strings.Contains(err.Error(), "requires confirmation") {
|
|
t.Errorf("expected 'requires confirmation' in error, got: %v", err)
|
|
}
|
|
if !strings.Contains(err.Error(), "drive.files.delete") {
|
|
t.Errorf("expected schema path in error action, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestServiceMethod_DryRunBypassesGate(t *testing.T) {
|
|
f, stdout, _, _ := cmdutil.TestFactory(t, testConfig)
|
|
cmd := NewCmdServiceMethod(f, driveSpec(), highRiskDeleteMethod(), "delete", "files", nil)
|
|
cmd.SetArgs([]string{
|
|
"--as", "bot",
|
|
"--params", `{"file_token":"tok_abc"}`,
|
|
"--dry-run",
|
|
})
|
|
|
|
if err := cmd.Execute(); err != nil {
|
|
t.Fatalf("dry-run should not hit confirmation gate; got: %v", err)
|
|
}
|
|
if !strings.Contains(stdout.String(), "files/tok_abc") {
|
|
t.Errorf("expected dry-run output to contain URL, got:\n%s", stdout.String())
|
|
}
|
|
}
|