{"record":{"id":"64c6f9681bbb7a91","repo":"babalae/better-genshin-impact","slug":"path-64c6f9","errorCode":null,"errorMessage":"文件路径 '{path}' 越界访问!","messagePattern":"文件路径 '(.+?)' 越界访问!","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"BetterGenshinImpact/Core/Script/Utils/ScriptUtils.cs","lineNumber":35,"sourceCode":"\n        // 检查是否含有非法文件名字符\n        var invalidChars = Path.GetInvalidFileNameChars();\n        string fileName = Path.GetFileName(path);\n        if (fileName.Any(c => invalidChars.Contains(c)))\n        {\n            throw new ArgumentException($\"文件路径 '{path}' 包含非法字符\");\n        }\n\n        // 替换分隔符\n        path = path.Replace('\\\\', '/');\n\n        // 组合并获取绝对路径\n        var fullPath = Path.GetFullPath(Path.Combine(root, path));\n\n        // 防止越界访问\n        if (!fullPath.StartsWith(root, StringComparison.OrdinalIgnoreCase))\n        {\n            throw new ArgumentException($\"文件路径 '{path}' 越界访问!\");\n        }\n\n        return fullPath;\n    }\n}\n","sourceCodeStart":17,"sourceCodeEnd":41,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/Core/Script/Utils/ScriptUtils.cs#L17-L41","documentation":"Thrown by ScriptUtils.NormalizePath as a path-traversal guard: after combining root + path and resolving to an absolute path, the result does not start with root. This blocks directory-escape attempts (e.g., '../../etc/passwd' or absolute paths that leave root).","triggerScenarios":"Path.GetFullPath(Path.Combine(root, path)) yields a path outside root. Examples: path='../../../secrets' resolves above root; path='/etc/passwd' is absolute and ignores root (on the drive); path='..\\\\..' on Windows escapes; path contains symlink-resolved escapes (note: GetFullPath does NOT resolve symlinks, so this guard can be bypassed via symlinks).","commonSituations":"Malicious or malformed manifest/webview input attempting traversal; a legitimate relative path that accidentally steps above root due to too many '..'; cross-drive paths on Windows where StartsWith(root, OrdinalIgnoreCase) fails because root and resolved are on different drives.","solutions":["Ensure callers pass truly relative paths; strip leading slashes/separators from user input before NormalizePath.","Use a stricter containment check: compare full path segments, not StartsWith string comparison (StartsWith can be fooled by sibling dirs sharing a prefix, e.g., root='C:\\app' matches 'C:\\app-other\\x').","Add a trailing separator to root before the comparison: fullPath.StartsWith(root + Path.DirectorySeparatorChar, OrdinalIgnoreCase) || fullPath == root.","If symlinks are a concern, resolve them with Path.GetFullPath after enumerating links, or disallow symlinks entirely."],"exampleFix":"// before\nvar fullPath = Path.GetFullPath(Path.Combine(root, path));\nif (!fullPath.StartsWith(root, StringComparison.OrdinalIgnoreCase))\n    throw new ArgumentException($\"文件路径 '{path}' 越界访问!\");\nreturn fullPath;\n\n// after (trailing-separator containment, prefix-collision-safe)\nvar rootFull = Path.GetFullPath(root).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)\n              + Path.DirectorySeparatorChar;\nvar fullPath = Path.GetFullPath(Path.Combine(rootFull, path));\nif (!fullPath.StartsWith(rootFull, StringComparison.OrdinalIgnoreCase))\n    throw new UnauthorizedAccessException($\"文件路径 '{path}' 越界访问! 已解析={fullPath}, 根={rootFull}\");\nreturn fullPath;","handlingStrategy":"validation","validationCode":"// Trailing-separator containment (prefix-collision-safe)\nvar rootFull = Path.GetFullPath(root).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)\n              + Path.DirectorySeparatorChar;\nvar fullPath = Path.GetFullPath(Path.Combine(rootFull, path));\nif (!fullPath.StartsWith(rootFull, StringComparison.OrdinalIgnoreCase))\n    throw new UnauthorizedAccessException($\"越界: {fullPath}\");","typeGuard":"static bool IsWithinRoot(string root, string fullPath)\n{\n    var r = Path.GetFullPath(root).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)\n            + Path.DirectorySeparatorChar;\n    return fullPath.StartsWith(r, StringComparison.OrdinalIgnoreCase);\n}","tryCatchPattern":"catch (UnauthorizedAccessException ex) when (ex.Message.Contains(\"越界\"))\n{\n    _logger.LogWarning(\"拒绝越界路径访问: {Msg}\", ex.Message);\n    throw;\n}","preventionTips":["Use a trailing-separator containment check, not bare StartsWith.","Strip leading separators and '..' from untrusted input.","Be aware GetFullPath does not resolve symlinks — disallow them if untrusted."],"tags":["security","path-traversal","validation","filesystem","cwe-22"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}