/* CarterPerez-dev | 2026 capabilities.go CapabilitySet type for parsing and inspecting Linux process capability bitmasks Wraps the five Linux capability bitmasks (effective, permitted, inheritable, bounding, ambient) with methods for testing individual capabilities, listing active sets, and diffing against Docker's default 14-capability set to find additions or drops. Key exports: CapabilitySet - holds all five capability bitmasks with bit-level methods HasCapability, HasDangerousCapabilities, HasCriticalCapabilities - checks GetAddedCapabilities, GetDroppedDefaultCapabilities, HasOnlyDefaultCapabilities ParseCapabilityMask, AllCapabilityNames - parsing and enumeration utilities Connects to: rules/capabilities.go - IsDangerousCapability, IsCriticalCapability, GetCapabilitySeverity finding.go - uses Severity for GetCapabilitiesBySeverity threshold checks proc.go - CapabilitySet embedded in ProcessInfo.Capabilities security.go - CapabilitySet embedded in SecurityProfile.Capabilities */ package proc import ( "fmt" "github.com/CarterPerez-dev/docksec/internal/finding" "github.com/CarterPerez-dev/docksec/internal/rules" ) type CapabilitySet struct { Effective uint64 Permitted uint64 Inheritable uint64 Bounding uint64 Ambient uint64 } var capabilityBits = map[int]string{ 0: "CAP_CHOWN", 1: "CAP_DAC_OVERRIDE", 2: "CAP_DAC_READ_SEARCH", 3: "CAP_FOWNER", 4: "CAP_FSETID", 5: "CAP_KILL", 6: "CAP_SETGID", 7: "CAP_SETUID", 8: "CAP_SETPCAP", 9: "CAP_LINUX_IMMUTABLE", 10: "CAP_NET_BIND_SERVICE", 11: "CAP_NET_BROADCAST", 12: "CAP_NET_ADMIN", 13: "CAP_NET_RAW", 14: "CAP_IPC_LOCK", 15: "CAP_IPC_OWNER", 16: "CAP_SYS_MODULE", 17: "CAP_SYS_RAWIO", 18: "CAP_SYS_CHROOT", 19: "CAP_SYS_PTRACE", 20: "CAP_SYS_PACCT", 21: "CAP_SYS_ADMIN", 22: "CAP_SYS_BOOT", 23: "CAP_SYS_NICE", 24: "CAP_SYS_RESOURCE", 25: "CAP_SYS_TIME", 26: "CAP_SYS_TTY_CONFIG", 27: "CAP_MKNOD", 28: "CAP_LEASE", 29: "CAP_AUDIT_WRITE", 30: "CAP_AUDIT_CONTROL", 31: "CAP_SETFCAP", 32: "CAP_MAC_OVERRIDE", 33: "CAP_MAC_ADMIN", 34: "CAP_SYSLOG", 35: "CAP_WAKE_ALARM", 36: "CAP_BLOCK_SUSPEND", 37: "CAP_AUDIT_READ", 38: "CAP_PERFMON", 39: "CAP_BPF", 40: "CAP_CHECKPOINT_RESTORE", } var capabilityNames = func() map[string]int { m := make(map[string]int, len(capabilityBits)) for bit, name := range capabilityBits { m[name] = bit } return m }() func (c *CapabilitySet) HasCapability(name string) bool { bit, ok := capabilityNames[name] if !ok { return false } return (c.Effective & (1 << bit)) != 0 } func (c *CapabilitySet) HasPermitted(name string) bool { bit, ok := capabilityNames[name] if !ok { return false } return (c.Permitted & (1 << bit)) != 0 } func (c *CapabilitySet) HasBounding(name string) bool { bit, ok := capabilityNames[name] if !ok { return false } return (c.Bounding & (1 << bit)) != 0 } func (c *CapabilitySet) HasAmbient(name string) bool { bit, ok := capabilityNames[name] if !ok { return false } return (c.Ambient & (1 << bit)) != 0 } func (c *CapabilitySet) ListEffective() []string { return c.listCaps(c.Effective) } func (c *CapabilitySet) ListPermitted() []string { return c.listCaps(c.Permitted) } func (c *CapabilitySet) ListBounding() []string { return c.listCaps(c.Bounding) } func (c *CapabilitySet) ListAmbient() []string { return c.listCaps(c.Ambient) } func (c *CapabilitySet) ListInheritable() []string { return c.listCaps(c.Inheritable) } func (c *CapabilitySet) listCaps(mask uint64) []string { var caps []string for bit, name := range capabilityBits { if (mask & (1 << bit)) != 0 { caps = append(caps, name) } } return caps } func (c *CapabilitySet) IsFullyPrivileged() bool { return c.Effective == 0x1ffffffffff || c.Effective == 0xffffffffffffffff } func (c *CapabilitySet) HasDangerousCapabilities() bool { for _, cap := range c.ListEffective() { if rules.IsDangerousCapability(cap) { return true } } return false } func (c *CapabilitySet) HasCriticalCapabilities() bool { for _, cap := range c.ListEffective() { if rules.IsCriticalCapability(cap) { return true } } return false } func (c *CapabilitySet) GetDangerousCapabilities() []string { var dangerous []string for _, cap := range c.ListEffective() { if rules.IsDangerousCapability(cap) { dangerous = append(dangerous, cap) } } return dangerous } func (c *CapabilitySet) GetCriticalCapabilities() []string { var critical []string for _, cap := range c.ListEffective() { if rules.IsCriticalCapability(cap) { critical = append(critical, cap) } } return critical } func (c *CapabilitySet) GetCapabilitiesBySeverity( minSeverity finding.Severity, ) []string { var result []string for _, cap := range c.ListEffective() { severity := rules.GetCapabilitySeverity(cap) if severity >= minSeverity { result = append(result, cap) } } return result } func (c *CapabilitySet) EffectiveCount() int { return countBits(c.Effective) } func (c *CapabilitySet) PermittedCount() int { return countBits(c.Permitted) } func (c *CapabilitySet) BoundingCount() int { return countBits(c.Bounding) } func countBits(n uint64) int { count := 0 for n != 0 { count += int(n & 1) n >>= 1 } return count } func ParseCapabilityMask(hex string) (uint64, error) { var mask uint64 _, err := fmt.Sscanf(hex, "%x", &mask) return mask, err } func CapabilityNameToBit(name string) (int, bool) { bit, ok := capabilityNames[name] return bit, ok } func CapabilityBitToName(bit int) (string, bool) { name, ok := capabilityBits[bit] return name, ok } func AllCapabilityNames() []string { names := make([]string, 0, len(capabilityBits)) for i := 0; i <= 40; i++ { if name, ok := capabilityBits[i]; ok { names = append(names, name) } } return names } var defaultDockerCaps = map[string]struct{}{ "CAP_CHOWN": {}, "CAP_DAC_OVERRIDE": {}, "CAP_FSETID": {}, "CAP_FOWNER": {}, "CAP_MKNOD": {}, "CAP_NET_RAW": {}, "CAP_SETGID": {}, "CAP_SETUID": {}, "CAP_SETFCAP": {}, "CAP_SETPCAP": {}, "CAP_NET_BIND_SERVICE": {}, "CAP_SYS_CHROOT": {}, "CAP_KILL": {}, "CAP_AUDIT_WRITE": {}, } func (c *CapabilitySet) GetAddedCapabilities() []string { var added []string for _, cap := range c.ListEffective() { if _, isDefault := defaultDockerCaps[cap]; !isDefault { added = append(added, cap) } } return added } func (c *CapabilitySet) GetDroppedDefaultCapabilities() []string { var dropped []string for cap := range defaultDockerCaps { if !c.HasCapability(cap) { dropped = append(dropped, cap) } } return dropped } func (c *CapabilitySet) HasOnlyDefaultCapabilities() bool { for _, cap := range c.ListEffective() { if _, isDefault := defaultDockerCaps[cap]; !isDefault { return false } } return true }