diff --git a/tables/macosrsr/rsr.go b/tables/macosrsr/rsr.go index c0a73fc..3a54c6e 100644 --- a/tables/macosrsr/rsr.go +++ b/tables/macosrsr/rsr.go @@ -75,7 +75,9 @@ func generateResults(rsrOutput RSROutput) []map[string]string { return results } -func runSwVersCmd() ([]byte, error) { +var runSwVersCmd = runSwVersCmdCommand + +func runSwVersCmdCommand() ([]byte, error) { cmd := exec.Command("/usr/bin/sw_vers", "--ProductVersionExtra") out, err := cmd.Output() if err != nil { @@ -113,7 +115,9 @@ func getSystemVersion() (SystemVersionPlist, error) { return unmarshalSystemVersionBytesToStruct(bytes) } -func readSystemVersionPlistToBytes() ([]byte, error) { +var readSystemVersionPlistToBytes = readSystemVersionPlistToBytesFile + +func readSystemVersionPlistToBytesFile() ([]byte, error) { var byteValue []byte plistFile, err := os.Open(systemVersionPath) diff --git a/tables/macosrsr/rsr_test.go b/tables/macosrsr/rsr_test.go index a346f40..ff274fb 100644 --- a/tables/macosrsr/rsr_test.go +++ b/tables/macosrsr/rsr_test.go @@ -1,15 +1,46 @@ package macosrsr import ( + "context" _ "embed" + "errors" "testing" + "github.com/osquery/osquery-go/plugin/table" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) //go:embed test_SystemVersion.plist var testSystemVersion []byte +func withReadSystemVersionPlistToBytes(t *testing.T, fn func() ([]byte, error)) { + t.Helper() + original := readSystemVersionPlistToBytes + readSystemVersionPlistToBytes = fn + t.Cleanup(func() { + readSystemVersionPlistToBytes = original + }) +} + +func withRunSwVersCmd(t *testing.T, fn func() ([]byte, error)) { + t.Helper() + original := runSwVersCmd + runSwVersCmd = fn + t.Cleanup(func() { + runSwVersCmd = original + }) +} + +func TestMacOSRsrColumns(t *testing.T) { + assert.Equal(t, []table.ColumnDefinition{ + table.TextColumn("rsr_version"), + table.TextColumn("macos_version"), + table.TextColumn("full_macos_version"), + table.TextColumn("rsr_supported"), + }, MacOSRsrColumns()) +} + func TestMacOSRsrGenerate(t *testing.T) { t.Parallel() type testData struct { @@ -146,6 +177,97 @@ func TestUnmarshalSystemVersionBytesToStruct(t *testing.T) { assert.Equal(t, expectedOutput, out, "output from unmarshalSystemVersionBytesToStruct does not match expected output") } +func TestUnmarshalSystemVersionBytesToStructInvalidPlist(t *testing.T) { + t.Parallel() + out, err := unmarshalSystemVersionBytesToStruct([]byte("not plist")) + assert.Error(t, err) + assert.Empty(t, out) +} + +func TestGetSystemVersionReadError(t *testing.T) { + withReadSystemVersionPlistToBytes(t, func() ([]byte, error) { + return nil, errors.New("read failed") + }) + + out, err := getSystemVersion() + assert.Error(t, err) + assert.Empty(t, out) + assert.ErrorContains(t, err, "readSystemVersionPlistToBytes") +} + +func TestMacOSRsrGenerateSupportedWithRsr(t *testing.T) { + withReadSystemVersionPlistToBytes(t, func() ([]byte, error) { + return testSystemVersion, nil + }) + withRunSwVersCmd(t, func() ([]byte, error) { + return []byte("(a)\n"), nil + }) + + rows, err := MacOSRsrGenerate(context.Background(), table.QueryContext{}) + require.NoError(t, err) + assert.Equal(t, []map[string]string{{ + "full_macos_version": "13.3.1 (a)", + "macos_version": "13.3.1", + "rsr_supported": "true", + "rsr_version": "(a)", + }}, rows) +} + +func TestMacOSRsrGenerateUnsupportedSkipsSwVers(t *testing.T) { + withReadSystemVersionPlistToBytes(t, func() ([]byte, error) { + return []byte(` + + +ProductVersion12.6.1 +ProductBuildVersion21G217 +`), nil + }) + swVersCalled := false + withRunSwVersCmd(t, func() ([]byte, error) { + swVersCalled = true + return []byte("(a)\n"), nil + }) + + rows, err := MacOSRsrGenerate(context.Background(), table.QueryContext{}) + require.NoError(t, err) + assert.False(t, swVersCalled) + assert.Equal(t, []map[string]string{{ + "full_macos_version": "12.6.1", + "macos_version": "12.6.1", + "rsr_supported": "false", + "rsr_version": "", + }}, rows) +} + +func TestMacOSRsrGenerateSwVersError(t *testing.T) { + withReadSystemVersionPlistToBytes(t, func() ([]byte, error) { + return testSystemVersion, nil + }) + withRunSwVersCmd(t, func() ([]byte, error) { + return nil, errors.New("sw_vers failed") + }) + + rows, err := MacOSRsrGenerate(context.Background(), table.QueryContext{}) + assert.Error(t, err) + assert.Nil(t, rows) + assert.ErrorContains(t, err, "run sw_vers command") +} + +func TestMacOSRsrGenerateCompatibilityError(t *testing.T) { + withReadSystemVersionPlistToBytes(t, func() ([]byte, error) { + return []byte(` + + +ProductVersionnot-a-version +`), nil + }) + + rows, err := MacOSRsrGenerate(context.Background(), table.QueryContext{}) + assert.Error(t, err) + assert.Nil(t, rows) + assert.ErrorContains(t, err, "rsrCompatible") +} + func TestRsrCompatible(t *testing.T) { type testData struct { input SystemVersionPlist