Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
5341120
feat(compliance): add CIS benchmark scanning integration
MacJediWizard Jan 2, 2026
9fe3993
fix(security): address HIGH priority security vulnerabilities
MacJediWizard Jan 3, 2026
4a19464
fix(security): resolve remaining security and code quality issues
MacJediWizard Jan 3, 2026
39a16b6
chore: bump version to 1.3.8
MacJediWizard Jan 3, 2026
3ecb7eb
chore: bump version to 1.4.0
MacJediWizard Jan 3, 2026
8ceca1f
fix: remove unused os import in docker_bench.go
MacJediWizard Jan 3, 2026
899fe10
fix: security hardening and code cleanup
MacJediWizard Jan 3, 2026
be0932c
fix: block skip_ssl_verify in production environments
MacJediWizard Jan 3, 2026
0270840
feat: add compliance_scan WebSocket handler and fix field names
MacJediWizard Jan 3, 2026
85f2a9e
chore: bump version to 1.4.1
MacJediWizard Jan 3, 2026
2b44438
Add auto-install/cleanup for compliance tools
MacJediWizard Jan 4, 2026
0c494c2
Bump version to 1.5.0
MacJediWizard Jan 4, 2026
0d7ffd7
Add integration setup status notifications to server
MacJediWizard Jan 4, 2026
a1b8bc6
Bump version to 1.5.1
MacJediWizard Jan 4, 2026
8e26b58
Remove OpenSCAP packages when compliance is disabled
MacJediWizard Jan 4, 2026
bb86917
Add --version flag and update version to 1.5.2
MacJediWizard Jan 4, 2026
b4ca95e
Only run Docker Bench when Docker integration is enabled
MacJediWizard Jan 4, 2026
c994850
Upgrade SCAP packages for latest Ubuntu 24.04 content (v1.5.4)
MacJediWizard Jan 4, 2026
eacc991
Add comprehensive scanner info to compliance status reports (v1.5.5)
MacJediWizard Jan 4, 2026
e476bc0
Fix apt-get hanging with non-interactive mode and timeouts (v1.5.6)
MacJediWizard Jan 4, 2026
ab5082b
Add dynamic profile discovery and comprehensive XCCDF support (v1.5.7)
MacJediWizard Jan 4, 2026
de37e5b
Add comprehensive remediation support (v1.5.8)
MacJediWizard Jan 4, 2026
3a1f01e
feat: add remediation support for compliance scans
MacJediWizard Jan 4, 2026
75df33a
fix: improve Ubuntu 24.04 OpenSCAP support
MacJediWizard Jan 4, 2026
5c14bb6
fix: improve Ubuntu 24.04 CIS content guidance
MacJediWizard Jan 4, 2026
caabe6b
feat: add SSG version check and upgrade capability
MacJediWizard Jan 4, 2026
c9f9e0a
fix: prevent SSG upgrade from hanging on apt prompts
MacJediWizard Jan 4, 2026
f28c383
debug: add verbose WebSocket message logging
MacJediWizard Jan 4, 2026
7137339
feat: install SSG content from GitHub releases
MacJediWizard Jan 4, 2026
78a6917
fix: detect GitHub-installed SSG version correctly
MacJediWizard Jan 4, 2026
7a05a14
feat: send updated status after SSG upgrade
MacJediWizard Jan 4, 2026
8960e49
fix: receive and use profile_id for specific compliance scans
MacJediWizard Jan 4, 2026
a2223e8
feat: add single rule remediation support
MacJediWizard Jan 4, 2026
b258d5e
Add real-time compliance scan progress updates via WebSocket
MacJediWizard Jan 4, 2026
0ea3551
Enhance OpenSCAP parser to extract rich rule metadata (v1.5.19)
MacJediWizard Jan 4, 2026
d3fcdae
Add actual/expected value extraction and OVAL results (v1.5.20)
MacJediWizard Jan 4, 2026
7368225
Fix: Parse benchmark file for rule metadata (v1.5.21)
MacJediWizard Jan 4, 2026
7726aed
Fix namespace regex and add debug logging (v1.5.25)
MacJediWizard Jan 4, 2026
4180b0a
Rename display names from PatchMon to PatchMonEnhanced in README
MacJediWizard Jan 4, 2026
7e8dc5a
Fix single rule remediation to use proper oscap flags
MacJediWizard Jan 4, 2026
cfc856f
Report integration status on agent startup/reconnect
MacJediWizard Jan 5, 2026
7e4ec07
Fix Docker Bench installation when Docker enabled after Compliance
MacJediWizard Jan 5, 2026
5d1c15b
Add oscap-docker container image CVE scanning
MacJediWizard Jan 5, 2026
8feb9ff
Merge upstream/main - add package description support
MacJediWizard Jan 5, 2026
4a6d6c4
security: Fix shell injection and TOCTOU vulnerabilities
MacJediWizard Jan 5, 2026
b09b921
chore: Bump version to 1.5.30
MacJediWizard Jan 5, 2026
5dca33d
Add oscap_docker_available field to scanner info
MacJediWizard Jan 5, 2026
d328a67
Fix compliance scanner status reporting on startup
MacJediWizard Jan 5, 2026
5216987
Add periodic integration status reporting with configurable interval
MacJediWizard Jan 5, 2026
5738995
chore: Bump version to 1.5.31
MacJediWizard Jan 5, 2026
be96d03
Fix integration status reporting and add refresh support
MacJediWizard Jan 5, 2026
eae6510
Fix oscap-docker installation on Ubuntu/Debian
MacJediWizard Jan 6, 2026
2499311
Mark oscap-docker as unavailable on Ubuntu/Debian
MacJediWizard Jan 6, 2026
815793c
v1.5.35: Fix Docker Bench scanning and error message truncation
MacJediWizard Jan 6, 2026
f1b1405
fix: use Docker Bench :latest tag instead of outdated digest
MacJediWizard Jan 6, 2026
c68929d
fix: correct Go version in go.mod to valid version 1.24.0
MacJediWizard Jan 6, 2026
08e526e
feat: add docker_inventory_refresh command handler
MacJediWizard Jan 6, 2026
dcc5136
fix: update version to 1.5.38
MacJediWizard Jan 6, 2026
b02351a
fix: improve Docker Bench reliability and debugging
MacJediWizard Jan 6, 2026
3b69e6d
chore: bump version to 1.5.39
MacJediWizard Jan 6, 2026
76c2842
fix: detect Docker socket location for Docker Bench
MacJediWizard Jan 6, 2026
dae3353
chore: bump version to 1.5.40
MacJediWizard Jan 6, 2026
a6d6ee8
fix: increase integration collection timeout to 10 minutes
MacJediWizard Jan 6, 2026
e0e44e4
chore: bump version to 1.5.41
MacJediWizard Jan 6, 2026
5391b17
feat: add progress logging for OpenSCAP scans
MacJediWizard Jan 6, 2026
c173b81
chore: bump version to 1.5.42
MacJediWizard Jan 6, 2026
599ae72
fix: connect WebSocket before running initial report
MacJediWizard Jan 6, 2026
6617a9b
chore: bump version to 1.5.43
MacJediWizard Jan 6, 2026
054258a
Increase OpenSCAP timeout to 15 minutes, add Docker Bench socket logging
MacJediWizard Jan 6, 2026
b4806ae
Switch to jauderho/docker-bench-security image
MacJediWizard Jan 6, 2026
f014dea
Add -b flag to Docker Bench to disable color output
MacJediWizard Jan 6, 2026
82f1093
Add remediation and finding parsing for Docker Bench
MacJediWizard Jan 6, 2026
68e1bd7
Fix Docker Bench multi-line remediation parsing
MacJediWizard Jan 6, 2026
408afa2
v1.5.49: Add debug logging for Docker Bench warn status investigation
MacJediWizard Jan 6, 2026
485262b
security: fix WebSocket input validation and credentials TOCTOU race
MacJediWizard Jan 7, 2026
201cd79
chore: bump version to 1.5.51
MacJediWizard Jan 7, 2026
f561179
Increase compliance scan timeout from 15 to 25 minutes
MacJediWizard Jan 7, 2026
d7a979b
Remove binaries from repo (should be in releases only)
MacJediWizard Jan 7, 2026
1dedb27
Add compliance_on_demand_only config option
MacJediWizard Jan 8, 2026
dd058dc
Remove binaries from repo
MacJediWizard Jan 8, 2026
4deb9b5
feat: add compliance on-demand-only mode WebSocket handler
MacJediWizard Jan 8, 2026
39262d8
chore: apply go fmt formatting
MacJediWizard Jan 8, 2026
c541db0
chore: bump version to 1.5.55
MacJediWizard Jan 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,6 @@ go.work.sum
# .vscode/

build/

# AI encryption key
.encryption_key
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PatchMon Agent
# PatchMonEnhanced Agent

PatchMon's monitoring agent sends package and repository information to the PatchMon server.
PatchMonEnhanced's monitoring agent sends package and repository information to the PatchMonEnhanced server.

## Installation

Expand Down Expand Up @@ -180,7 +180,7 @@ Logs are written to `/var/log/patchmon-agent.log` with timestamps and structured
```
2023-09-27T10:30:00 level=info msg="Collecting package information..."
2023-09-27T10:30:01 level=info msg="Found packages" count=156
2023-09-27T10:30:02 level=info msg="Sending report to PatchMon server..."
2023-09-27T10:30:02 level=info msg="Sending report to PatchMonEnhanced server..."
2023-09-27T10:30:03 level=info msg="Report sent successfully"
```

Expand Down Expand Up @@ -232,7 +232,7 @@ The Go implementation maintains compatibility with the existing shell script wor

1. **Same command structure**: All commands work identically
2. **Same configuration files**: Uses the same paths and formats
3. **Same API compatibility**: Works with existing PatchMon servers
3. **Same API compatibility**: Works with existing PatchMonEnhanced servers
4. **Improved performance**: Faster execution and better error handling

To migrate:
Expand Down
147 changes: 114 additions & 33 deletions cmd/patchmon-agent/commands/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import (
"encoding/json"
"fmt"
"os"
"sync"
"time"

"patchmon-agent/internal/client"
"patchmon-agent/internal/hardware"
"patchmon-agent/internal/integrations"
"patchmon-agent/internal/integrations/compliance"
"patchmon-agent/internal/integrations/docker"
"patchmon-agent/internal/network"
"patchmon-agent/internal/packages"
Expand Down Expand Up @@ -102,10 +104,10 @@ func sendReport(outputJson bool) error {
needsReboot, rebootReason := systemDetector.CheckRebootRequired()
installedKernel := systemDetector.GetLatestInstalledKernel()
logger.WithFields(logrus.Fields{
"needs_reboot": needsReboot,
"reason": rebootReason,
"installed_kernel": installedKernel,
"running_kernel": systemInfo.KernelVersion,
"needs_reboot": needsReboot,
"reason": rebootReason,
"installed_kernel": installedKernel,
"running_kernel": systemInfo.KernelVersion,
}).Info("Reboot status check completed")

// Get package information
Expand Down Expand Up @@ -172,31 +174,31 @@ func sendReport(outputJson bool) error {

// Create payload
payload := &models.ReportPayload{
Packages: packageList,
Repositories: repoList,
OSType: osType,
OSVersion: osVersion,
Hostname: hostname,
IP: ipAddress,
Architecture: architecture,
AgentVersion: version.Version,
MachineID: systemDetector.GetMachineID(),
KernelVersion: systemInfo.KernelVersion,
Packages: packageList,
Repositories: repoList,
OSType: osType,
OSVersion: osVersion,
Hostname: hostname,
IP: ipAddress,
Architecture: architecture,
AgentVersion: version.Version,
MachineID: systemDetector.GetMachineID(),
KernelVersion: systemInfo.KernelVersion,
InstalledKernelVersion: installedKernel,
SELinuxStatus: systemInfo.SELinuxStatus,
SystemUptime: systemInfo.SystemUptime,
LoadAverage: systemInfo.LoadAverage,
CPUModel: hardwareInfo.CPUModel,
CPUCores: hardwareInfo.CPUCores,
RAMInstalled: hardwareInfo.RAMInstalled,
SwapSize: hardwareInfo.SwapSize,
DiskDetails: hardwareInfo.DiskDetails,
GatewayIP: networkInfo.GatewayIP,
DNSServers: networkInfo.DNSServers,
NetworkInterfaces: networkInfo.NetworkInterfaces,
ExecutionTime: executionTime,
NeedsReboot: needsReboot,
RebootReason: rebootReason,
SELinuxStatus: systemInfo.SELinuxStatus,
SystemUptime: systemInfo.SystemUptime,
LoadAverage: systemInfo.LoadAverage,
CPUModel: hardwareInfo.CPUModel,
CPUCores: hardwareInfo.CPUCores,
RAMInstalled: hardwareInfo.RAMInstalled,
SwapSize: hardwareInfo.SwapSize,
DiskDetails: hardwareInfo.DiskDetails,
GatewayIP: networkInfo.GatewayIP,
DNSServers: networkInfo.DNSServers,
NetworkInterfaces: networkInfo.NetworkInterfaces,
ExecutionTime: executionTime,
NeedsReboot: needsReboot,
RebootReason: rebootReason,
}

// If --report-json flag is set, output JSON and exit
Expand Down Expand Up @@ -241,13 +243,27 @@ func sendReport(outputJson bool) error {
return nil
}
} else {
// Proactive update check after report (non-blocking with timeout)
// Run in a goroutine to avoid blocking the report completion
// Proactive update check after report (with timeout to prevent hanging)
// Use a WaitGroup to ensure the goroutine completes before function returns
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()

// Create a context with timeout to prevent indefinite hanging
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()

// Add a delay to prevent immediate checks after service restart
// This gives the new process time to fully initialize
time.Sleep(5 * time.Second)

select {
case <-time.After(5 * time.Second):
// Continue with update check
case <-ctx.Done():
logger.Debug("Update check cancelled due to timeout")
return
}

logger.Info("Checking for agent updates...")
versionInfo, err := getServerVersionInfo()
if err != nil {
Expand Down Expand Up @@ -277,6 +293,8 @@ func sendReport(outputJson bool) error {
logger.WithField("version", versionInfo.CurrentVersion).Info("Agent is up to date")
}
}()
// Wait for the update check to complete (with the internal timeout)
wg.Wait()
}

// Collect and send integration data (Docker, etc.) separately
Expand Down Expand Up @@ -305,11 +323,23 @@ func sendIntegrationData() {

// Register available integrations
integrationMgr.Register(docker.New(logger))

// Only register compliance integration if not set to on-demand only
// When compliance_on_demand_only is true, compliance scans will only run when triggered from the UI
if !cfgManager.IsComplianceOnDemandOnly() {
complianceInteg := compliance.New(logger)
complianceInteg.SetDockerIntegrationEnabled(cfgManager.IsIntegrationEnabled("docker"))
integrationMgr.Register(complianceInteg)
} else {
logger.Info("Skipping compliance scan during scheduled report (compliance_on_demand_only=true)")
}
// Future: integrationMgr.Register(proxmox.New(logger))
// Future: integrationMgr.Register(kubernetes.New(logger))

// Discover and collect from all available integrations
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
// 25 minute timeout to allow OpenSCAP scans to complete (they can take 15+ minutes on complex systems)
// This gives time for both OpenSCAP and Docker Bench to complete
ctx, cancel := context.WithTimeout(context.Background(), 25*time.Minute)
defer cancel()

integrationData := integrationMgr.CollectAll(ctx)
Expand All @@ -332,6 +362,11 @@ func sendIntegrationData() {
sendDockerData(httpClient, dockerData, hostname, machineID)
}

// Send Compliance data if available
if complianceData, exists := integrationData["compliance"]; exists && complianceData.Error == "" {
sendComplianceData(httpClient, complianceData, hostname, machineID)
}

// Future: Send other integration data here
}

Expand Down Expand Up @@ -375,3 +410,49 @@ func sendDockerData(httpClient *client.Client, integrationData *models.Integrati
"updates": response.UpdatesFound,
}).Info("Docker data sent successfully")
}

// sendComplianceData sends compliance scan data to server
func sendComplianceData(httpClient *client.Client, integrationData *models.IntegrationData, hostname, machineID string) {
// Extract Compliance data from integration data
complianceData, ok := integrationData.Data.(*models.ComplianceData)
if !ok {
logger.Warn("Failed to extract compliance data from integration")
return
}

if len(complianceData.Scans) == 0 {
logger.Debug("No compliance scans to send")
return
}

payload := &models.CompliancePayload{
ComplianceData: *complianceData,
Hostname: hostname,
MachineID: machineID,
AgentVersion: version.Version,
}

totalRules := 0
for _, scan := range complianceData.Scans {
totalRules += scan.TotalRules
}

logger.WithFields(logrus.Fields{
"scans": len(complianceData.Scans),
"total_rules": totalRules,
}).Info("Sending compliance data to server...")

ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) // Longer timeout for compliance
defer cancel()

response, err := httpClient.SendComplianceData(ctx, payload)
if err != nil {
logger.WithError(err).Warn("Failed to send compliance data (will retry on next report)")
return
}

logger.WithFields(logrus.Fields{
"scans_received": response.ScansReceived,
"message": response.Message,
}).Info("Compliance data sent successfully")
}
9 changes: 5 additions & 4 deletions cmd/patchmon-agent/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ var (

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "patchmon-agent",
Short: "PatchMon Agent for package monitoring",
Use: "patchmon-agent",
Short: "PatchMon Agent for package monitoring",
Version: version.Version,
Long: `PatchMon Agent v` + version.Version + `

A monitoring agent that sends package information to PatchMon.`,
Expand Down Expand Up @@ -87,7 +88,8 @@ func initialiseAgent() {
if logFile == "" {
logFile = config.DefaultLogFile
}
_ = os.MkdirAll(filepath.Dir(logFile), 0755)
// SECURITY: Use 0750 for log directory (no world access)
_ = os.MkdirAll(filepath.Dir(logFile), 0750)
logger.SetOutput(&lumberjack.Logger{Filename: logFile, MaxSize: 10, MaxBackups: 5, MaxAge: 14, Compress: true})
}

Expand Down Expand Up @@ -132,4 +134,3 @@ func checkRoot() error {
}
return nil
}

Loading