-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathexecuteCheck.go
More file actions
160 lines (132 loc) · 4.27 KB
/
executeCheck.go
File metadata and controls
160 lines (132 loc) · 4.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package main
import (
"context"
"fmt"
"log"
"os"
"os/exec"
"strings"
"time"
"github.com/codeGROOVE-dev/gitMDM/internal/analyzer"
"github.com/codeGROOVE-dev/gitMDM/internal/config"
"github.com/codeGROOVE-dev/gitMDM/internal/gitmdm"
)
// executeCheck executes a single check rule (command or file) and returns the output.
func (a *Agent) executeCheck(ctx context.Context, checkName string, rule config.CommandRule) gitmdm.CommandOutput {
// Handle file checks
if rule.File != "" {
return a.readFile(checkName, rule)
}
// Handle command checks
if rule.Output != "" {
return a.executeCommand(ctx, checkName, rule)
}
// Empty rule
return gitmdm.CommandOutput{
Skipped: true,
FailReason: "No command or file specified",
}
}
// readFile reads a file and returns its contents as a CommandOutput.
func (*Agent) readFile(checkName string, rule config.CommandRule) gitmdm.CommandOutput {
start := time.Now()
if *debugMode {
log.Printf("[DEBUG] Reading file for check %s: %s", checkName, rule.File)
}
content, err := os.ReadFile(rule.File)
output := gitmdm.CommandOutput{
File: rule.File,
}
if err != nil {
if os.IsNotExist(err) {
output.FileMissing = true
if *debugMode {
log.Printf("[DEBUG] File not found for check %s: %s", checkName, rule.File)
}
} else {
output.Stderr = fmt.Sprintf("Error reading file: %v", err)
output.ExitCode = 1
}
} else {
output.Stdout = string(content)
output.ExitCode = 0
// Limit file size
if len(output.Stdout) > maxOutputSize {
output.Stdout = output.Stdout[:maxOutputSize] + "\n[File truncated to 90KB]..."
}
}
// Analyze against the rule
if err := analyzer.AnalyzeCheck(&output, rule); err != nil {
log.Printf("[ERROR] Failed to analyze file check: %v", err)
}
if *debugMode {
log.Printf("[DEBUG] File read completed in %v (missing: %v, failed: %v): %s",
time.Since(start), output.FileMissing, output.Failed, rule.File)
}
return output
}
// checkCommandAvailable verifies that a command is available to execute.
func checkCommandAvailable(checkName, command string) *gitmdm.CommandOutput {
if containsShellOperators(command) {
return nil // Commands with shell operators need shell interpretation
}
commandParts := strings.Fields(command)
if len(commandParts) == 0 {
return nil
}
primaryCmd := commandParts[0]
if isShellBuiltin(primaryCmd) || strings.Contains(primaryCmd, "/") {
return nil // Shell builtins and absolute paths don't need validation
}
// Temporarily set PATH for LookPath
oldPath := os.Getenv("PATH")
if err := os.Setenv("PATH", securePath()); err != nil {
log.Printf("[WARN] Failed to set PATH for command check: %v", err)
}
_, lookupErr := exec.LookPath(primaryCmd)
if err := os.Setenv("PATH", oldPath); err != nil {
log.Printf("[WARN] Failed to restore PATH: %v", err)
}
if lookupErr != nil {
if *debugMode {
log.Printf("[DEBUG] Command '%s' not found in PATH for check '%s', skipping", primaryCmd, checkName)
}
return &gitmdm.CommandOutput{
Command: command,
Skipped: true,
FileMissing: true, // Treat missing command like missing file
Stderr: fmt.Sprintf("Skipped: %s not found", primaryCmd),
}
}
return nil
}
// executeCommand executes a command and returns its output.
func (a *Agent) executeCommand(ctx context.Context, checkName string, rule config.CommandRule) gitmdm.CommandOutput {
start := time.Now()
command := rule.Output
// Check if command is available
if result := checkCommandAvailable(checkName, command); result != nil {
return *result
}
// Use longer timeout for software update checks (they contact remote servers)
timeout := commandTimeout
if checkName == "available_updates" || checkName == "automatic_updates" {
timeout = 30 * time.Second
}
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
if *debugMode {
log.Printf("[DEBUG] Executing command: %s", command)
}
// Execute the command
output := a.executeCommandWithPipes(ctx, checkName, command)
// Analyze against the rule
if err := analyzer.AnalyzeCheck(&output, rule); err != nil {
log.Printf("[ERROR] Failed to analyze command check: %v", err)
}
if *debugMode {
log.Printf("[DEBUG] Command completed in %v (skipped: %v, failed: %v): %s",
time.Since(start), output.Skipped, output.Failed, command)
}
return output
}