Validate that a file name from a backup does not contain path traversal sequences. This prevents a corrupted or tampered backup from accessing files outside the intended directories during restore.
| 101 | /// Validate that a file name from a backup does not contain path traversal sequences. |
| 102 | /// This prevents a corrupted or tampered backup from accessing files outside the intended directories during restore. |
| 103 | void validateFileNameFromBackup(const String & file_name, const String & field_name, const String & backup_name_for_logging) |
| 104 | { |
| 105 | fs::path path(file_name); |
| 106 | |
| 107 | /// Reject absolute or rooted paths. |
| 108 | if (path.is_absolute() || path.has_root_name() || path.has_root_directory()) |
| 109 | throw Exception( |
| 110 | ErrorCodes::INSECURE_PATH, |
| 111 | "Backup {}: <{}> {} is an absolute path, which is not allowed", |
| 112 | backup_name_for_logging, |
| 113 | field_name, |
| 114 | quoteString(file_name)); |
| 115 | |
| 116 | /// Normalize the path and check that it does not escape the backup root. |
| 117 | auto normalized = path.lexically_normal(); |
| 118 | |
| 119 | /// Reject empty or degenerate paths. |
| 120 | if (normalized.empty() || normalized == fs::path(".")) |
| 121 | throw Exception( |
| 122 | ErrorCodes::BACKUP_DAMAGED, |
| 123 | "Backup {}: <{}> {} is empty or invalid", |
| 124 | backup_name_for_logging, |
| 125 | field_name, |
| 126 | quoteString(file_name)); |
| 127 | |
| 128 | /// After normalization, a path that escapes the root starts with "..". |
| 129 | if (*normalized.begin() == "..") |
| 130 | throw Exception( |
| 131 | ErrorCodes::INSECURE_PATH, |
| 132 | "Backup {}: <{}> {} resolves to a path outside the backup, which is not allowed", |
| 133 | backup_name_for_logging, |
| 134 | field_name, |
| 135 | quoteString(file_name)); |
| 136 | } |
| 137 | } |
| 138 | |
| 139 |
no test coverage detected