6.8 KiB
Process Scanner
On-demand /proc filesystem scanning for process inventory and drill-down detail.
Component Details
| Property | Value |
|---|---|
| Method | Direct /proc filesystem reads (no subprocess spawns) |
| Platform | Linux only (stub on other platforms) |
| Execution time | ~200ms for 200-process snapshot; ~50ms per drill-down |
| Trigger | On-demand when user opens the Processes tab in the dashboard |
| Data model | 25+ fields per process (osquery parity) + 7 related data types |
| Storage | Dedicated tables: agent_process_snapshots, agent_processes, agent_process_related |
| Retention | Last 10 snapshots per agent (auto-cleanup) |
Architecture
The process scanner follows the existing command-dispatch pattern:
- Dashboard opens Processes tab →
POST /api/v1/agents/:id/processes/scan - Server creates a signed
scan_processescommand (with dedup check) - Agent polls for commands, receives
scan_processes, callssystem.GetFullProcessSnapshot() - Agent reports snapshot to
POST /api/v1/agents/:id/process-scan - Server stores snapshot + processes + related data, cleans up old snapshots
- Dashboard reads latest snapshot via
GET /api/v1/agents/:id/processes
On drill-down (clicking a process row):
- Dashboard requests
GET /api/v1/agents/:id/processes/:processId - Server returns process + all related data (open files, sockets, pipes, env, memory map, namespaces, listening ports)
Data Collection
List Scan (GetFullProcessSnapshot)
Reads /proc/[pid]/stat, /proc/[pid]/status, /proc/[pid]/exe, /proc/[pid]/cmdline, /proc/[pid]/cwd, /proc/[pid]/io for every PID. No related data collected at this stage.
Fields (25+): PID, Name, Path, Cmdline, Cwd, State, UID, GID, EUID, EGID, User, Group, TTY, TTYName, CPUSecondsUser, CPUSecondsSystem, CPUPercent, RSSBytes, VMSBytes, MemPercent, Threads, Nice, StartTimeSeconds, ParentPID, ProcessGroupID, ElevationStatus, OnDisk, DiskBytesRead, DiskBytesWritten
Drill-Down (GetProcessDetail)
Adds related data from a single /proc/[pid]/fd/ walk (consolidated from three separate traversals):
| Data Type | Source | Cap (configurable) |
|---|---|---|
| Open files | /proc/[pid]/fd/ symlink targets |
max_open_files (default 2000) |
| Open sockets | /proc/[pid]/net/tcp, tcp6, unix |
max_sockets (default 500) |
| Open pipes | /proc/[pid]/fd/ pipe inodes |
max_pipes (default 500) |
| Environment keys | /proc/[pid]/environ (keys only, no values) |
max_env_keys (default 200) |
| Memory map | /proc/[pid]/maps |
max_memory_map (default 2000) |
| Namespaces | /proc/[pid]/ns/ symlinks |
max_namespaces (default 50) |
| Listening ports | Socket inode correlation with /proc/net/tcp |
max_listening_ports (default 100) |
Key Implementation Detail: Socket Inode Correlation
Listening ports are per-process, not system-wide. The scanner collects socket inodes from /proc/[pid]/fd/ symlinks (socket:[12345]), then matches them against inode numbers in /proc/net/tcp and /proc/net/tcp6. Only LISTEN state (0A) entries whose inode matches a process socket are included.
Key Implementation Detail: IPv6 Address Parsing
The kernel stores IPv6 addresses in /proc/net/tcp6 as 4 little-endian 32-bit words (%08X%08X%08X%08X). The parser reverses bytes within each 4-byte group (not across the entire 16-byte address) and uses %02x for zero-padded output.
Key Implementation Detail: /proc/[pid]/stat Field Indices
After stripping pid (comm), the fields array is 0-indexed from field 3:
[0]=state,[1]=ppid,[2]=pgrp,[3]=session,[4]=tty_nr[11]=utime,[12]=stime,[16]=nice,[19]=starttime
Configurable Caps
Data collection limits are server-controlled via ProcessExplorerConfig (stored in security settings under operational category). Delivered to agents on check-in. Set to 0 for no cap.
| Setting | Default | Purpose |
|---|---|---|
process_explorer_max_open_files |
2000 | Open file descriptors per process |
process_explorer_max_sockets |
500 | Open sockets per process |
process_explorer_max_pipes |
500 | Open pipes per process |
process_explorer_max_memory_map |
2000 | Memory map entries per process |
process_explorer_max_namespaces |
50 | Namespace entries per process |
process_explorer_max_env_keys |
200 | Environment variable keys per process |
process_explorer_max_listening_ports |
100 | TCP listening ports per process |
UI: Settings → Process Explorer (/settings/process-explorer)
Implementation Files
Agent
| File | Purpose |
|---|---|
agent/internal/system/process_detail.go |
Types: FullProcess, ProcessOpenFile, ProcessOpenSocket, ProcessOpenPipe, ProcessMemoryMap, ProcessNamespace, ProcessListeningPort, ProcessCaps, FullProcessSnapshot |
agent/internal/system/process_detail_linux.go |
Linux /proc reader: getFullProcessSnapshot(), getProcessDetail(), walkProcFD(), readListeningPorts(), parseHexAddr() |
agent/internal/system/process_detail_other.go |
Stub for non-Linux platforms |
agent/internal/handlers/processes.go |
HandleScanProcesses — command handler |
agent/internal/client/client.go |
ReportProcessScan — reports to server |
Server
| File | Purpose |
|---|---|
server/internal/database/migrations/055_create_process_tables.up.sql |
Schema: agent_process_snapshots, agent_processes, agent_process_related |
server/internal/models/process.go |
Server-side models |
server/internal/database/queries/processes.go |
ProcessQueries — insert, query, cleanup |
server/internal/api/handlers/processes.go |
ProcessHandler — 4 endpoints |
server/internal/models/command.go |
CommandTypeScanProcesses constant |
Web
| File | Purpose |
|---|---|
web/src/types/process.ts |
TypeScript interfaces |
web/src/hooks/useProcesses.ts |
React Query hooks |
web/src/components/ProcessesTab.tsx |
Main tab with sortable table |
web/src/components/ProcessDetailModal.tsx |
6-tab modal (Overview, Network, Files, Environment, Memory, Namespaces) |
web/src/hooks/useProcessExplorer.ts |
Settings hooks |
web/src/pages/settings/ProcessExplorer.tsx |
Settings UI |
Security Considerations
- Environment variable values are never transmitted. Only key names are collected. Env vars may contain secrets (API keys, database passwords).
- No subprocess spawns. All data comes from direct
/procreads — nops,lsof, or similar commands. - On-demand only. The scan command is only issued when a user opens the Processes tab. No background broadcasting.
- Command dedup. The server checks for existing pending
scan_processescommands before creating a new one.
Added: 2026-06-10