146 lines
5.1 KiB
Go
146 lines
5.1 KiB
Go
package cli
|
||
|
||
import (
|
||
"bytes"
|
||
"context"
|
||
"encoding/json"
|
||
"errors"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
"testing"
|
||
|
||
"git.pcux.cn/xuans/Scert/backend/internal/config"
|
||
"git.pcux.cn/xuans/Scert/backend/internal/dnsprovider"
|
||
"git.pcux.cn/xuans/Scert/backend/internal/secretstore"
|
||
)
|
||
|
||
func TestDNSCheckRequiresExplicitWriteConfirmation(t *testing.T) {
|
||
called := false
|
||
command := newDNSCheckCommand(func(context.Context, string, map[string]string) (dnsprovider.AuthorizationReport, error) {
|
||
called = true
|
||
return dnsprovider.AuthorizationReport{}, nil
|
||
})
|
||
command.SetArgs([]string{"--domain", "example.com"})
|
||
if err := command.Execute(); err == nil || !strings.Contains(err.Error(), "--confirm-dns-write") {
|
||
t.Fatalf("缺少写入确认时必须拒绝:%v", err)
|
||
}
|
||
if called {
|
||
t.Fatal("确认前不得调用云端 API")
|
||
}
|
||
}
|
||
|
||
func TestDNSInspectDoesNotRequireWriteOrPromoteReadableToVerified(t *testing.T) {
|
||
prepareDNSCheckConfig(t)
|
||
command := newDNSInspectCommand(func(context.Context, string, map[string]string) (dnsprovider.AuthorizationReport, error) {
|
||
return dnsprovider.AuthorizationReport{
|
||
Provider: "aliyun", Domains: []dnsprovider.AuthorizationDomain{{Domain: "example.com", Listed: true}},
|
||
}, nil
|
||
})
|
||
var output bytes.Buffer
|
||
command.SetOut(&output)
|
||
command.SetArgs([]string{"--domain", "example.com"})
|
||
if err := command.Execute(); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
var report dnsprovider.AuthorizationReport
|
||
if err := json.Unmarshal(output.Bytes(), &report); err != nil || report.Verified || report.Domains[0].Writable {
|
||
t.Fatalf("只读成功不能升级为写权限通过:%s", output.String())
|
||
}
|
||
if command.Flag("confirm-dns-write") != nil {
|
||
t.Fatal("只读命令不得提供写入开关")
|
||
}
|
||
}
|
||
|
||
func TestDNSCheckReportsLiveResultWithoutPersistingConfiguration(t *testing.T) {
|
||
for _, outcome := range []string{"allowed", "denied", "unknown", "unverified"} {
|
||
t.Run(outcome, func(t *testing.T) {
|
||
cfg := prepareDNSCheckConfig(t)
|
||
before, err := os.ReadFile(configPath)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
calls := 0
|
||
command := newDNSCheckCommand(func(ctx context.Context, name string, credentials map[string]string) (dnsprovider.AuthorizationReport, error) {
|
||
calls++
|
||
if name != "example.com" || credentials["access_key_id"] != "test-id" {
|
||
t.Fatal("检查目标或凭据来源不正确")
|
||
}
|
||
if _, ok := ctx.Deadline(); !ok {
|
||
t.Fatal("权限请求必须设置超时")
|
||
}
|
||
report := dnsprovider.AuthorizationReport{Provider: "aliyun", RequestedDomain: name, Verified: outcome == "allowed"}
|
||
switch outcome {
|
||
case "denied":
|
||
return report, errors.New("Forbidden.NoPermission")
|
||
case "unknown":
|
||
return report, context.DeadlineExceeded
|
||
default:
|
||
return report, nil
|
||
}
|
||
})
|
||
var output bytes.Buffer
|
||
command.SilenceErrors, command.SilenceUsage = true, true
|
||
command.SetOut(&output)
|
||
command.SetArgs([]string{"--domain", "EXAMPLE.COM.", "--confirm-dns-write"})
|
||
err = command.Execute()
|
||
if (err == nil) != (outcome == "allowed") || calls != 1 {
|
||
t.Fatalf("检查结果错误:outcome=%s calls=%d err=%v", outcome, calls, err)
|
||
}
|
||
var report dnsprovider.AuthorizationReport
|
||
if err := json.Unmarshal(output.Bytes(), &report); err != nil || report.Verified != (outcome == "allowed") {
|
||
t.Fatalf("必须输出可机器读取的真实报告:%s", output.String())
|
||
}
|
||
if strings.Contains(output.String(), "test-secret") || strings.Contains(output.String(), "test-id") {
|
||
t.Fatal("输出不得包含凭据")
|
||
}
|
||
after, err := os.ReadFile(configPath)
|
||
if err != nil || !bytes.Equal(before, after) {
|
||
t.Fatal("权限诊断不得回写配置或初始化状态")
|
||
}
|
||
if _, err := os.Stat(cfg.Paths.Data); !errors.Is(err, os.ErrNotExist) {
|
||
t.Fatalf("权限诊断不得初始化业务数据库:%v", err)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestDNSCheckDoesNotCreateMissingMasterKey(t *testing.T) {
|
||
oldPath := configPath
|
||
t.Cleanup(func() { configPath = oldPath })
|
||
cfg := config.Default(t.TempDir())
|
||
cfg.DNS.Provider, cfg.DNS.CredentialRef = "aliyun", "secret://dns/check"
|
||
configPath = filepath.Join(cfg.Paths.Config, "scert.json")
|
||
if err := cfg.Save(configPath); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
command := newDNSCheckCommand(nil)
|
||
command.SetArgs([]string{"--domain", "example.com", "--confirm-dns-write"})
|
||
if err := command.Execute(); err == nil {
|
||
t.Fatal("主密钥缺失必须拒绝")
|
||
}
|
||
if _, err := os.Stat(filepath.Join(cfg.Paths.Config, "master.key")); !errors.Is(err, os.ErrNotExist) {
|
||
t.Fatal("诊断不得创建新主密钥")
|
||
}
|
||
}
|
||
|
||
func prepareDNSCheckConfig(t *testing.T) config.Config {
|
||
t.Helper()
|
||
oldPath := configPath
|
||
t.Cleanup(func() { configPath = oldPath })
|
||
cfg := config.Default(t.TempDir())
|
||
cfg.DNS.Provider, cfg.DNS.CredentialRef = "aliyun", "secret://dns/check"
|
||
configPath = filepath.Join(cfg.Paths.Config, "scert.json")
|
||
if err := cfg.Save(configPath); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
store, err := secretstore.Open(cfg.Paths.Config)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if err := store.Put(t.Context(), cfg.DNS.CredentialRef, map[string]string{"access_key_id": "test-id", "access_key_secret": "test-secret"}); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
return cfg
|
||
}
|