Skip to content

Commit 6e2393f

Browse files
tas50claude
andcommitted
🧹 Add status to default fields for gatekeeper and sip
- Use Internal struct caching to avoid double command execution - Use command resource pattern instead of conn.RunCommand - Use more specific SIP enabled check (status: enabled) - Add filevault to spelling expect.txt - Add hasPersonalRecoveryKey, hasInstitutionalRecoveryKey, users fields to filevault Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 25782ea commit 6e2393f

File tree

7 files changed

+216
-45
lines changed

7 files changed

+216
-45
lines changed

.github/actions/spelling/expect.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ failback
8181
fargate
8282
filestore
8383
filesz
84+
filevault
8485
firefox
8586
firestore
8687
FLEXGROUP

providers/os/resources/macos_filevault.go

Lines changed: 92 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,122 @@
44
package resources
55

66
import (
7-
"io"
7+
"errors"
88
"strings"
9+
"sync"
910

10-
"go.mondoo.com/mql/v13/providers/os/connection/shared"
11+
"go.mondoo.com/mql/v13/llx"
1112
)
1213

13-
func (m *mqlMacosFilevault) status() (string, error) {
14-
conn := m.MqlRuntime.Connection.(shared.Connection)
14+
type mqlMacosFilevaultInternal struct {
15+
lock sync.Mutex
16+
fetched bool
17+
output string
18+
}
1519

16-
cmd, err := conn.RunCommand("fdesetup status")
17-
if err != nil {
18-
return "", err
20+
func (m *mqlMacosFilevault) fetchStatus() (string, error) {
21+
if m.fetched {
22+
return m.output, nil
23+
}
24+
m.lock.Lock()
25+
defer m.lock.Unlock()
26+
if m.fetched {
27+
return m.output, nil
1928
}
2029

21-
data, err := io.ReadAll(cmd.Stdout)
30+
res, err := NewResource(m.MqlRuntime, "command", map[string]*llx.RawData{
31+
"command": llx.StringData("fdesetup status"),
32+
})
2233
if err != nil {
2334
return "", err
2435
}
36+
cmd := res.(*mqlCommand)
37+
if exit := cmd.GetExitcode(); exit.Data != 0 {
38+
return "", errors.New("fdesetup status failed: " + cmd.GetStderr().Data)
39+
}
2540

2641
// fdesetup status outputs lines like:
2742
// "FileVault is On."
2843
// "FileVault is Off."
2944
// "Encryption in progress: Percent completed = 50.0"
3045
// "Decryption in progress: Percent completed = 50.0"
31-
output := strings.TrimSpace(string(data))
32-
33-
// Return the first line which contains the primary status
46+
output := strings.TrimSpace(cmd.GetStdout().Data)
3447
lines := strings.SplitN(output, "\n", 2)
35-
return strings.TrimSpace(lines[0]), nil
48+
49+
m.output = strings.TrimSpace(lines[0])
50+
m.fetched = true
51+
return m.output, nil
52+
}
53+
54+
func (m *mqlMacosFilevault) status() (string, error) {
55+
return m.fetchStatus()
3656
}
3757

3858
func (m *mqlMacosFilevault) enabled() (bool, error) {
39-
status, err := m.status()
59+
status, err := m.fetchStatus()
4060
if err != nil {
4161
return false, err
4262
}
4363

44-
// FileVault is considered enabled if it's on or encryption is in progress
4564
return strings.Contains(status, "FileVault is On") ||
4665
strings.Contains(status, "Encryption in progress"), nil
4766
}
67+
68+
func (m *mqlMacosFilevault) runFdesetup(subcmd string) (string, error) {
69+
res, err := NewResource(m.MqlRuntime, "command", map[string]*llx.RawData{
70+
"command": llx.StringData("fdesetup " + subcmd),
71+
})
72+
if err != nil {
73+
return "", err
74+
}
75+
cmd := res.(*mqlCommand)
76+
if exit := cmd.GetExitcode(); exit.Data != 0 {
77+
return "", errors.New("fdesetup " + subcmd + " failed: " + cmd.GetStderr().Data)
78+
}
79+
return strings.TrimSpace(cmd.GetStdout().Data), nil
80+
}
81+
82+
func (m *mqlMacosFilevault) hasPersonalRecoveryKey() (bool, error) {
83+
// fdesetup haspersonalrecoverykey outputs "true" or "false"
84+
output, err := m.runFdesetup("haspersonalrecoverykey")
85+
if err != nil {
86+
return false, err
87+
}
88+
return output == "true", nil
89+
}
90+
91+
func (m *mqlMacosFilevault) hasInstitutionalRecoveryKey() (bool, error) {
92+
// fdesetup hasinstitutionalrecoverykey outputs "true" or "false"
93+
output, err := m.runFdesetup("hasinstitutionalrecoverykey")
94+
if err != nil {
95+
return false, err
96+
}
97+
return output == "true", nil
98+
}
99+
100+
func (m *mqlMacosFilevault) users() ([]any, error) {
101+
// fdesetup list outputs lines like:
102+
// "user1,85632A00-1234-5678-ABCD-123456789ABC"
103+
// "user2,95632A00-1234-5678-ABCD-123456789ABC"
104+
output, err := m.runFdesetup("list")
105+
if err != nil {
106+
return nil, err
107+
}
108+
109+
if output == "" {
110+
return []any{}, nil
111+
}
112+
113+
var users []any
114+
for _, line := range strings.Split(output, "\n") {
115+
line = strings.TrimSpace(line)
116+
if line == "" {
117+
continue
118+
}
119+
// Each line is "username,UUID" — extract the username
120+
parts := strings.SplitN(line, ",", 2)
121+
users = append(users, parts[0])
122+
}
123+
124+
return users, nil
125+
}

providers/os/resources/macos_gatekeeper.go

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,54 @@
44
package resources
55

66
import (
7-
"io"
7+
"errors"
88
"strings"
9+
"sync"
910

10-
"go.mondoo.com/mql/v13/providers/os/connection/shared"
11+
"go.mondoo.com/mql/v13/llx"
1112
)
1213

13-
func (m *mqlMacosGatekeeper) status() (string, error) {
14-
conn := m.MqlRuntime.Connection.(shared.Connection)
14+
type mqlMacosGatekeeperInternal struct {
15+
lock sync.Mutex
16+
fetched bool
17+
output string
18+
}
1519

16-
cmd, err := conn.RunCommand("spctl --status")
17-
if err != nil {
18-
return "", err
20+
func (m *mqlMacosGatekeeper) fetchStatus() (string, error) {
21+
if m.fetched {
22+
return m.output, nil
23+
}
24+
m.lock.Lock()
25+
defer m.lock.Unlock()
26+
if m.fetched {
27+
return m.output, nil
1928
}
2029

21-
data, err := io.ReadAll(cmd.Stdout)
30+
res, err := NewResource(m.MqlRuntime, "command", map[string]*llx.RawData{
31+
"command": llx.StringData("spctl --status"),
32+
})
2233
if err != nil {
2334
return "", err
2435
}
36+
cmd := res.(*mqlCommand)
37+
if exit := cmd.GetExitcode(); exit.Data != 0 {
38+
return "", errors.New("spctl --status failed: " + cmd.GetStderr().Data)
39+
}
2540

2641
// spctl --status outputs:
2742
// "assessments enabled" (Gatekeeper on)
2843
// "assessments disabled" (Gatekeeper off)
29-
return strings.TrimSpace(string(data)), nil
44+
m.output = strings.TrimSpace(cmd.GetStdout().Data)
45+
m.fetched = true
46+
return m.output, nil
47+
}
48+
49+
func (m *mqlMacosGatekeeper) status() (string, error) {
50+
return m.fetchStatus()
3051
}
3152

3253
func (m *mqlMacosGatekeeper) enabled() (bool, error) {
33-
status, err := m.status()
54+
status, err := m.fetchStatus()
3455
if err != nil {
3556
return false, err
3657
}

providers/os/resources/macos_sip.go

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,61 @@
44
package resources
55

66
import (
7-
"io"
7+
"errors"
88
"strings"
9+
"sync"
910

10-
"go.mondoo.com/mql/v13/providers/os/connection/shared"
11+
"go.mondoo.com/mql/v13/llx"
1112
)
1213

13-
func (m *mqlMacosSip) status() (string, error) {
14-
conn := m.MqlRuntime.Connection.(shared.Connection)
14+
type mqlMacosSipInternal struct {
15+
lock sync.Mutex
16+
fetched bool
17+
output string
18+
}
1519

16-
cmd, err := conn.RunCommand("csrutil status")
17-
if err != nil {
18-
return "", err
20+
func (m *mqlMacosSip) fetchStatus() (string, error) {
21+
if m.fetched {
22+
return m.output, nil
23+
}
24+
m.lock.Lock()
25+
defer m.lock.Unlock()
26+
if m.fetched {
27+
return m.output, nil
1928
}
2029

21-
data, err := io.ReadAll(cmd.Stdout)
30+
res, err := NewResource(m.MqlRuntime, "command", map[string]*llx.RawData{
31+
"command": llx.StringData("csrutil status"),
32+
})
2233
if err != nil {
2334
return "", err
2435
}
36+
cmd := res.(*mqlCommand)
37+
if exit := cmd.GetExitcode(); exit.Data != 0 {
38+
return "", errors.New("csrutil status failed: " + cmd.GetStderr().Data)
39+
}
2540

2641
// csrutil status outputs:
2742
// "System Integrity Protection status: enabled."
2843
// "System Integrity Protection status: disabled."
2944
// May also include individual configuration flags on separate lines
30-
output := strings.TrimSpace(string(data))
31-
32-
// Return the first line which contains the primary status
45+
output := strings.TrimSpace(cmd.GetStdout().Data)
3346
lines := strings.SplitN(output, "\n", 2)
34-
return strings.TrimSpace(lines[0]), nil
47+
48+
m.output = strings.TrimSpace(lines[0])
49+
m.fetched = true
50+
return m.output, nil
51+
}
52+
53+
func (m *mqlMacosSip) status() (string, error) {
54+
return m.fetchStatus()
3555
}
3656

3757
func (m *mqlMacosSip) enabled() (bool, error) {
38-
status, err := m.status()
58+
status, err := m.fetchStatus()
3959
if err != nil {
4060
return false, err
4161
}
4262

43-
return strings.Contains(status, "enabled"), nil
63+
return strings.Contains(status, "status: enabled"), nil
4464
}

providers/os/resources/os.lr

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2269,18 +2269,24 @@ macos.filevault @defaults("enabled status") {
22692269
enabled() bool
22702270
// FileVault status (On, Off, Encryption in progress, Decryption in progress)
22712271
status() string
2272+
// Whether a personal recovery key exists
2273+
hasPersonalRecoveryKey() bool
2274+
// Whether an institutional recovery key exists
2275+
hasInstitutionalRecoveryKey() bool
2276+
// Users that can unlock the FileVault encrypted drive
2277+
users() []string
22722278
}
22732279

22742280
// macOS Gatekeeper application execution policy
2275-
macos.gatekeeper @defaults("enabled") {
2281+
macos.gatekeeper @defaults("enabled status") {
22762282
// Whether Gatekeeper assessments are enabled
22772283
enabled() bool
22782284
// Gatekeeper assessment status (assessments enabled, assessments disabled)
22792285
status() string
22802286
}
22812287

22822288
// macOS System Integrity Protection (SIP)
2283-
macos.sip @defaults("enabled") {
2289+
macos.sip @defaults("enabled status") {
22842290
// Whether System Integrity Protection is enabled
22852291
enabled() bool
22862292
// SIP status message

0 commit comments

Comments
 (0)