Skip to main content

cageforge_policy_compose/
error.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Typed failures raised while composing portable policy boundaries.
4
5use std::path::PathBuf;
6
7use cageforge_command::{CommandError, EnvironmentBase};
8use cageforge_policy::PolicyError;
9use thiserror::Error;
10
11/// The policy boundary involved in a composition error.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum CompositionBoundary {
14    /// The filesystem policy boundary.
15    Filesystem,
16    /// The network policy boundary.
17    Network,
18}
19
20/// An error raised while narrowing a requested policy.
21#[derive(Debug, Error, Clone, PartialEq, Eq)]
22pub enum CompositionError {
23    /// The requested policy failed its own invariant checks.
24    #[error("requested sandbox policy is invalid: {source}")]
25    InvalidRequestedPolicy {
26        /// The validation error from the policy crate.
27        #[source]
28        source: PolicyError,
29    },
30    /// The ceiling failed its own invariant checks.
31    #[error("policy ceiling is invalid: {source}")]
32    InvalidCeiling {
33        /// The validation error from the policy crate.
34        #[source]
35        source: PolicyError,
36    },
37    /// A path could not be used as a workspace-root declaration.
38    #[error("invalid workspace root {path:?}: {reason}")]
39    InvalidWorkspaceRoot {
40        /// The invalid root declaration.
41        path: PathBuf,
42        /// Why the declaration is unsafe or ambiguous.
43        reason: &'static str,
44    },
45    /// A requested root is outside the roots permitted by the ceiling.
46    #[error("requested workspace root {path:?} is outside the policy ceiling")]
47    WorkspaceRootNotGranted {
48        /// The root that could not be granted.
49        path: PathBuf,
50    },
51    /// The effective runtime path context could not be rebuilt safely.
52    #[error("effective path context is invalid: {source}")]
53    InvalidPathContext {
54        /// The underlying path-context validation error.
55        #[source]
56        source: PolicyError,
57    },
58    /// A filesystem context came from a different effective composition.
59    #[error("effective path context belongs to a different composed sandbox")]
60    PathContextMismatch,
61    /// The caller supplied an environment base broader than the effective
62    /// composition allows.
63    #[error("environment input base {supplied:?} is broader than effective base {required:?}")]
64    EnvironmentBaseTooPermissive {
65        /// The narrowest base accepted by the effective policy.
66        required: EnvironmentBase,
67        /// The base supplied by the backend caller.
68        supplied: EnvironmentBase,
69    },
70    /// Applying a validated environment transformation failed.
71    #[error("environment transformation failed: {source}")]
72    EnvironmentApplication {
73        /// The command-layer error raised while applying the transformation.
74        #[source]
75        source: CommandError,
76    },
77    /// One side delegates enforcement while the other expects local enforcement.
78    #[error("{boundary} enforcement ownership cannot be composed safely")]
79    EnforcementOwnershipConflict {
80        /// The boundary with incompatible ownership.
81        boundary: CompositionBoundary,
82    },
83    /// External enforcement was requested without one shared owner proof.
84    #[error("{boundary} external enforcement requires one shared owner proof")]
85    ExternalOwnerMismatch {
86        /// The boundary with unrelated or missing external owners.
87        boundary: CompositionBoundary,
88    },
89    /// An external owner proof was attached to a locally enforced boundary.
90    #[error("{boundary} external owner proof is only valid for external enforcement")]
91    UnexpectedExternalOwner {
92        /// The boundary with an unexpected owner proof.
93        boundary: CompositionBoundary,
94    },
95    /// Evaluating one component policy failed after composition.
96    #[error("{boundary} policy evaluation failed: {source}")]
97    PolicyEvaluation {
98        /// The boundary being evaluated.
99        boundary: CompositionBoundary,
100        /// The underlying policy error.
101        #[source]
102        source: PolicyError,
103    },
104}
105
106impl std::fmt::Display for CompositionBoundary {
107    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108        match self {
109            Self::Filesystem => formatter.write_str("filesystem"),
110            Self::Network => formatter.write_str("network"),
111        }
112    }
113}