-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathanalyzer.go
More file actions
149 lines (131 loc) · 4.1 KB
/
analyzer.go
File metadata and controls
149 lines (131 loc) · 4.1 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
// Package analyzer provides generic compliance check analysis based on YAML rules.
package analyzer
import (
"fmt"
"log"
"os"
"regexp"
"strings"
"github.com/codeGROOVE-dev/gitMDM/internal/config"
"github.com/codeGROOVE-dev/gitMDM/internal/gitmdm"
)
var debug = os.Getenv("DEBUG") == "true"
// AnalyzeCheck analyzes a CommandOutput against a CommandRule to determine pass/fail.
func AnalyzeCheck(output *gitmdm.CommandOutput, rule config.CommandRule) error {
// If command/file was skipped or missing, don't fail the check
if output.Skipped || output.FileMissing {
return nil // Not a failure, just not applicable
}
// If command failed (non-zero exit) and no explicit exitcode check is configured,
// skip includes/excludes analysis (the command couldn't run properly)
if output.ExitCode != 0 && rule.ExitCode == nil {
// Mark as skipped since the command couldn't run (e.g., pgrep failed)
if debug {
log.Printf("[DEBUG] Skipping analysis for command with exit code %d (no exitcode rule configured)", output.ExitCode)
}
output.Skipped = true
return nil
}
// Combine stdout and stderr for analysis
content := output.Stdout + output.Stderr
// Check includes pattern (fail if ANY line matches - like grep)
if rule.Includes != "" {
includesRegex, err := regexp.Compile("(?i)" + rule.Includes)
if err != nil {
return fmt.Errorf("invalid includes regex: %w", err)
}
// Split content into lines and check each one
lines := strings.Split(content, "\n")
for _, line := range lines {
if includesRegex.MatchString(line) {
output.Failed = true
output.FailReason = fmt.Sprintf("Output matched failure pattern: %s", rule.Includes)
output.Remediation = rule.Remediation
return nil // Not an error, just a failed check
}
}
}
// Check excludes pattern (fail if NO line matches - like grep -q)
if rule.Excludes != "" {
excludesRegex, err := regexp.Compile("(?i)" + rule.Excludes)
if err != nil {
return fmt.Errorf("invalid excludes regex: %w", err)
}
// Split content into lines and check if any line matches
lines := strings.Split(content, "\n")
matchFound := false
for _, line := range lines {
if excludesRegex.MatchString(line) {
matchFound = true
break
}
}
if !matchFound {
output.Failed = true
output.FailReason = fmt.Sprintf("Output missing required pattern: %s", rule.Excludes)
output.Remediation = rule.Remediation
return nil
}
}
// Check exit code (fail if matches)
if rule.ExitCode != nil && output.ExitCode == *rule.ExitCode {
output.Failed = true
output.FailReason = fmt.Sprintf("Exit code %d indicates failure", output.ExitCode)
output.Remediation = rule.Remediation
return nil
}
// If no criteria specified, it's informational only
return nil
}
// DetermineOverallStatus determines the overall status of a check based on all outputs.
func DetermineOverallStatus(outputs []gitmdm.CommandOutput) (status string, reason string, remediation []string) {
hasFailure := false
hasSuccess := false
allSkipped := true
var failReasons []string
var allRemediation []string
remediationSeen := make(map[string]bool)
for _, output := range outputs {
if output.Failed {
hasFailure = true
allSkipped = false
if output.FailReason != "" {
failReasons = append(failReasons, output.FailReason)
}
// Collect unique remediation steps
for _, step := range output.Remediation {
if !remediationSeen[step] {
allRemediation = append(allRemediation, step)
remediationSeen[step] = true
}
}
} else if !output.Skipped && !output.FileMissing {
hasSuccess = true
allSkipped = false
}
}
// Determine overall status
switch {
case hasFailure:
status = "fail"
switch {
case len(failReasons) == 1:
reason = failReasons[0]
case len(failReasons) > 1:
reason = strings.Join(failReasons, "; ")
default:
reason = "Check failed"
}
remediation = allRemediation
case hasSuccess:
status = "pass"
reason = "Check passed"
case allSkipped:
status = "n/a"
reason = "Check not applicable"
default:
status = "n/a"
reason = "Unable to determine status"
}
return status, reason, remediation
}