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
//! 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);
//! ```
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
/// 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.