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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
//! What an analyzer's environment is, in one place, for both backends.
//!
//! Two things reach an analyzer's environment and they read alike while doing
//! opposite things:
//!
//! - **Inheriting locates.** `CARGO_HOME`, `RUSTUP_HOME` — where the toolchain
//! is *on this machine*, which only the parent environment knows and which is
//! the user's to choose. This process has no opinion on the value.
//! - **Setting constrains.** `CARGO_TARGET_DIR` — where the build may write,
//! which is a property the runner **guarantees** and therefore must choose
//! itself. A guarantee that reads its own precondition out of the ambient
//! environment is not a guarantee.
//!
//! Conflating them is not hypothetical. `CARGO_TARGET_DIR` was once listed as a
//! name to *inherit* by a module whose documentation promised it *set* one, and
//! inheriting a name the parent has not set is a no-op that reads as a
//! configuration — so `roteiro lint` wrote into the tree it was reviewing under
//! a paragraph saying it did not (ADR-0020 v1.4).
//!
//! # Why this module exists rather than the two backends each having their own
//!
//! Because they did, and that is the shape that produced the defect above.
//! [`crate::subprocess`] had [`ChildEnv`] and [`crate::boxlite`] had a
//! `guest_environment()` built from nothing — two mechanisms for one concept,
//! which is how the two drift into disagreeing about what an analyzer is
//! allowed to see.
//!
//! They are now one type with **two consumers**, and the difference between the
//! consumers is real rather than tidied away:
//!
//! | | host child | microVM guest |
//! |---|---|---|
//! | [`ChildEnv::set`] | applied | applied |
//! | [`ChildEnv::inherit`] | applied | **cannot mean anything** |
//! | base ([`BASE`]) | applied | applied |
//!
//! The `inherit` half is host-only **by construction, not by omission**. A guest
//! does not share this machine's filesystem, so `CARGO_HOME=/Users/you/.cargo`
//! names nothing there; and it does not share this machine's environment block,
//! so there is no parent to inherit *from*. Everything a guest needs to locate
//! is a property of its image or of a mount, and both are chosen by the runner —
//! so in a guest **everything is set**, and the inherit half has nothing it
//! could express. [`ChildEnv::guest_pairs`] says so where it would otherwise be
//! silently dropped.
//!
//! @rto:0014
//! @rto:0020
/// Variables both backends put on every analyzer, whatever launched it.
///
/// One list rather than two identical ones, so a change to what an analyzer is
/// told cannot land on one backend and not the other — which is the drift the
/// module documentation describes.
pub const BASE: & = &;
/// The names a host child inherits from this process whatever the caller asked
/// for.
///
/// `PATH` because the analyzer needs to find its own helpers, `HOME` because
/// tools that cannot locate a home directory fail in confusing ways, and the
/// rest because a temporary directory is not optional on either platform. Both
/// are the parent's, unchanged.
pub const HOST_FLOOR: & = &;
/// The two ways a variable can reach an analyzer's environment, kept apart.
///
/// See the module documentation for why they are two fields rather than one
/// list, and why only one of them can mean anything in a guest.
pub
/// Give a host child a minimal, explicit environment.
///
/// A third-party binary running on a developer's machine inherits everything by
/// default, and a developer's environment is where `GITHUB_TOKEN`, `AWS_*`,
/// `SEMGREP_APP_TOKEN` and an SSH agent socket live. None of that is an analyzer
/// input, so none of it is passed.
///
/// This is a *reduction* in what the process can reach, **not a boundary**. It
/// stops an analyzer from picking up a credential by accident; it does not stop
/// one that goes looking. The boundary is [`crate::boxlite`], and the difference
/// between the two is the whole of ADR-0014.
///
/// [`ChildEnv::inherit`] is for a caller whose tool needs more than a parser
/// does — the linter in [`crate::lint`] needs the variables that locate a Rust
/// toolchain, and would otherwise be handed a `PATH` shim with no toolchain
/// behind it. It is a **named list per caller**, not a pattern or an
/// inherit-everything escape: each addition is a variable somebody wrote down
/// and justified.
pub