Skip to content

Commit e7b49a0

Browse files
committed
Only allow canonical names when deserializing Capability
Signed-off-by: Stepan Koltsov <stepan.koltsov@gmail.com>
1 parent 3047d16 commit e7b49a0

1 file changed

Lines changed: 28 additions & 87 deletions

File tree

src/runtime/capability.rs

Lines changed: 28 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
1-
use serde::{
2-
de::{Deserializer, Error},
3-
Deserialize, Serialize,
4-
};
1+
use serde::{Deserialize, Serialize};
52
use std::collections::HashSet;
63

74
use strum_macros::{Display, EnumString};
85

96
/// Capabilities is a unique set of Capability values.
107
pub type Capabilities = HashSet<Capability>;
118

12-
#[derive(Clone, Copy, Debug, EnumString, Eq, Display, Hash, PartialEq, Serialize)]
9+
#[derive(Clone, Copy, Debug, Deserialize, EnumString, Eq, Display, Hash, PartialEq, Serialize)]
1310
/// All available capabilities.
1411
///
1512
/// For the purpose of performing permission checks, traditional UNIX
@@ -519,75 +516,6 @@ pub enum Capability {
519516
WakeAlarm,
520517
}
521518

522-
impl<'de> Deserialize<'de> for Capability {
523-
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
524-
where
525-
D: Deserializer<'de>,
526-
{
527-
let input = String::deserialize(deserializer)?;
528-
let upper = input.to_uppercase();
529-
530-
// Extract the capability name from various input formats.
531-
//
532-
// This function strips all "CAP_" prefixes to normalize capability names.
533-
// This ensures correct adaptation regardless of whether users specify
534-
// CAP_XXX or XXX directly at the upper layer(Specially k8s), making the
535-
// API more flexible and user-friendly.
536-
// Examples: "CAP_SYS_ADMIN", "SYS_ADMIN"
537-
//
538-
let mut clean_cap = upper.as_str();
539-
while let Some(stripped) = clean_cap.strip_prefix("CAP_") {
540-
clean_cap = stripped;
541-
}
542-
match clean_cap {
543-
"AUDIT_CONTROL" => Ok(Self::AuditControl),
544-
"AUDIT_READ" => Ok(Self::AuditRead),
545-
"AUDIT_WRITE" => Ok(Self::AuditWrite),
546-
"BLOCK_SUSPEND" => Ok(Self::BlockSuspend),
547-
"BPF" => Ok(Self::Bpf),
548-
"CHECKPOINT_RESTORE" => Ok(Self::CheckpointRestore),
549-
"CHOWN" => Ok(Self::Chown),
550-
"DAC_OVERRIDE" => Ok(Self::DacOverride),
551-
"DAC_READ_SEARCH" => Ok(Self::DacReadSearch),
552-
"FOWNER" => Ok(Self::Fowner),
553-
"FSETID" => Ok(Self::Fsetid),
554-
"IPC_LOCK" => Ok(Self::IpcLock),
555-
"IPC_OWNER" => Ok(Self::IpcOwner),
556-
"KILL" => Ok(Self::Kill),
557-
"LEASE" => Ok(Self::Lease),
558-
"LINUX_IMMUTABLE" => Ok(Self::LinuxImmutable),
559-
"MAC_ADMIN" => Ok(Self::MacAdmin),
560-
"MAC_OVERRIDE" => Ok(Self::MacOverride),
561-
"MKNOD" => Ok(Self::Mknod),
562-
"NET_ADMIN" => Ok(Self::NetAdmin),
563-
"NET_BIND_SERVICE" => Ok(Self::NetBindService),
564-
"NET_BROADCAST" => Ok(Self::NetBroadcast),
565-
"NET_RAW" => Ok(Self::NetRaw),
566-
"PERFMON" => Ok(Self::Perfmon),
567-
"SETGID" => Ok(Self::Setgid),
568-
"SETFCAP" => Ok(Self::Setfcap),
569-
"SETPCAP" => Ok(Self::Setpcap),
570-
"SETUID" => Ok(Self::Setuid),
571-
"SYS_ADMIN" => Ok(Self::SysAdmin),
572-
"SYS_BOOT" => Ok(Self::SysBoot),
573-
"SYS_CHROOT" => Ok(Self::SysChroot),
574-
"SYS_MODULE" => Ok(Self::SysModule),
575-
"SYS_NICE" => Ok(Self::SysNice),
576-
"SYS_PACCT" => Ok(Self::SysPacct),
577-
"SYS_PTRACE" => Ok(Self::SysPtrace),
578-
"SYS_RAWIO" => Ok(Self::SysRawio),
579-
"SYS_RESOURCE" => Ok(Self::SysResource),
580-
"SYS_TIME" => Ok(Self::SysTime),
581-
"SYS_TTY_CONFIG" => Ok(Self::SysTtyConfig),
582-
"SYSLOG" => Ok(Self::Syslog),
583-
"WAKE_ALARM" => Ok(Self::WakeAlarm),
584-
other => Err(Error::custom(format!(
585-
"no variant for {input} (converted to {other})",
586-
))),
587-
}
588-
}
589-
}
590-
591519
#[cfg(test)]
592520
mod tests {
593521
use super::*;
@@ -602,21 +530,34 @@ mod tests {
602530

603531
#[test]
604532
fn deserialize() -> Result<()> {
605-
for case in &["SYSLOG", "CAP_SYSLOG", "cap_SYSLOG", "sySloG"] {
606-
let res: Capability = serde_json::from_str(&format!("\"{case}\""))?;
607-
assert_eq!(Capability::Syslog, res);
608-
}
533+
let res: Capability = serde_json::from_str("\"CAP_SYSLOG\"")?;
534+
assert_eq!(Capability::Syslog, res);
609535
Ok(())
610536
}
611537

538+
#[test]
539+
fn deserialize_rejects_non_canonical() {
540+
for case in &[
541+
"SYSLOG",
542+
"cap_SYSLOG",
543+
"sySloG",
544+
"SYS_ADMIN",
545+
"cap_sys_admin",
546+
] {
547+
let res: std::result::Result<Capability, _> =
548+
serde_json::from_str(&format!("\"{case}\""));
549+
assert!(res.is_err(), "expected {case} to be rejected");
550+
}
551+
}
552+
612553
#[test]
613554
fn capabilities() -> Result<()> {
614555
let res: Capabilities = serde_json::from_str(
615556
r#"[
616-
"syslog",
617-
"SYSLOG",
618-
"chown",
619-
"cap_chown"
557+
"CAP_SYSLOG",
558+
"CAP_SYSLOG",
559+
"CAP_CHOWN",
560+
"CAP_CHOWN"
620561
]"#,
621562
)?;
622563
assert_eq!(res.len(), 2);
@@ -682,11 +623,11 @@ mod tests {
682623
}
683624

684625
#[test]
685-
fn deserialize_one_more_cap_prefix() -> Result<()> {
686-
for case in &["SYS_ADMIN", "CAP_CAP_SYS_ADMIN", "cap_CAP_cap_SYS_ADMIN"] {
687-
let res: Capability = serde_json::from_str(&format!("\"{case}\""))?;
688-
assert_eq!(Capability::SysAdmin, res);
626+
fn deserialize_rejects_extra_cap_prefix() {
627+
for case in &["CAP_CAP_SYS_ADMIN", "cap_CAP_cap_SYS_ADMIN"] {
628+
let res: std::result::Result<Capability, _> =
629+
serde_json::from_str(&format!("\"{case}\""));
630+
assert!(res.is_err(), "expected {case} to be rejected");
689631
}
690-
Ok(())
691632
}
692633
}

0 commit comments

Comments
 (0)