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
//! The analyzer execution seam: one contract, interchangeable backends.
//!
//! Running an external analyzer (`cargo-audit`, `semgrep`, successors) can happen
//! in CI, on a developer's machine, or — later — locally inside a sandbox. This
//! crate exists so those stop being competing architectures: every backend
//! implements one [`AnalyzerRunner`] trait, takes one [`AnalysisRequest`], and
//! returns one [`AnalysisResponse`] of normalized findings plus run evidence. A
//! caller never learns which backend produced a result, so adding the sandboxed
//! and subprocess backends later changes no call site.
//!
//! Today there is exactly one implementation, [`IngestRunner`], which consumes a
//! normalized report produced elsewhere. It is the zero-install default, not a
//! fallback: it needs no container runtime and adds no isolation surface, and
//! what it produces is byte-for-byte the shape a sandboxed run will produce.
//!
//! # What this crate does not do
//!
//! It does not decide how results are *stored*. Persistence lives in `rto-graph`,
//! which files findings in their own tables — never `nodes`/`edges`, never a
//! provenance class, never in the exported graph artifact (ADR-0012). Nothing
//! here can move the published `GraphArtifact` by a byte, and that is checked by
//! test rather than assumed.
//!
//! No analyzer is implemented here, and no sandbox dependency is pulled in; the
//! backends arrive behind their own features (ADR-0014).
//!
//! @rto:0014
//! @rto:0012
//!
//! # Example
//!
//! ```
//! use rto_exec::{AnalysisRequest, AnalyzerRunner, Consent, IngestRunner, Worktree};
//! use rto_graph::SourceIdentity;
//!
//! let report = br#"{
//! "schema": "roteiro.findings/v1",
//! "analyzer": "cargo-audit",
//! "analyzer_version": "0.21.0",
//! "started_at": "2026-08-15T09:00:00Z",
//! "ended_at": "2026-08-15T09:00:04Z",
//! "exit_status": 1,
//! "findings": [{
//! "identity": ["RUSTSEC-2024-0001", "openssl", "0.10.5", "lock123"],
//! "rule": "RUSTSEC-2024-0001",
//! "severity": "high",
//! "title": "openssl is vulnerable",
//! "message": "upgrade to 0.10.66"
//! }]
//! }"#;
//!
//! let request = AnalysisRequest {
//! analyzer: "cargo-audit".to_owned(),
//! worktree: Worktree::read_only("/repo".as_ref()).expect("worktree"),
//! network: rto_graph::NetworkPolicy::Deny,
//! consent: Consent::Granted,
//! source: SourceIdentity::default(),
//! };
//! let response = IngestRunner::new(report.to_vec()).run(&request).expect("ingest");
//! assert_eq!(response.findings.len(), 1);
//! assert_eq!(response.run.isolation, rto_graph::Isolation::Ingested);
//! ```
/// Where the pinned-asset cache lives, and the precedence that decides it.
///
/// Its source carries no `//!` header because `build.rs` pulls the same file in
/// with `include!`, where an inner doc comment is a syntax error — so the module
/// documentation lives here instead. `build.rs` needs it to find the sandbox
/// runtime `roteiro security prefetch` installed, which is the same cache
/// [`asset_paths::asset_root`] names; read the file's own comments for why that
/// is shared rather than copied.
// Asset provisioning is **always compiled**, behind no feature at all.
//
// It used to be `cfg(any(exec-subprocess, exec-boxlite))`, on the reading that
// provisioning belongs to whichever backend consumes the assets. That was the
// wrong shape and this module half-said so already: it is shared between the
// backends and owned by neither, and the note on `SANDBOX_RUNTIME_NOTICE` below
// records that an `exec-subprocess`-only build provisions *for a later
// `exec-boxlite` build* — provisioning already served a backend that was not
// compiled in.
//
// The bootstrap argument settles it. `AGENTS.md` tells a contributor to run
// `roteiro security prefetch --allow-download` *before* building
// `--features exec-boxlite`, because that build script requires the verified
// archive at compile time. If prefetch lived behind an execution feature, you
// would need a build with a *different* execution backend compiled in before you
// could provision the one you actually wanted. That is circular.
//
// Nothing here executes anything: it downloads, digests, pins and reports. Every
// `Command::new` in this crate is in `subprocess.rs` or `boxlite.rs`, and both
// stay behind their features. Provisioning is not execution.
/// Emitting a `file://` URL for a local path, and reading one back.
///
/// Its source carries no `//!` header because `build.rs` pulls the same file in
/// with `include!`, where an inner doc comment is a syntax error — so the module
/// documentation lives here instead. `build.rs` is this crate's only emitter: it
/// prints the `BOXLITE_RUNTIME_URL=` recipe an operator pastes, and parses that
/// variable back when it is set. What reads the URL in between is `boxlite`'s
/// own `curl`, which percent-decodes and rejects an unencoded space outright —
/// read the file's own comments for the measurements, and for why the encoder
/// and the decoder have to be one file rather than two.
/// The per-file digests of the extracted sandbox runtime — **generated**.
///
/// Derived from the archives in [`runtime_pins`] by
/// `scripts/derive-runtime-file-pins.py`, and verified by `build.rs` against
/// what `boxlite` actually extracted, since those files rather than the archive
/// are what `include_bytes!` puts in the binary. Same `include!` arrangement,
/// and so the same standalone constraint; its module documentation lives here
/// for the same reason [`runtime_pins`]'s does.
/// The pinned sandbox-runtime archives, and the host-platform selection.
///
/// Its source carries no `//!` header because `build.rs` pulls the same file in
/// with `include!`, where an inner doc comment is a syntax error — so the module
/// documentation lives here instead. Read the file's own comments for what is
/// pinned and why it has to be.
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
/// The licence notice for the third-party binaries an `exec-boxlite` build
/// embeds, compiled in so it cannot be separated from what it describes.
///
/// `roteiro security prefetch` prints it before installing the sandbox runtime,
/// which is the same disclose-then-consent shape `roteiro model pull` uses. It
/// is compiled into every build, because every build can provision the runtime —
/// including one with no execution backend at all, which prefetches it for a
/// later `exec-boxlite` build — so the obligations travel with the artifact
/// rather than living only in the repository.
pub const SANDBOX_RUNTIME_NOTICE: &str = include_str!;
/// Lowercase hex SHA-256 of `bytes`.
///
/// Used for the report digest that ties an `AnalysisRun` to the exact bytes it
/// was derived from, and for deriving an opaque worktree id from a path.