rules_test.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. package main
  2. import (
  3. "os"
  4. "path/filepath"
  5. "testing"
  6. )
  7. func TestRuleProblem(t *testing.T) {
  8. roots := []string{"/home", "/mnt", "/media"}
  9. ok := []string{"/home/u/docs", "/mnt/nas/a", "/media/u/Data/资料", "/mnt/x"}
  10. bad := map[string]string{
  11. "/etc": "只能选择",
  12. "/home": "本身",
  13. "/mnt": "本身",
  14. "/home/u": "用户主目录",
  15. "/home/u/.ssh": "隐藏",
  16. "/home/u/a/.git/x": "隐藏",
  17. "/homex/u/a": "只能选择",
  18. "/home/u/../../etc": "规范",
  19. "relative/path": "规范",
  20. "/srv/agent-server": "只能选择",
  21. "/media/u/Data/.env": "隐藏",
  22. }
  23. for _, p := range ok {
  24. if msg := ruleProblem(p, roots); msg != "" {
  25. t.Errorf("%s 应该允许,实际:%s", p, msg)
  26. }
  27. }
  28. for p, want := range bad {
  29. if msg := ruleProblem(p, roots); msg == "" || !contains(msg, want) {
  30. t.Errorf("%s 应该拒绝(%s),实际:%q", p, want, msg)
  31. }
  32. }
  33. }
  34. func contains(s, sub string) bool {
  35. return len(sub) == 0 || (len(s) >= len(sub) && indexOf(s, sub) >= 0)
  36. }
  37. func indexOf(s, sub string) int {
  38. for i := 0; i+len(sub) <= len(s); i++ {
  39. if s[i:i+len(sub)] == sub {
  40. return i
  41. }
  42. }
  43. return -1
  44. }
  45. // 用临时目录模拟允许的根目录
  46. func tempRoots(t *testing.T) (string, []string) {
  47. base, _ := filepath.EvalSymlinks(t.TempDir())
  48. root := filepath.Join(base, "mnt")
  49. if err := os.MkdirAll(filepath.Join(root, "data", "docs"), 0o755); err != nil {
  50. t.Fatal(err)
  51. }
  52. return base, []string{root}
  53. }
  54. func TestCheckSourceAndBrowse(t *testing.T) {
  55. base, roots := tempRoots(t)
  56. root := roots[0]
  57. os.MkdirAll(filepath.Join(root, "data", ".hidden"), 0o755)
  58. os.MkdirAll(filepath.Join(base, "outside"), 0o755)
  59. os.Symlink(filepath.Join(base, "outside"), filepath.Join(root, "escape"))
  60. os.Symlink(filepath.Join(root, "data", "docs"), filepath.Join(root, "inner"))
  61. os.WriteFile(filepath.Join(root, "file.txt"), []byte("x"), 0o644)
  62. if real, err := checkSource(filepath.Join(root, "data", "docs"), roots); err != nil || real != filepath.Join(root, "data", "docs") {
  63. t.Fatalf("应该通过:%v %v", real, err)
  64. }
  65. if real, err := checkSource(filepath.Join(root, "inner"), roots); err != nil || real != filepath.Join(root, "data", "docs") {
  66. t.Fatalf("范围内的符号链接解析为真实路径:%v %v", real, err)
  67. }
  68. cases := map[string]string{
  69. filepath.Join(root, "escape"): "not_allowed",
  70. filepath.Join(root, "data", ".hidden"): "not_allowed",
  71. filepath.Join(root, "file.txt"): "not_allowed",
  72. filepath.Join(root, "nope"): "path_missing",
  73. root: "not_allowed",
  74. "relative": "not_allowed",
  75. }
  76. for p, state := range cases {
  77. if _, err := checkSource(p, roots); err == nil || err.State != state {
  78. t.Errorf("%s 应该是 %s,实际 %v", p, state, err)
  79. }
  80. }
  81. res, cerr := browse(root, roots)
  82. if cerr != nil {
  83. t.Fatal(cerr)
  84. }
  85. got := map[string]browseEntry{}
  86. for _, e := range res.Entries {
  87. got[e.Name] = e
  88. }
  89. if _, ok := got["file.txt"]; ok {
  90. t.Error("文件不应出现在目录选择中")
  91. }
  92. if !got["data"].Selectable || got["escape"].Selectable || !got["inner"].Selectable {
  93. t.Errorf("可选状态不正确:%+v", got)
  94. }
  95. if res.Parent != "" {
  96. t.Errorf("根目录的上级应为空:%q", res.Parent)
  97. }
  98. sub, _ := browse(filepath.Join(root, "data"), roots)
  99. for _, e := range sub.Entries {
  100. if e.Name == ".hidden" {
  101. t.Error("隐藏目录不应出现")
  102. }
  103. }
  104. if _, cerr := browse(filepath.Join(base, "outside"), roots); cerr == nil {
  105. t.Error("不能浏览范围以外的目录")
  106. }
  107. if _, cerr := browse(filepath.Join(root, "escape"), roots); cerr == nil {
  108. t.Error("不能经由符号链接浏览范围以外的目录")
  109. }
  110. top, _ := browse("", roots)
  111. if len(top.Entries) != 1 || top.Entries[0].Selectable {
  112. t.Errorf("顶层列出允许的根目录且不能直接选择:%+v", top.Entries)
  113. }
  114. }