1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
//! Name and reference validation utilities.
//!
//! # Architecture
//! Git has strict rules for naming references (branches, tags) and tree entries.
//! This module enforces these rules to prevent filesystem traversal vulnerabilities,
//! repository corruption, and ambiguity in revision parsing.
//!
//! # Design Rationale: Layered Validation
//! Validation is structured hierarchically. [`validate_name`] provides baseline
//! sanitization (length, emptiness, control characters). Specialized functions
//! like [`validate_ref_name`] and [`validate_tree_entry_name`] build upon this
//! baseline, adding domain-specific constraints. This prevents duplication and
//! ensures all names are fundamentally safe before context-specific rules are applied.
use crateMAX_NAME_LENGTH;
use crateVctrlError;
use Path;
/// Validates a generic name.
///
/// # Why this exists
/// Establishes the minimum safety criteria for any string used as an identifier
/// in the version control system. It prevents empty strings (which cause ambiguity),
/// excessively long strings (which can exhaust memory or trigger filesystem errors),
/// and ASCII control characters (which can corrupt terminal output or interprocess
/// communication).
///
/// # How it works
/// The function checks the byte length of the string against [`MAX_NAME_LENGTH`].
/// Because [`MAX_NAME_LENGTH`] is a `u64`, it must be safely downcast to `usize`
/// using `try_from` to support 32-bit architectures where `usize` is smaller than `u64`.
/// It then iterates over the bytes to detect ASCII control characters (e.g., `\0`, `\n`, `\t`).
///
/// # Errors
///
/// Returns [`VctrlError::InvalidName`] if the name is empty, exceeds the maximum
/// allowed length, or contains ASCII control characters.
///
/// # Examples
///
/// ```
/// # use libvctrl_handler::validation::validate_name;
/// assert!(validate_name("valid_name").is_ok());
/// assert!(validate_name("").is_err());
/// assert!(validate_name(&"a".repeat(256)).is_err());
/// assert!(validate_name("invalid\nname").is_err());
/// ```
/// Validates a reference name (e.g., branch or tag) strictly according to Git rules.
///
/// # Why this exists
/// Git references map directly to the filesystem (e.g., `.git/refs/heads/main`).
/// Without strict validation, a malicious reference name could traverse the filesystem
/// (e.g., `../../etc/passwd`) or create ambiguous revision queries (e.g., names
/// containing `..` or `~`). This function enforces the rules defined in
/// `git-check-ref-format`.
///
/// # How it works
/// It first applies baseline validation via [`validate_name`]. It then checks for
/// forbidden sequences:
/// - `..`: Prevents path traversal and ambiguous range specifiers.
/// - `~`, `^`, `:`: Prevents ambiguity with revision specifiers (e.g., `HEAD~1`).
/// - `.lock` extension: Prevents race conditions with Git's internal lock files.
/// - Leading/trailing dots or slashes: Prevents hidden files or directory confusion.
///
/// # Errors
///
/// Returns [`VctrlError::InvalidName`] if the name fails basic name validation
/// or contains forbidden characters or patterns.
///
/// # Examples
///
/// ```
/// # use libvctrl_handler::validation::validate_ref_name;
/// assert!(validate_ref_name("refs/heads/main").is_ok());
/// assert!(validate_ref_name("feature/branch").is_ok());
///
/// // Path traversal is forbidden
/// assert!(validate_ref_name("refs/heads/../danger").is_err());
///
/// // Cannot end with .lock
/// assert!(validate_ref_name("refs/heads/config.lock").is_err());
/// ```
/// Validates a tree entry name strictly.
///
/// # Why this exists
/// A tree entry represents a single file or subdirectory. Its name must be a
/// single path component, not a full path. Allowing path separators (`/` or `\`)
/// or directory aliases (`.` or `..`) would corrupt the tree hierarchy by injecting
/// implicit directories or allowing traversal outside the tree.
///
/// # How it works
/// After baseline validation via [`validate_name`], it scans for `/` and `\`
/// characters and explicitly rejects the strings `.` and `..`.
///
/// # Errors
///
/// Returns [`VctrlError::InvalidName`] if the name fails basic name validation
/// or contains forbidden path characters or names.
///
/// # Examples
///
/// ```
/// # use libvctrl_handler::validation::validate_tree_entry_name;
/// assert!(validate_tree_entry_name("file.txt").is_ok());
/// assert!(validate_tree_entry_name("src").is_ok());
///
/// // Path separators are forbidden
/// assert!(validate_tree_entry_name("dir/file.txt").is_err());
/// assert!(validate_tree_entry_name("dir\\file.txt").is_err());
///
/// // Directory aliases are forbidden
/// assert!(validate_tree_entry_name(".").is_err());
/// assert!(validate_tree_entry_name("..").is_err());
/// ```