125 lines
3.6 KiB
Go
125 lines
3.6 KiB
Go
package verifyfiles
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
// newTestResolver 造一个临时验证目录,返回 resolver。
|
|
func newTestResolver(t *testing.T, cfg Config) *Resolver {
|
|
t.Helper()
|
|
r, err := New(cfg)
|
|
if err != nil {
|
|
t.Fatalf("创建 Resolver 失败: %v", err)
|
|
}
|
|
if r == nil {
|
|
t.Fatal("Resolver 不应为 nil")
|
|
}
|
|
return r
|
|
}
|
|
|
|
// TestResolveAllowed 确认合法验证文件能被解析到磁盘路径。
|
|
func TestResolveAllowed(t *testing.T) {
|
|
dir := t.TempDir()
|
|
if err := os.WriteFile(filepath.Join(dir, "1XXl8fPHr9.txt"), []byte("1XXl8fPHr9"), 0o644); err != nil {
|
|
t.Fatalf("准备测试文件失败: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(dir, "baidu_verify_codexxx.html"), []byte("<html></html>"), 0o644); err != nil {
|
|
t.Fatalf("准备测试文件失败: %v", err)
|
|
}
|
|
|
|
r := newTestResolver(t, Config{Dir: dir})
|
|
|
|
for _, name := range []string{"1XXl8fPHr9.txt", "baidu_verify_codexxx.html"} {
|
|
got, ok := r.Resolve("/" + name)
|
|
if !ok {
|
|
t.Fatalf("合法验证文件 /%s 未被解析出来", name)
|
|
}
|
|
want := filepath.Join(dir, name)
|
|
if got != want {
|
|
t.Fatalf("路径不对,期望 %s,实际 %s", want, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestResolveRejected 确认非法/敏感/不存在的路径一律不放行。
|
|
func TestResolveRejected(t *testing.T) {
|
|
dir := t.TempDir()
|
|
for _, name := range []string{"secret.env", "notes.md", "index.html", "app.conf"} {
|
|
if err := os.WriteFile(filepath.Join(dir, name), []byte("x"), 0o644); err != nil {
|
|
t.Fatalf("准备测试文件失败: %v", err)
|
|
}
|
|
}
|
|
if err := os.Mkdir(filepath.Join(dir, "sub.txt"), 0o755); err != nil {
|
|
t.Fatalf("准备测试目录失败: %v", err)
|
|
}
|
|
|
|
r := newTestResolver(t, Config{Dir: dir})
|
|
|
|
cases := []string{
|
|
"/",
|
|
"",
|
|
"/secret.env", // 敏感后缀不在白名单
|
|
"/notes.md", // 后缀不在白名单
|
|
"/app.conf", // 配置文件不得外泄
|
|
"/index.html", // 会被 ServeFile 重定向
|
|
"/sub/dir.txt", // 只允许根路径单段文件名
|
|
"/../secret.env", // 路径穿越
|
|
"/1XXl8fPHr9.txt/../..", // 路径穿越
|
|
"/missing.txt", // 文件不存在
|
|
"/sub.txt", // 目录不是文件
|
|
}
|
|
for _, c := range cases {
|
|
if got, ok := r.Resolve(c); ok {
|
|
t.Fatalf("路径 %q 不应被放行,实际解析为 %s", c, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestAllowHost 确认域名白名单按「忽略端口 + 忽略大小写」匹配。
|
|
func TestAllowHost(t *testing.T) {
|
|
r := newTestResolver(t, Config{Dir: t.TempDir(), Hosts: "api.yunzer.cn"})
|
|
|
|
if !r.AllowHost("api.yunzer.cn") {
|
|
t.Fatal("api.yunzer.cn 应被允许")
|
|
}
|
|
if !r.AllowHost("API.Yunzer.cn:443") {
|
|
t.Fatal("带端口与大写的 api.yunzer.cn 应被允许")
|
|
}
|
|
if r.AllowHost("other.com") {
|
|
t.Fatal("未在白名单里的域名不应被允许")
|
|
}
|
|
|
|
// 未配置 hosts 时,所有走本服务的域名都生效
|
|
all := newTestResolver(t, Config{Dir: t.TempDir()})
|
|
if !all.AllowHost("api.yunzer.cn") || !all.AllowHost("dh2.fun") {
|
|
t.Fatal("未配置 verify_hosts 时应允许所有域名")
|
|
}
|
|
}
|
|
|
|
// TestNewDisabled 确认 dir = "-" 表示关闭该功能。
|
|
func TestNewDisabled(t *testing.T) {
|
|
r, err := New(Config{Dir: "-"})
|
|
if err != nil {
|
|
t.Fatalf("不应报错: %v", err)
|
|
}
|
|
if r != nil {
|
|
t.Fatal("dir = \"-\" 时 Resolver 应为 nil")
|
|
}
|
|
}
|
|
|
|
// TestDefaultExts 确认默认扩展名包含 .txt。
|
|
func TestDefaultExts(t *testing.T) {
|
|
r := newTestResolver(t, Config{Dir: t.TempDir()})
|
|
found := false
|
|
for _, e := range r.Exts() {
|
|
if e == ".txt" {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatalf("默认扩展名应包含 .txt,实际 %v", r.Exts())
|
|
}
|
|
}
|