Skip to main content

Crate rto_exec

Crate rto_exec 

Source
Expand description

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);

Re-exports§

pub use adapter::ADAPTERS;
pub use adapter::Adapter;
pub use adapter::AssetPaths;
pub use adapter::Invocation;
pub use adapter::NO_SNIPPET;
pub use adapter::NativeContext;
pub use adapter::UNKNOWN_VERSION;
pub use adapter::adapter_for;
pub use adapter::known_analyzers;
pub use adapter::snippet_hash;
pub use adapter::snippet_hash_at;
pub use crossref::Correspondence;
pub use crossref::Report;
pub use crossref::cross_reference;
pub use snippet::NoSnippets;
pub use snippet::SnippetSource;
pub use snippet::WorktreeSnippets;

Modules§

adapter
Per-analyzer adapters: native analyzer output in, a NormalizedReport out.
crossref
Cross-referencing dependency findings across analyzers.
snippet
The source text a finding points at, read from the analyzed worktree.

Structs§

AnalysisRequest
What a caller asks a backend to do.
AnalysisResponse
What a backend returns: normalized findings plus the evidence for the run that produced them.
IngestRunner
Consumes a normalized report and yields the same values any other backend would.
NormalizedReport
A normalized analyzer report — the interchange format roteiro security ingest consumes and every analyzer adapter emits.
ReportFinding
One finding as it appears in a normalized report.
Worktree
The worktree an analyzer is pointed at.

Enums§

Consent
Explicit user consent to run an analyzer.
ExecError
Errors an analyzer backend can raise.

Constants§

MAX_REPORT_FINDINGS
The most findings accepted from one report.
REPORT_SCHEMA
Schema tag every normalized report must carry. Bump on a breaking change to the report format, exactly as rto_graph::ARTIFACT_SCHEMA does for the graph artifact.

Traits§

AnalyzerRunner
One analyzer backend.

Functions§

age_in_days
Whole days between two RFC 3339 UTC timestamps, None if either will not parse.
check_reported_path
Reject a reported path that is absolute or climbs out of the worktree.
check_request
The preflight every backend shares: explicit consent, denied egress, a read-only worktree, and a well-formed analyzer id.
normalize_native
Turn one analyzer’s native output into a normalized report, using that analyzer’s adapter.
rfc3339_from_unix
Format secs seconds since the Unix epoch as RFC 3339 UTC.
rfc3339_utc
Format a SystemTime as RFC 3339 UTC with second granularity (2026-08-15T09:00:04Z).
sha256_hex
Lowercase hex SHA-256 of bytes.
unix_from_rfc3339
Parse YYYY-MM-DD or a full RFC 3339 UTC timestamp to seconds since the epoch. Deliberately strict and small: it accepts what rfc3339_from_unix emits and the date-only form advisory databases publish, and refuses anything else rather than guessing an offset.
worktree_id
Derive a stable, opaque id for the checkout at path.