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
//! What:
//! The `rulesFile` option of `security/forbidden-strings`:
//! which files in the
//! repository hold the scanner's private rules,
//! each written relative to the
//! repository's top level.
//! This module checks one path;
//! `config_policies.rs` reads the string and array forms and calls it for each path.
//! Why:
//! The user decided on 2026-10-05 that configuration names the rules file first,
//! then
//! `FORBIDDEN_STRINGS_RULES`,
//! then the default file.
//! A configured name is checked
//! when the configuration is read:
//! it must stay inside the repository by its words
//! alone,
//! so a configuration cannot point the scanner at a file elsewhere on the
//! machine.
//! A name that is missing on disk is not a configuration error;
//! loading
//! the rules reports it when the policy runs.
//!
//! In TS you'd write (pseudocode):
//! ```ts
//! // const rulesFile = parseRulesFile(option); // throws RulesFileRefusal
//! ```
/// What:
/// Why a `rulesFile` value was refused.
/// `#[derive(...)]` generates copying,
/// debug
/// printing and `==`.
/// Why:
/// The configuration error names the key and explains this one reason.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// type RulesFileRefusal = 'empty' | 'absolute' | 'drive' | 'backslash' | 'nul' | 'empty-component' | 'dot-component';
/// ```
/// What:
/// The explanation of one refusal,
/// completing the sentence "the value ...".
/// Why:
/// One place owns the words,
/// so every refusal reads the same.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// function refusalReason(refusal: RulesFileRefusal): string;
/// ```
/// What:
/// Whether one character is `/`,
/// the separator of the option's components.
/// Why:
/// A named predicate for `split`,
/// because the repository bans anonymous functions.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// const isSlash = (character: string) => character === '/';
/// ```
/// What:
/// Whether `text` starts with an ASCII letter and a colon,
/// as `C:` does.
/// Why:
/// Joining such a value to the repository's top level on Windows would name
/// another volume.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// const startsWithDrive = (text: string) => /^[A-Za-z]:/.test(text);
/// ```
/// What:
/// Check one `rulesFile` value.
/// `Result<(), RulesFileRefusal>` is "accepted" or the
/// first reason it is not.
/// Why:
/// A value that is relative,
/// uses `/` between non-empty components,
/// and has no `.`
/// or `..` component names a file inside the repository on every platform,
/// whatever
/// the file system later holds.
/// The checks run in a fixed order so the first
/// reason reported is the same on every platform.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// function checkRulesFile(value: string): void; // throws RulesFileRefusal
/// ```
/// Option-value controls stay out of the release executable.