Skip to main content

praxis_core/config/
insecure_options.rs

1// SPDX-License-Identifier: LGPL-3.0-only
2// Copyright (c) 2024 Shane Utt
3
4//! Consolidated security override flags.
5//!
6//! All options default to `false` (secure by default). Each flag
7//! demotes one specific security check from an error to a warning.
8
9use serde::Deserialize;
10
11// -----------------------------------------------------------------------------
12// InsecureOptions
13// -----------------------------------------------------------------------------
14
15/// Consolidated security overrides for Praxis.
16///
17/// Every field defaults to `false`. Setting a flag to `true`
18/// demotes the corresponding security check from an error to a warning.
19///
20/// Only intended for use in development and testing.
21///
22/// ```
23/// use praxis_core::config::InsecureOptions;
24///
25/// let opts = InsecureOptions::default();
26/// assert!(!opts.allow_root);
27/// assert!(!opts.allow_public_admin);
28/// assert!(!opts.allow_unbounded_body);
29/// assert!(!opts.skip_pipeline_validation);
30/// assert!(!opts.allow_tls_without_sni);
31/// assert!(!opts.allow_private_health_checks);
32/// ```
33///
34/// ```
35/// use praxis_core::config::InsecureOptions;
36///
37/// let opts: InsecureOptions =
38///     serde_yaml::from_str("allow_root: true\nallow_public_admin: true\n").unwrap();
39/// assert!(opts.allow_root);
40/// assert!(opts.allow_public_admin);
41/// assert!(!opts.allow_unbounded_body);
42/// ```
43#[allow(clippy::struct_excessive_bools, reason = "security override flags")]
44#[derive(Debug, Clone, Default, Deserialize)]
45#[serde(default)]
46pub struct InsecureOptions {
47    /// Allow running as root (UID 0).
48    pub allow_root: bool,
49
50    /// Allow admin endpoint on 0.0.0.0 / [::].
51    pub allow_public_admin: bool,
52
53    /// Allow stream-buffered body mode with no size limit.
54    pub allow_unbounded_body: bool,
55
56    /// Skip pipeline ordering validation.
57    pub skip_pipeline_validation: bool,
58
59    /// Allow TLS without SNI hostname verification.
60    pub allow_tls_without_sni: bool,
61
62    /// Allow health checks to loopback/metadata addresses.
63    pub allow_private_health_checks: bool,
64}
65
66// -----------------------------------------------------------------------------
67// Tests
68// -----------------------------------------------------------------------------
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn all_flags_default_to_false() {
76        let opts = InsecureOptions::default();
77        assert!(!opts.allow_root, "allow_root should default to false");
78        assert!(!opts.allow_public_admin, "allow_public_admin should default to false");
79        assert!(
80            !opts.allow_unbounded_body,
81            "allow_unbounded_body should default to false"
82        );
83        assert!(
84            !opts.skip_pipeline_validation,
85            "skip_pipeline_validation should default to false"
86        );
87        assert!(
88            !opts.allow_tls_without_sni,
89            "allow_tls_without_sni should default to false"
90        );
91        assert!(
92            !opts.allow_private_health_checks,
93            "allow_private_health_checks should default to false"
94        );
95    }
96
97    #[test]
98    fn deserializes_partial_overrides() {
99        let yaml = "allow_root: true\nskip_pipeline_validation: true\n";
100        let opts: InsecureOptions = serde_yaml::from_str(yaml).unwrap();
101        assert!(opts.allow_root, "allow_root should be true");
102        assert!(opts.skip_pipeline_validation, "skip_pipeline_validation should be true");
103        assert!(!opts.allow_public_admin, "allow_public_admin should still be false");
104    }
105
106    #[test]
107    fn deserializes_empty_to_defaults() {
108        let opts: InsecureOptions = serde_yaml::from_str("{}").unwrap();
109        assert!(!opts.allow_root, "empty YAML should produce defaults");
110    }
111}