Watch
1
0
Fork
You've already forked RedFlag
0
RedFlag/agent/internal/system/process_detail.go
Fimeg 102ea23058 processes: read the capabilities the readme already claimed
CapEff was never collected — "sockets, capabilities, namespaces" meant the
collection limits, and a reader has no way to know that. The mask is read on
the list scan and expanded to names only on drill-down, because a fully
privileged process holds all 41 and nobody reads that on 300 rows.

Docker's documented default mask a80425fb decodes to exactly its fourteen.
2026-08-31 22:44:18 -04:00

154 lines
6.1 KiB
Go

package system
import "time"
// FullProcess represents a complete process snapshot with osquery-level detail.
// Fields mirror the osquery `processes` table schema plus related data for
// drill-down (open files, sockets, env, memory map, namespaces, pipes).
type FullProcess struct {
// Core identification
PID int `json:"pid"`
Name string `json:"name"`
Path string `json:"path,omitempty"` // /proc/[pid]/exe symlink target
Cmdline string `json:"cmdline"` // /proc/[pid]/cmdline (NUL→space)
Cwd string `json:"cwd,omitempty"` // /proc/[pid]/cwd symlink target
// State and identity
State string `json:"state"` // R/S/D/Z/T from /proc/[pid]/stat
UID uint32 `json:"uid"`
GID uint32 `json:"gid"`
EUID uint32 `json:"euid"`
EGID uint32 `json:"egid"`
User string `json:"user"` // resolved from /etc/passwd
Group string `json:"group"` // resolved from /etc/group
// Terminal
TTY int `json:"tty"`
TTYName string `json:"tty_name,omitempty"`
// Resource usage
CPUSecondsUser float64 `json:"cpu_seconds_user"` // utime / clock_ticks
CPUSecondsSystem float64 `json:"cpu_seconds_system"` // stime / clock_ticks
CPUPercent float64 `json:"cpu_percent"` // vs total CPU ticks
RSSBytes uint64 `json:"rss_bytes"` // VmRSS from /proc/[pid]/status
VMSBytes uint64 `json:"vms_bytes"` // VmSize from /proc/[pid]/status
MemPercent float64 `json:"mem_percent"`
Threads int `json:"threads"` // Threads field from /proc/[pid]/status
// Process metadata
Nice int `json:"nice"`
StartTimeSeconds uint64 `json:"start_time_seconds"` // starttime from /proc/[pid]/stat
ParentPID int `json:"parent_pid"`
ProcessGroupID int `json:"process_group_id"`
// On-disk check
OnDisk int `json:"on_disk"` // 1=yes, 0=no, -1=unknown (matches osquery)
// Elevation
ElevationStatus string `json:"elevation_status,omitempty"` // "elevated" if uid!=euid
// Ownership — cgroup, systemd unit, container. Inlined into the JSON.
ProcessOwner
// Effective capability mask from /proc/[pid]/status. Decoded into names on
// drill-down only — see Capabilities below.
CapabilitiesEffective string `json:"capabilities_effective,omitempty"`
// Disk I/O (from /proc/[pid]/io, may be empty for other users' processes)
DiskBytesRead uint64 `json:"disk_bytes_read,omitempty"`
DiskBytesWritten uint64 `json:"disk_bytes_written,omitempty"`
// Related data — populated only on drill-down (GetProcessDetail), not on list scan
OpenFiles []ProcessOpenFile `json:"open_files,omitempty"`
OpenSockets []ProcessOpenSocket `json:"open_sockets,omitempty"`
OpenPipes []ProcessOpenPipe `json:"open_pipes,omitempty"`
EnvironmentVars []string `json:"environment_vars,omitempty"` // keys only
MemoryMap []ProcessMemoryMap `json:"memory_map,omitempty"`
Namespaces []ProcessNamespace `json:"namespaces,omitempty"`
ListeningPorts []ProcessListeningPort `json:"listening_ports,omitempty"`
Capabilities []string `json:"capabilities,omitempty"` // effective set, decoded
}
// Related data types — stored as JSONB per relation in the database.
type ProcessOpenFile struct {
FD int `json:"fd"`
Path string `json:"path"`
Type string `json:"type,omitempty"` // file, socket, pipe, etc.
}
type ProcessOpenSocket struct {
FD int `json:"fd"`
Family string `json:"family"` // IPv4, IPv6, UNIX
Protocol string `json:"protocol"` // TCP, UDP
LocalAddr string `json:"local_addr"`
LocalPort int `json:"local_port"`
RemoteAddr string `json:"remote_addr"`
RemotePort int `json:"remote_port"`
State string `json:"state"`
Inode uint64 `json:"inode"`
Path string `json:"path,omitempty"` // for UNIX sockets
}
type ProcessOpenPipe struct {
FD int `json:"fd"`
Inode uint64 `json:"inode"`
Mode string `json:"mode,omitempty"` // r/w
Type string `json:"type,omitempty"` // named vs anonymous
}
type ProcessMemoryMap struct {
Start uint64 `json:"start"`
End uint64 `json:"end"`
Permissions string `json:"permissions"` // r/w/x/p
Offset uint64 `json:"offset"`
Device string `json:"device"`
Inode uint64 `json:"inode"`
Path string `json:"path,omitempty"`
}
type ProcessNamespace struct {
Type string `json:"type"` // cgroup, ipc, mnt, net, pid, user, uts
Inode string `json:"inode"` // string because kernel shows as "[4026531835]"
}
type ProcessListeningPort struct {
Protocol string `json:"protocol"` // TCP, UDP
LocalAddr string `json:"local_addr"`
LocalPort int `json:"local_port"`
FD int `json:"fd,omitempty"`
Socket uint64 `json:"socket,omitempty"`
}
// FullProcessSnapshot is the complete result of an on-demand process scan.
type FullProcessSnapshot struct {
Processes []FullProcess `json:"processes"`
ProcessCount int `json:"process_count"`
ScannedAt time.Time `json:"scanned_at"`
DurationMs int64 `json:"duration_ms"`
}
// GetFullProcessSnapshot reads /proc to build a complete process inventory.
// Platform-specific implementations live in process_detail_linux.go and
// process_detail_other.go (stub returning "not supported").
func GetFullProcessSnapshot() (*FullProcessSnapshot, error) {
return getFullProcessSnapshot()
}
// ProcessCaps holds per-process data collection limits.
// Zero values mean "no cap" (but the underlying /proc data is still bounded by the kernel).
type ProcessCaps struct {
MaxOpenFiles int `json:"max_open_files"`
MaxSockets int `json:"max_sockets"`
MaxPipes int `json:"max_pipes"`
MaxMemoryMap int `json:"max_memory_map"`
MaxNamespaces int `json:"max_namespaces"`
MaxEnvKeys int `json:"max_env_keys"`
MaxListeningPorts int `json:"max_listening_ports"`
}
// GetProcessDetail reads the related data (open files, sockets, env, etc.) for a
// specific process. Called on drill-down, not on the initial list scan.
func GetProcessDetail(pid int, caps ProcessCaps) (*FullProcess, error) {
return getProcessDetail(pid, caps)
}