use serde::Deserialize;
#[expect(clippy::struct_excessive_bools, reason = "per-check skip flags")]
#[derive(Clone, Debug, Default, Deserialize, serde::Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct SkipPipelineChecks {
pub conditional_security: bool,
pub conflicting_cluster_selectors: bool,
pub duplicate_load_balancers: bool,
pub duplicate_rewrite_filters: bool,
pub duplicate_routers: bool,
pub lb_without_router: bool,
pub misaligned_clusters: bool,
pub unreachable_filters: bool,
}
impl SkipPipelineChecks {
pub fn all() -> Self {
Self {
conditional_security: true,
conflicting_cluster_selectors: true,
duplicate_load_balancers: true,
duplicate_rewrite_filters: true,
duplicate_routers: true,
lb_without_router: true,
misaligned_clusters: true,
unreachable_filters: true,
}
}
pub fn any(&self) -> bool {
self.conditional_security
|| self.conflicting_cluster_selectors
|| self.duplicate_load_balancers
|| self.duplicate_rewrite_filters
|| self.duplicate_routers
|| self.lb_without_router
|| self.misaligned_clusters
|| self.unreachable_filters
}
}
#[expect(clippy::struct_excessive_bools, reason = "security override flags")]
#[derive(Clone, Debug, Default, Deserialize, serde::Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct InsecureOptions {
pub allow_open_security_filters: bool,
pub allow_private_endpoints: bool,
pub allow_private_health_checks: bool,
pub allow_private_upstreams: bool,
pub allow_public_admin: bool,
pub allow_root: bool,
pub allow_tls_no_verify: bool,
pub allow_tls_without_sni: bool,
pub allow_unbounded_body: bool,
pub csrf_log_only: bool,
pub skip_pipeline_checks: SkipPipelineChecks,
pub skip_pipeline_validation: bool,
}
impl InsecureOptions {
pub fn effective_pipeline_checks(&self) -> SkipPipelineChecks {
if self.skip_pipeline_validation {
SkipPipelineChecks::all()
} else {
self.skip_pipeline_checks.clone()
}
}
}
#[cfg(test)]
#[expect(clippy::allow_attributes, reason = "blanket test suppressions")]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::indexing_slicing,
clippy::needless_raw_strings,
clippy::needless_raw_string_hashes,
clippy::too_many_lines,
reason = "tests use unwrap/expect/indexing/raw strings for brevity"
)]
mod tests {
use super::*;
#[test]
fn all_flags_default_to_false() {
let opts = InsecureOptions::default();
assert!(
!opts.allow_open_security_filters,
"allow_open_security_filters should default to false"
);
assert!(
!opts.allow_private_endpoints,
"allow_private_endpoints should default to false"
);
assert!(
!opts.allow_private_health_checks,
"allow_private_health_checks should default to false"
);
assert!(
!opts.allow_private_upstreams,
"allow_private_upstreams should default to false"
);
assert!(!opts.allow_public_admin, "allow_public_admin should default to false");
assert!(!opts.allow_root, "allow_root should default to false");
assert!(!opts.allow_tls_no_verify, "allow_tls_no_verify should default to false");
assert!(
!opts.allow_tls_without_sni,
"allow_tls_without_sni should default to false"
);
assert!(
!opts.allow_unbounded_body,
"allow_unbounded_body should default to false"
);
assert!(!opts.csrf_log_only, "csrf_log_only should default to false");
assert!(
!opts.skip_pipeline_validation,
"skip_pipeline_validation should default to false"
);
assert!(
!opts.skip_pipeline_checks.any(),
"skip_pipeline_checks should all default to false"
);
}
#[test]
fn deserializes_partial_overrides() {
let yaml = "allow_root: true\nskip_pipeline_validation: true\n";
let opts: InsecureOptions = serde_yaml::from_str(yaml).unwrap();
assert!(opts.allow_root, "allow_root should be true");
assert!(opts.skip_pipeline_validation, "skip_pipeline_validation should be true");
assert!(!opts.allow_public_admin, "allow_public_admin should still be false");
}
#[test]
fn deserializes_empty_to_defaults() {
let opts: InsecureOptions = serde_yaml::from_str("{}").unwrap();
assert!(!opts.allow_root, "empty YAML should produce defaults");
}
#[test]
fn skip_pipeline_checks_all_sets_every_flag() {
let checks = SkipPipelineChecks::all();
assert!(checks.conditional_security, "conditional_security should be true");
assert!(
checks.conflicting_cluster_selectors,
"conflicting_cluster_selectors should be true"
);
assert!(
checks.duplicate_load_balancers,
"duplicate_load_balancers should be true"
);
assert!(
checks.duplicate_rewrite_filters,
"duplicate_rewrite_filters should be true"
);
assert!(checks.duplicate_routers, "duplicate_routers should be true");
assert!(checks.lb_without_router, "lb_without_router should be true");
assert!(checks.misaligned_clusters, "misaligned_clusters should be true");
assert!(checks.unreachable_filters, "unreachable_filters should be true");
}
#[test]
fn skip_pipeline_checks_any_detects_single_flag() {
let mut checks = SkipPipelineChecks::default();
assert!(!checks.any(), "default checks should have no flags set");
checks.duplicate_routers = true;
assert!(checks.any(), "any() should detect single flag");
}
#[test]
fn effective_pipeline_checks_blanket_overrides_granular() {
let opts = InsecureOptions {
skip_pipeline_validation: true,
..Default::default()
};
let checks = opts.effective_pipeline_checks();
assert!(checks.lb_without_router, "blanket flag should set all checks");
assert!(checks.conditional_security, "blanket flag should set all checks");
assert!(checks.misaligned_clusters, "blanket flag should set all checks");
}
#[test]
fn effective_pipeline_checks_uses_granular_when_blanket_off() {
let opts = InsecureOptions {
skip_pipeline_checks: SkipPipelineChecks {
duplicate_routers: true,
..Default::default()
},
..Default::default()
};
let checks = opts.effective_pipeline_checks();
assert!(checks.duplicate_routers, "granular flag should be preserved");
assert!(!checks.lb_without_router, "other flags should remain false");
}
#[test]
fn deserializes_granular_pipeline_checks() {
let yaml = "skip_pipeline_checks:\n duplicate_routers: true\n misaligned_clusters: true\n";
let opts: InsecureOptions = serde_yaml::from_str(yaml).unwrap();
assert!(
opts.skip_pipeline_checks.duplicate_routers,
"duplicate_routers should be true"
);
assert!(
opts.skip_pipeline_checks.misaligned_clusters,
"misaligned_clusters should be true"
);
assert!(
!opts.skip_pipeline_checks.lb_without_router,
"lb_without_router should remain false"
);
assert!(!opts.skip_pipeline_validation, "blanket flag should remain false");
}
#[test]
fn rejects_unknown_skip_pipeline_checks_field() {
let yaml = "skip_pipeline_checks:\n nonexistent_check: true\n";
let err = serde_yaml::from_str::<InsecureOptions>(yaml).unwrap_err();
assert!(
err.to_string().contains("nonexistent_check"),
"unknown skip_pipeline_checks field should be rejected: {err}"
);
}
}