1
0

valid_path.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. package utils
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "github.com/0xJacky/Nginx-UI/internal/cache"
  7. "github.com/0xJacky/Nginx-UI/internal/helper"
  8. "github.com/0xJacky/Nginx-UI/internal/nginx"
  9. "github.com/0xJacky/Nginx-UI/settings"
  10. )
  11. // IsValidLogPath checks if a log path is valid:
  12. // 1. It must be a regular file or a symlink to a regular file
  13. // 2. It must not point to a console or special device
  14. // 3. It must be under the whitelist directories
  15. func IsValidLogPath(logPath string) bool {
  16. // First check if the path is in the whitelist
  17. if !isLogPathUnderWhiteList(logPath) {
  18. return false
  19. }
  20. // Check if the path exists
  21. fileInfo, err := os.Lstat(logPath)
  22. if err != nil {
  23. // If the file doesn't exist, it might be created later
  24. // We'll assume it's valid for now
  25. return true
  26. }
  27. // If it's a symlink, follow it safely
  28. if fileInfo.Mode()&os.ModeSymlink != 0 {
  29. // Use EvalSymlinks to safely resolve the entire symlink chain
  30. // This function detects circular symlinks and returns an error
  31. resolvedPath, err := filepath.EvalSymlinks(logPath)
  32. if err != nil {
  33. return false
  34. }
  35. // Check the resolved target file
  36. targetInfo, err := os.Stat(resolvedPath)
  37. if err != nil {
  38. return false
  39. }
  40. // Only accept regular files as targets
  41. result := targetInfo.Mode().IsRegular()
  42. return result
  43. }
  44. // For non-symlinks, just check if it's a regular file
  45. result := fileInfo.Mode().IsRegular()
  46. return result
  47. }
  48. // isLogPathUnderWhiteList checks if a log path is under one of the paths in LogDirWhiteList
  49. func isLogPathUnderWhiteList(path string) bool {
  50. prefix := nginx.GetPrefix()
  51. cacheKey := fmt.Sprintf("isLogPathUnderWhiteList:%s", path)
  52. res, ok := cache.Get(cacheKey)
  53. // If cached, return the result directly
  54. if ok {
  55. return res.(bool)
  56. }
  57. // Only build the whitelist when cache miss occurs
  58. logDirWhiteList := append([]string{}, settings.NginxSettings.LogDirWhiteList...)
  59. accessLogPath := nginx.GetAccessLogPath()
  60. errorLogPath := nginx.GetErrorLogPath()
  61. if accessLogPath != "" {
  62. logDirWhiteList = append(logDirWhiteList, filepath.Dir(accessLogPath))
  63. }
  64. if errorLogPath != "" {
  65. logDirWhiteList = append(logDirWhiteList, filepath.Dir(errorLogPath))
  66. }
  67. if prefix != "" {
  68. logDirWhiteList = append(logDirWhiteList, prefix)
  69. }
  70. // Check if path is under any whitelist directory
  71. for _, whitePath := range logDirWhiteList {
  72. if helper.IsUnderDirectory(path, whitePath) {
  73. cache.Set(cacheKey, true, 0)
  74. return true
  75. }
  76. }
  77. // Cache negative result as well to avoid repeated checks
  78. cache.Set(cacheKey, false, 0)
  79. return false
  80. }