Some checks are pending
Security Checks / security (push) Waiting to run
统一 frontend、backend、test、test-log 和 out 目录,更新构建、部署及测试入口;纳入配置管理兼容与首次安全引导修复;排除本机配置、密钥和测试产物。
105 lines
3.0 KiB
Go
105 lines
3.0 KiB
Go
package config
|
||
|
||
import (
|
||
"bytes"
|
||
"os"
|
||
"path/filepath"
|
||
"runtime"
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
func TestConfigFileUsesRestrictedAtomicStorage(t *testing.T) {
|
||
oldRoot := ROOT_PATH
|
||
ROOT_PATH = t.TempDir() + string(os.PathSeparator)
|
||
t.Cleanup(func() { ROOT_PATH = oldRoot })
|
||
|
||
if err := writeConfigFile("config.json", []byte(`{"version":1}`)); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if err := writeConfigFile("config.json", []byte(`{"version":2}`)); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
data, err := readConfigFile("config.json")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if string(data) != `{"version":2}` {
|
||
t.Fatalf("配置文件内容为 %q,期望原子替换后的内容", data)
|
||
}
|
||
directory := filepath.Join(ROOT_PATH, "config")
|
||
entries, err := os.ReadDir(directory)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
for _, entry := range entries {
|
||
if strings.HasPrefix(entry.Name(), ".config.json-") {
|
||
t.Fatalf("残留配置临时文件: %s", entry.Name())
|
||
}
|
||
}
|
||
if runtime.GOOS != "windows" {
|
||
fileInfo, statErr := os.Stat(filepath.Join(directory, "config.json"))
|
||
if statErr != nil {
|
||
t.Fatal(statErr)
|
||
}
|
||
if fileInfo.Mode().Perm() != 0600 {
|
||
t.Fatalf("配置文件权限为 %o,期望 600", fileInfo.Mode().Perm())
|
||
}
|
||
directoryInfo, statErr := os.Stat(directory)
|
||
if statErr != nil {
|
||
t.Fatal(statErr)
|
||
}
|
||
if directoryInfo.Mode().Perm() != 0700 {
|
||
t.Fatalf("配置目录权限为 %o,期望 700", directoryInfo.Mode().Perm())
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestConfigStorageRejectsSymlinkDirectory(t *testing.T) {
|
||
oldRoot := ROOT_PATH
|
||
root := t.TempDir()
|
||
ROOT_PATH = root + string(os.PathSeparator)
|
||
t.Cleanup(func() { ROOT_PATH = oldRoot })
|
||
outside := filepath.Join(t.TempDir(), "outside")
|
||
if err := os.Mkdir(outside, 0700); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if err := os.Symlink(outside, filepath.Join(root, "config")); err != nil {
|
||
t.Skipf("当前环境不支持符号链接测试: %v", err)
|
||
}
|
||
if err := writeConfigFile("config.json", []byte(`{"safe":true}`)); err == nil {
|
||
t.Fatal("配置符号链接目录应被拒绝")
|
||
}
|
||
}
|
||
|
||
func TestConfigStorageRejectsSymlinkFile(t *testing.T) {
|
||
oldRoot := ROOT_PATH
|
||
ROOT_PATH = t.TempDir() + string(os.PathSeparator)
|
||
t.Cleanup(func() { ROOT_PATH = oldRoot })
|
||
directory, err := ensureSecureConfigDirectory()
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
outsidePath := filepath.Join(t.TempDir(), "outside.json")
|
||
original := []byte(`{"outside":true}`)
|
||
if err := os.WriteFile(outsidePath, original, 0600); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if err := os.Symlink(outsidePath, filepath.Join(directory, "config.json")); err != nil {
|
||
t.Skipf("当前环境不支持符号链接测试: %v", err)
|
||
}
|
||
if err := writeConfigFile("config.json", []byte(`{"safe":true}`)); err == nil {
|
||
t.Fatal("配置符号链接文件应被拒绝")
|
||
}
|
||
if _, err := readConfigFile("config.json"); err == nil {
|
||
t.Fatal("配置读取不应跟随符号链接")
|
||
}
|
||
outsideData, err := os.ReadFile(outsidePath)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if !bytes.Equal(outsideData, original) {
|
||
t.Fatal("拒绝配置符号链接时不应修改外部文件")
|
||
}
|
||
}
|