Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
38 changes: 30 additions & 8 deletions tables/mdm/mdm.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,16 @@ func MDMInfoGenerate(
) ([]map[string]string, error) {
fs := utils.OSFileSystem{}
// There might not be any profiles installed, but we still care if the device is DEP capable, so discard the error
profiles, _ := getMDMProfile()
profiles, _ := getMDMProfileFunc()

depEnrolled, userApproved := "unknown", "unknown"
status, err := getMDMProfileStatus(fs)
status, err := getMDMProfileStatusFunc(fs)
if err == nil { // only supported on 10.13.4+
depEnrolled = strconv.FormatBool(status.DEPEnrolled)
userApproved = strconv.FormatBool(status.UserApproved)
}

depstatus := getDEPStatus(status, fs)
depstatus := getDEPStatusFunc(status, fs)
depCapable := strconv.FormatBool(depstatus.DEPCapable)

var enrollProfileItems []profileItem
Expand Down Expand Up @@ -142,9 +142,12 @@ func MDMInfoGenerate(
return results, nil
}

var getMDMProfileFunc = getMDMProfile
var getMDMProfileStatusFunc = getMDMProfileStatus
var getDEPStatusFunc = getDEPStatus

func getMDMProfile() (*profilesOutput, error) {
cmd := exec.Command("/usr/bin/profiles", "-L", "-o", "stdout-xml")
out, err := cmd.Output()
out, err := runProfilesListCmd()
if err != nil {
return nil, errors.Wrap(err, "calling /usr/bin/profiles to get MDM profile payload")
}
Expand All @@ -157,19 +160,38 @@ func getMDMProfile() (*profilesOutput, error) {
return &profiles, nil
}

var runProfilesListCmd = func() ([]byte, error) {
cmd := exec.Command("/usr/bin/profiles", "-L", "-o", "stdout-xml")
return cmd.Output()
}

func getMDMProfileStatus(fs utils.FileSystem) (profileStatus, error) {
if !utils.FileExists(fs, "/usr/bin/profiles") {
if _, err := fs.Stat("/usr/bin/profiles"); err != nil {
return profileStatus{}, errors.New("mdm: /usr/bin/profiles does not exist")
}
cmd := exec.Command("/usr/bin/profiles", "status", "-type", "enrollment")
out, err := cmd.Output()
out, err := runProfilesStatusCmd()
if err != nil {
return profileStatus{}, errors.Wrap(
err,
"calling /usr/bin/profiles to get MDM profile status",
)
}
return parseMDMProfileStatus(out)
}

var runProfilesStatusCmd = func() ([]byte, error) {
cmd := exec.Command("/usr/bin/profiles", "status", "-type", "enrollment")
return cmd.Output()
}

func parseMDMProfileStatus(out []byte) (profileStatus, error) {
lines := bytes.Split(out, []byte("\n"))
if len(lines) < 2 {
return profileStatus{}, errors.Errorf(
"mdm: could not split the DEP Enrollment status %s",
string(out),
)
}
depEnrollmentParts := bytes.SplitN(lines[0], []byte(":"), 2)
if len(depEnrollmentParts) < 2 {
return profileStatus{}, errors.Errorf(
Expand Down
178 changes: 161 additions & 17 deletions tables/mdm/mdm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package mdm

import (
"context"
"errors"
"fmt"
"os"
"testing"
Expand All @@ -12,6 +13,51 @@ import (
"github.com/stretchr/testify/assert"
)

func withMDMProfileFunc(t *testing.T, fn func() (*profilesOutput, error)) {
t.Helper()
original := getMDMProfileFunc
getMDMProfileFunc = fn
t.Cleanup(func() {
getMDMProfileFunc = original
})
}

func withMDMProfileStatusFunc(t *testing.T, fn func(utils.FileSystem) (profileStatus, error)) {
t.Helper()
original := getMDMProfileStatusFunc
getMDMProfileStatusFunc = fn
t.Cleanup(func() {
getMDMProfileStatusFunc = original
})
}

func withDEPStatusFunc(t *testing.T, fn func(profileStatus, utils.FileSystem) depStatus) {
t.Helper()
original := getDEPStatusFunc
getDEPStatusFunc = fn
t.Cleanup(func() {
getDEPStatusFunc = original
})
}

func withRunProfilesListCmd(t *testing.T, fn func() ([]byte, error)) {
t.Helper()
original := runProfilesListCmd
runProfilesListCmd = fn
t.Cleanup(func() {
runProfilesListCmd = original
})
}

func withRunProfilesStatusCmd(t *testing.T, fn func() ([]byte, error)) {
t.Helper()
original := runProfilesStatusCmd
runProfilesStatusCmd = fn
t.Cleanup(func() {
runProfilesStatusCmd = original
})
}

// TestMDMInfoColumns tests if the MDMInfoColumns function returns the correct columns
func TestMDMInfoColumns(t *testing.T) {
columns := MDMInfoColumns()
Expand All @@ -36,38 +82,136 @@ func TestMDMInfoColumns(t *testing.T) {

// TestMDMInfoGenerate tests the MDMInfoGenerate function
func TestMDMInfoGenerate(t *testing.T) {
ctx := context.Background()
queryContext := table.QueryContext{}
withMDMProfileFunc(t, func() (*profilesOutput, error) {
return &profilesOutput{ComputerLevel: []profilePayload{{
ProfileIdentifier: "com.example.mdm",
ProfileInstallDate: "2026-01-01 00:00:00 +0000",
ProfileItems: []profileItem{
{
PayloadType: "com.apple.mdm",
PayloadContent: &payloadContent{
AccessRights: 8191,
CheckInURL: "https://mdm.example.com/checkin",
ServerURL: "https://mdm.example.com/server",
Topic: "com.apple.mgmt.External.example",
IdentityCertificateUUID: "identity-uuid",
SignMessage: true,
},
},
{PayloadType: "com.apple.security.scep"},
},
}}}, nil
})
withMDMProfileStatusFunc(t, func(fs utils.FileSystem) (profileStatus, error) {
return profileStatus{DEPEnrolled: true, UserApproved: true}, nil
})
withDEPStatusFunc(t, func(status profileStatus, fs utils.FileSystem) depStatus {
return depStatus{DEPCapable: true}
})

results, err := MDMInfoGenerate(context.Background(), table.QueryContext{})
assert.NoError(t, err)
assert.Equal(t, []map[string]string{{
"enrolled": "true",
"server_url": "https://mdm.example.com/server",
"checkin_url": "https://mdm.example.com/checkin",
"access_rights": "8191",
"install_date": "2026-01-01 00:00:00 +0000",
"payload_identifier": "com.example.mdm",
"sign_message": "true",
"topic": "com.apple.mgmt.External.example",
"identity_certificate_uuid": "identity-uuid",
"installed_from_dep": "true",
"user_approved": "true",
"has_scep_payload": "true",
"dep_capable": "true",
}}, results)
}

results, err := MDMInfoGenerate(ctx, queryContext)
func TestMDMInfoGenerateUnenrolledWhenProfileMissing(t *testing.T) {
withMDMProfileFunc(t, func() (*profilesOutput, error) {
return nil, errors.New("profiles failed")
})
withMDMProfileStatusFunc(t, func(fs utils.FileSystem) (profileStatus, error) {
return profileStatus{}, errors.New("status unsupported")
})
withDEPStatusFunc(t, func(status profileStatus, fs utils.FileSystem) depStatus {
return depStatus{}
})

results, err := MDMInfoGenerate(context.Background(), table.QueryContext{})
assert.NoError(t, err)
assert.NotNil(t, results)
assert.Equal(t, []map[string]string{{
"enrolled": "false",
"dep_capable": "false",
}}, results)
}

// TestGetMDMProfile tests the getMDMProfile function
func TestGetMDMProfile(t *testing.T) {
withRunProfilesListCmd(t, func() ([]byte, error) {
return []byte(`<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>_computerlevel</key><array>
<dict>
<key>ProfileIdentifier</key><string>com.example.mdm</string>
<key>ProfileInstallDate</key><string>2026-01-01</string>
<key>ProfileItems</key><array/>
</dict>
</array>
</dict></plist>`), nil
})

profiles, err := getMDMProfile()
assert.NoError(t, err)
assert.Equal(t, "com.example.mdm", profiles.ComputerLevel[0].ProfileIdentifier)
}

func TestGetMDMProfileErrors(t *testing.T) {
t.Run("command error", func(t *testing.T) {
withRunProfilesListCmd(t, func() ([]byte, error) {
return nil, errors.New("profiles failed")
})
profiles, err := getMDMProfile()
assert.Error(t, err)
assert.Nil(t, profiles)
assert.ErrorContains(t, err, "calling /usr/bin/profiles")
})

// Since profiles isn't present on non-macOS, the test should handle both cases
if err == nil {
assert.NotNil(t, profiles)
} else {
t.Run("invalid plist", func(t *testing.T) {
withRunProfilesListCmd(t, func() ([]byte, error) {
return []byte("not plist"), nil
})
profiles, err := getMDMProfile()
assert.Error(t, err)
}
assert.Nil(t, profiles)
assert.ErrorContains(t, err, "unmarshal profiles output")
})
}

// TestGetMDMProfileStatus tests the getMDMProfileStatus function
func TestGetMDMProfileStatus(t *testing.T) {
withRunProfilesStatusCmd(t, func() ([]byte, error) {
return []byte("Enrolled via DEP: Yes\nMDM enrollment: User Approved\n"), nil
})
fs := utils.MockFileSystem{FileExists: true, Err: nil}
status, err := getMDMProfileStatus(fs)

// Since the status is only supported on 10.13.4+, the test should handle both cases
if err == nil {
assert.NotNil(t, status)
} else {
assert.Error(t, err)
}
assert.NoError(t, err)
assert.Equal(t, profileStatus{DEPEnrolled: true, UserApproved: true}, status)
}

func TestParseMDMProfileStatusErrors(t *testing.T) {
_, err := parseMDMProfileStatus([]byte("Enrolled via DEP Yes\nMDM enrollment: User Approved\n"))
assert.Error(t, err)
assert.ErrorContains(t, err, "could not split the DEP Enrollment source")

_, err = parseMDMProfileStatus([]byte("Enrolled via DEP: Yes\nMDM enrollment User Approved\n"))
assert.Error(t, err)
assert.ErrorContains(t, err, "could not split the DEP Enrollment status")

_, err = parseMDMProfileStatus([]byte("Enrolled via DEP: Yes"))
assert.Error(t, err)
assert.ErrorContains(t, err, "could not split the DEP Enrollment status")
}

// TestGetDEPStatus tests the getDEPStatus function
Expand Down
Loading