pub struct AdmittedTool { /* private fields */ }Expand description
An executable whose exact bytes and version were admitted by a consumer.
Every execution rechecks digest/permission before spawning. Consumers must
exclude concurrent writers to the executable and its parent directories:
filesystem checks and exec are separate operations. This is not a file
capability or verification of dynamic libraries, interpreters, or descendants.
Implementations§
Source§impl AdmittedTool
impl AdmittedTool
Sourcepub fn admit(
spec: &ToolSpec<'_>,
context: &ExecutionContext<'_>,
limits: OutputLimits,
) -> Result<Self, ToolError>
pub fn admit( spec: &ToolSpec<'_>, context: &ExecutionContext<'_>, limits: OutputLimits, ) -> Result<Self, ToolError>
Admit exact executable bytes before invoking the selected version command.
§Errors
Rejects invalid inputs, non-executable files, digest/size mismatch, process failures, non-UTF-8 stdout, and a different successful version identity.
Examples found in repository?
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 use ic_host_tools::{
6 candid::extract,
7 tool::{AdmittedTool, ExecutionContext, OutputLimits, ToolSpec},
8 };
9 use std::{
10 ffi::OsString,
11 io::{self, Write as _},
12 path::PathBuf,
13 time::Duration,
14 };
15
16 let mut args = std::env::args_os().skip(1);
17 let mut required = || {
18 args.next().ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "usage: extract_candid TOOL SHA256 VERSION WASM WORKDIR MAX_TOOL_BYTES MAX_WASM_BYTES MAX_STDOUT_BYTES MAX_STDERR_BYTES TIMEOUT_MS"))
19 };
20 let executable = PathBuf::from(required()?);
21 let digest = required()?
22 .into_string()
23 .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "digest must be UTF-8"))?
24 .parse()?;
25 let version = required()?
26 .into_string()
27 .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "version must be UTF-8"))?;
28 let source = PathBuf::from(required()?);
29 let directory = PathBuf::from(required()?);
30 let mut number = || -> Result<u64, Box<dyn std::error::Error>> {
31 Ok(required()?
32 .to_str()
33 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "limits must be UTF-8"))?
34 .parse()?)
35 };
36 let executable_bytes = number()?;
37 let source_bytes = number()?;
38 let limits = OutputLimits {
39 stdout_bytes: usize::try_from(number()?)?,
40 stderr_bytes: usize::try_from(number()?)?,
41 timeout: Duration::from_millis(number()?),
42 };
43 if args.next().is_some() {
44 return Err(io::Error::new(io::ErrorKind::InvalidInput, "unexpected argument").into());
45 }
46 // This example selects a credential-free empty environment explicitly.
47 let context = ExecutionContext {
48 current_dir: &directory,
49 environment: &[],
50 };
51 let tool = AdmittedTool::admit(
52 &ToolSpec {
53 executable: &executable,
54 sha256: digest,
55 executable_bytes,
56 version_arguments: &[OsString::from("--version")],
57 version_identity: &version,
58 },
59 &context,
60 limits,
61 )?;
62 let candid = extract(&tool, &source, &context, source_bytes, limits)?;
63 io::stdout().lock().write_all(candid.text.as_bytes())?;
64 Ok(())
65}More examples
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 use ic_host_tools::{
6 provenance::{IgnoreSubmodules, StatusOptions, UntrackedFiles, capture_git},
7 tool::{AdmittedTool, ExecutionContext, OutputLimits, ToolSpec},
8 };
9 use std::{ffi::OsString, io, path::PathBuf, time::Duration};
10
11 let invalid = |message| io::Error::new(io::ErrorKind::InvalidInput, message);
12 let mut args = std::env::args_os().skip(1);
13 let mut required = || {
14 args.next().ok_or_else(|| invalid(
15 "usage: inspect_git TOOL SHA256 VERSION WORKDIR MAX_TOOL_BYTES MAX_STDOUT_BYTES MAX_STDERR_BYTES TIMEOUT_MS UNTRACKED SUBMODULES",
16 ))
17 };
18 let executable = PathBuf::from(required()?);
19 let digest = required()?
20 .into_string()
21 .map_err(|_| invalid("digest must be UTF-8"))?
22 .parse()?;
23 let version = required()?
24 .into_string()
25 .map_err(|_| invalid("version must be UTF-8"))?;
26 let directory = PathBuf::from(required()?);
27 let mut number = || -> Result<u64, Box<dyn std::error::Error>> {
28 Ok(required()?
29 .to_str()
30 .ok_or_else(|| invalid("limits must be UTF-8"))?
31 .parse()?)
32 };
33 let executable_bytes = number()?;
34 let limits = OutputLimits {
35 stdout_bytes: usize::try_from(number()?)?,
36 stderr_bytes: usize::try_from(number()?)?,
37 timeout: Duration::from_millis(number()?),
38 };
39 let untracked = match required()?.to_str() {
40 Some("no") => UntrackedFiles::No,
41 Some("normal") => UntrackedFiles::Normal,
42 Some("all") => UntrackedFiles::All,
43 _ => return Err(invalid("untracked must be no, normal or all").into()),
44 };
45 let ignore_submodules = match required()?.to_str() {
46 Some("none") => IgnoreSubmodules::None,
47 Some("untracked") => IgnoreSubmodules::Untracked,
48 Some("dirty") => IgnoreSubmodules::Dirty,
49 Some("all") => IgnoreSubmodules::All,
50 _ => return Err(invalid("submodules must be none, untracked, dirty or all").into()),
51 };
52 if args.next().is_some() {
53 return Err(invalid("unexpected argument").into());
54 }
55 // The example explicitly selects an empty environment; the library never
56 // chooses environment exclusions or tool/version pins for a consumer.
57 let context = ExecutionContext {
58 current_dir: &directory,
59 environment: &[],
60 };
61 let git = AdmittedTool::admit(
62 &ToolSpec {
63 executable: &executable,
64 sha256: digest,
65 executable_bytes,
66 version_arguments: &[OsString::from("--version")],
67 version_identity: &version,
68 },
69 &context,
70 limits,
71 )?;
72 let observed = capture_git(
73 &git,
74 &context,
75 StatusOptions {
76 untracked,
77 ignore_submodules,
78 },
79 limits,
80 )?;
81 println!("revision={}", observed.revision);
82 println!("tree={}", observed.tree);
83 println!("dirty={}", observed.is_dirty());
84 println!("status_bytes={}", observed.status_identity.bytes);
85 println!("status_sha256={}", observed.status_identity.sha256);
86 Ok(())
87}Sourcepub const fn identity(&self) -> ArtifactIdentity
pub const fn identity(&self) -> ArtifactIdentity
Admitted raw executable identity.
Sourcepub fn version_identity(&self) -> &str
pub fn version_identity(&self) -> &str
Successfully observed, trimmed version identity.
Sourcepub fn run(
&self,
arguments: &[OsString],
context: &ExecutionContext<'_>,
limits: OutputLimits,
) -> Result<ExecutionEvidence, ToolError>
pub fn run( &self, arguments: &[OsString], context: &ExecutionContext<'_>, limits: OutputLimits, ) -> Result<ExecutionEvidence, ToolError>
Run once with a cleared, explicitly supplied environment and null stdin.
Stdout/stderr are drained fairly through nonblocking pipes without reader threads. On overflow/deadline the direct child is killed and reaped; pipe handles are closed without waiting for descendants to close their copies. Descendant processes remain caller-owned. This must not be interpreted as a rollback or safe automatic retry of a command with external effects.
§Errors
Returns invalid-input or identity failures before execution, or an execution failure retaining bounded prefixes and cleanup outcomes.