Skip to main content

AdmittedTool

Struct AdmittedTool 

Source
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

Source

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?
examples/extract_candid.rs (lines 51-61)
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
Hide additional examples
examples/inspect_git.rs (lines 61-71)
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}
Source

pub fn path(&self) -> &Path

Canonical absolute path selected during admission.

Source

pub const fn identity(&self) -> ArtifactIdentity

Admitted raw executable identity.

Source

pub fn version_identity(&self) -> &str

Successfully observed, trimmed version identity.

Source

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.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.