203 lines
4.8 KiB
Go
203 lines
4.8 KiB
Go
package filesystem
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"path/filepath"
|
||
"runtime"
|
||
"strings"
|
||
"sync"
|
||
)
|
||
|
||
// PathValidator 路径验证器接口
|
||
// 提供统一的路径安全检查,避免重复代码
|
||
type PathValidator interface {
|
||
// Validate 验证路径并返回详细的错误信息
|
||
Validate(path string) *ValidationError
|
||
|
||
// IsSafe 快速检查路径是否安全
|
||
IsSafe(path string) bool
|
||
|
||
// IsSensitive 检查路径是否为敏感路径
|
||
IsSensitive(path string) bool
|
||
}
|
||
|
||
// ValidationError 验证错误
|
||
type ValidationError struct {
|
||
Path string
|
||
Reason string
|
||
IsError bool // true=禁止访问, false=敏感路径
|
||
}
|
||
|
||
func (e *ValidationError) Error() string {
|
||
if e.IsError {
|
||
return fmt.Sprintf("路径验证失败: %s - %s", e.Path, e.Reason)
|
||
}
|
||
return fmt.Sprintf("敏感路径警告: %s - %s", e.Path, e.Reason)
|
||
}
|
||
|
||
// DefaultPathValidator 默认路径验证器实现
|
||
type DefaultPathValidator struct {
|
||
config *Config
|
||
}
|
||
|
||
// NewPathValidator 创建新的路径验证器
|
||
func NewPathValidator(config *Config) PathValidator {
|
||
return &DefaultPathValidator{
|
||
config: config,
|
||
}
|
||
}
|
||
|
||
// Validate 验证路径
|
||
func (v *DefaultPathValidator) Validate(path string) *ValidationError {
|
||
// 清理路径
|
||
cleanPath := filepath.Clean(path)
|
||
|
||
// 1. 检查路径遍历攻击
|
||
if strings.Contains(cleanPath, PathTraversalPattern) {
|
||
return &ValidationError{
|
||
Path: path,
|
||
Reason: "检测到路径遍历尝试",
|
||
IsError: true,
|
||
}
|
||
}
|
||
|
||
// 2. 检查符号链接
|
||
if !v.config.Security.PathValidation.AllowSymlinks {
|
||
if fi, err := os.Lstat(path); err == nil && fi.Mode()&os.ModeSymlink != 0 {
|
||
return &ValidationError{
|
||
Path: path,
|
||
Reason: "不允许访问符号链接",
|
||
IsError: true,
|
||
}
|
||
}
|
||
}
|
||
|
||
// 3. 检查UNC路径(Windows)
|
||
if runtime.GOOS == "windows" && !v.config.Security.PathValidation.AllowUNCPaths {
|
||
if strings.HasPrefix(cleanPath, `\\`) {
|
||
return &ValidationError{
|
||
Path: path,
|
||
Reason: "不允许访问UNC网络路径",
|
||
IsError: true,
|
||
}
|
||
}
|
||
}
|
||
|
||
// 4. Windows特定检查
|
||
if runtime.GOOS == "windows" && v.config.Security.PathValidation.CheckWindowsSystemPaths {
|
||
if err := v.checkWindowsSystemPaths(cleanPath); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
// 5. 检查敏感路径
|
||
if v.isSensitivePath(cleanPath) {
|
||
return &ValidationError{
|
||
Path: path,
|
||
Reason: "访问敏感路径",
|
||
IsError: false, // 警告而非错误
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// IsSafe 快速检查路径是否安全
|
||
func (v *DefaultPathValidator) IsSafe(path string) bool {
|
||
err := v.Validate(path)
|
||
return err == nil || !err.IsError
|
||
}
|
||
|
||
// IsSensitive 检查路径是否为敏感路径
|
||
func (v *DefaultPathValidator) IsSensitive(path string) bool {
|
||
cleanPath := filepath.Clean(path)
|
||
return v.isSensitivePath(cleanPath)
|
||
}
|
||
|
||
// checkWindowsSystemPaths 检查Windows系统路径
|
||
func (v *DefaultPathValidator) checkWindowsSystemPaths(path string) *ValidationError {
|
||
lowerPath := strings.ToLower(path)
|
||
|
||
// 检查盘符
|
||
if len(lowerPath) >= 3 && lowerPath[1] == ':' {
|
||
driveLetter := lowerPath[0:1]
|
||
|
||
// 检查系统核心目录
|
||
forbiddenDirs := []string{
|
||
driveLetter + ":\\windows",
|
||
driveLetter + ":\\system volume information",
|
||
driveLetter + ":\\boot",
|
||
}
|
||
|
||
for _, fb := range forbiddenDirs {
|
||
if strings.HasPrefix(lowerPath, fb) {
|
||
return &ValidationError{
|
||
Path: path,
|
||
Reason: "禁止访问系统关键目录",
|
||
IsError: true,
|
||
}
|
||
}
|
||
}
|
||
|
||
// 检查用户配置目录(可能包含敏感信息)
|
||
forbiddenPaths := []string{
|
||
"\\.ssh\\",
|
||
"\\.gnupg\\",
|
||
"\\.config\\",
|
||
"\\appdata\\roaming\\mozilla\\",
|
||
"\\appdata\\roaming\\google\\chrome\\",
|
||
"\\appdata\\local\\google\\user data\\",
|
||
}
|
||
|
||
for _, fp := range forbiddenPaths {
|
||
if strings.Contains(lowerPath, fp) {
|
||
return &ValidationError{
|
||
Path: path,
|
||
Reason: "禁止访问敏感配置目录",
|
||
IsError: true,
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// isSensitivePath 检查是否为敏感路径
|
||
func (v *DefaultPathValidator) isSensitivePath(path string) bool {
|
||
lowerPath := strings.ToLower(filepath.Clean(path))
|
||
|
||
// 检查配置的敏感路径列表
|
||
for _, sp := range v.config.Security.PathValidation.SensitivePaths {
|
||
if strings.Contains(lowerPath, strings.ToLower(sp)) {
|
||
return true
|
||
}
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
// 默认路径验证器(缓存,避免每次调用重复初始化)
|
||
var (
|
||
defaultValidatorOnce sync.Once
|
||
defaultValidator PathValidator
|
||
)
|
||
|
||
func getDefaultValidator() PathValidator {
|
||
defaultValidatorOnce.Do(func() {
|
||
defaultValidator = NewPathValidator(DefaultConfig())
|
||
})
|
||
return defaultValidator
|
||
}
|
||
|
||
// isSafePath 兼容函数:保持向后兼容
|
||
func isSafePath(path string) bool {
|
||
return getDefaultValidator().IsSafe(path)
|
||
}
|
||
|
||
// isSensitivePath 兼容函数:保持向后兼容
|
||
func isSensitivePath(path string) bool {
|
||
return getDefaultValidator().IsSensitive(path)
|
||
}
|