Skip to main content

cageforge_policy/
error.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Typed failures raised by policy construction, normalization, and queries.
4//!
5//! [`crate::PolicyError`] is the boundary between invalid portable policy data
6//! and the valid values consumed by composition or a native backend.
7
8use crate::AccessMode;
9use std::net::SocketAddr;
10use std::path::PathBuf;
11use thiserror::Error;
12
13/// Errors returned when a policy value cannot represent a safe request.
14#[derive(Debug, Error, Clone, PartialEq, Eq)]
15pub enum PolicyError {
16    /// A path argument was empty.
17    #[error("path cannot be empty")]
18    EmptyPath,
19    /// A path that must be absolute was relative.
20    #[error("path must be absolute: {}", path.display())]
21    ExpectedAbsolute {
22        /// The path that was supplied by the caller.
23        path: PathBuf,
24    },
25    /// A path that must be workspace-relative was absolute.
26    #[error("path must be workspace-relative: {}", path.display())]
27    ExpectedRelative {
28        /// The path that was supplied by the caller.
29        path: PathBuf,
30    },
31    /// A path contained a NUL character that an operating-system backend cannot use.
32    #[error("path must not contain a NUL character: {}", path.display())]
33    PathContainsNul {
34        /// The path that was supplied by the caller.
35        path: PathBuf,
36    },
37    /// A workspace-relative path attempts to escape its root.
38    #[error(
39        "workspace-relative path cannot contain parent traversal: {}",
40        path.display()
41    )]
42    ParentTraversal {
43        /// The workspace-relative path that attempted to escape its root.
44        path: PathBuf,
45    },
46    /// A domain pattern is empty or malformed.
47    #[error("invalid domain pattern: {pattern}")]
48    InvalidDomainPattern {
49        /// The domain pattern that failed validation.
50        pattern: String,
51    },
52    /// An IP-literal target claimed a resolved address for a different IP.
53    #[error("resolved address {address} does not match IP literal {literal}")]
54    ResolvedAddressMismatch {
55        /// The normalized literal supplied as the target host.
56        literal: String,
57        /// The inconsistent address in the resolution snapshot.
58        address: SocketAddr,
59    },
60    /// A filesystem glob is empty or malformed.
61    #[error("invalid glob pattern {pattern:?}: {reason}")]
62    InvalidGlobPattern {
63        /// The glob pattern that failed validation.
64        pattern: String,
65        /// The reason the pattern is invalid.
66        reason: String,
67    },
68    /// A glob requested an access mode that is not portable across backends.
69    #[error("filesystem glob rules support only deny access; requested {access:?}")]
70    UnsupportedGlobAccess {
71        /// The access mode requested for the glob.
72        access: AccessMode,
73    },
74    /// A protected relative path is empty or unsafe.
75    #[error("invalid protected relative path {path:?}: {reason}")]
76    InvalidProtectedPath {
77        /// The protected path that failed validation.
78        path: PathBuf,
79        /// The reason the path is invalid.
80        reason: String,
81    },
82    /// A path resolution context contains an invalid value.
83    #[error("{message}")]
84    InvalidContext {
85        /// A human-readable explanation of the invalid context.
86        message: String,
87    },
88    /// A policy rule is internally inconsistent.
89    #[error("{message}")]
90    InvalidRule {
91        /// A human-readable explanation of the inconsistent rule.
92        message: String,
93    },
94}