Skip to main content

iris_trust/
error.rs

1//! What it means for a decoder to be untrusted.
2
3use iris_format::Digest;
4
5/// Why a decoder was not handed over.
6///
7/// Only one of these is a security event. The other three say the container does not carry a module
8/// this host can run, which is a bad file or an unfinished feature, and a host that wants to tell
9/// those apart in a log can match on the variant rather than read the text.
10#[derive(Clone, PartialEq, Eq, Debug, thiserror::Error)]
11#[non_exhaustive]
12pub enum Untrusted {
13    /// The container does not say which decoder reads it.
14    ///
15    /// A container without a decoder reference is not unreadable, it is just not self decoding, and
16    /// reading it is somebody else's problem rather than this crate's.
17    #[error("the container names no decoder, so nothing here knows how to read it")]
18    Missing,
19
20    /// The decoder lives somewhere else and this host was not told it may go and get it.
21    ///
22    /// This is the default and it fails closed. A decoder named by a URI means a dataset can cause
23    /// a fetch and then have the result executed, which may well be fine and is not something a
24    /// host should end up doing because nobody thought about it.
25    #[error(
26        "the decoder named {name} for this dataset lives outside the container, and this host runs \
27         embedded decoders only. A host that means to run this one calls \
28         Policy::with_external_decoders_resolved_by and supplies the bytes, which are hashed \
29         against the digest the container gives either way."
30    )]
31    External {
32        /// The decoder's name, as the container gives it.
33        name: String,
34    },
35
36    /// The decoder lives somewhere else, this host was told it may go and get it, and it came back
37    /// with nothing.
38    ///
39    /// A resolver that cannot find a decoder is an ordinary outcome rather than an attack: the
40    /// registry is down, or the module was never published, or this host has no copy. The digest
41    /// is here because it is what the next host to try should look for.
42    #[error("nothing resolved the decoder named {name}, whose module should hash to {digest}")]
43    Unresolved {
44        /// The decoder's name, as the container gives it.
45        name: String,
46        /// The digest the module has to hash to, whoever finds it.
47        digest: Digest,
48    },
49
50    /// The container puts the decoder somewhere this build has never heard of.
51    ///
52    /// A newer writer describing a location this build does not know about is a file from the
53    /// future, and the only safe reading of one is that this host cannot read it. Guessing which of
54    /// the locations it does know about was meant is how a host ends up running the wrong bytes.
55    #[error(
56        "the container puts the decoder named {name} somewhere this build has no idea how to \
57         reach, so nothing is run"
58    )]
59    Elsewhere {
60        /// The decoder's name, as the container gives it.
61        name: String,
62    },
63
64    /// The decoder reference names a section the file does not have.
65    ///
66    /// The footer parsed and then disagreed with itself. Nothing was substituted for the missing
67    /// module and nothing ever will be, because a module that is not there has no digest to check.
68    #[error(
69        "the container puts its decoder in section {section}, and there is no section {section} \
70         in the file"
71    )]
72    Lost {
73        /// The section id the decoder reference names.
74        section: u32,
75    },
76
77    /// The module in the container does not hash to what the container says it should.
78    ///
79    /// Both digests are in the message on purpose. The expected one identifies the decoder that was
80    /// meant to be here, which is the thing to go and look for, and the found one identifies what
81    /// actually arrived, which is the thing to keep for whoever asks how it got there.
82    #[error("the decoder module hashes to {found}, and the container says it should be {expected}")]
83    Digest {
84        /// What the container claims the module is.
85        expected: Digest,
86        /// What the bytes in the container actually hash to.
87        found: Digest,
88    },
89}