diff --git a/.github/workflows/test-coverage.yml b/.github/workflows/test-coverage.yml index 222d3d0..0a5a8e5 100644 --- a/.github/workflows/test-coverage.yml +++ b/.github/workflows/test-coverage.yml @@ -14,3 +14,12 @@ jobs: uses: vladopajic/go-test-coverage@v2 with: config: ./.testcoverage.yml + test-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v6 + with: + go-version-file: go.mod + - name: run tests + run: go test ./... diff --git a/pkg/client/client.go b/pkg/client/client.go index 2becf38..63e011a 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -6,6 +6,8 @@ import ( osq "github.com/macadmins/osquery-extension/pkg/utils" ) +const launchServicesPlistPath = "Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist" + type Client struct { Runner osq.CmdRunner CurrentUser string @@ -13,6 +15,7 @@ type Client struct { } type Option func(*Client) +type currentUserLookup func() (*user.User, error) func WithCurrentUser(currentUser string) Option { return func(c *Client) { @@ -27,6 +30,10 @@ func WithPlistLocation(plistLocation string) Option { } func NewClient(opts ...Option) (Client, error) { + return newClient(user.Current, opts...) +} + +func newClient(lookupCurrentUser currentUserLookup, opts ...Option) (Client, error) { c := Client{} c.Runner = osq.NewRunner().Runner for _, opt := range opts { @@ -34,7 +41,7 @@ func NewClient(opts ...Option) (Client, error) { } if c.CurrentUser == "" { - currentUser, err := user.Current() + currentUser, err := lookupCurrentUser() if err != nil { return c, err } @@ -42,8 +49,12 @@ func NewClient(opts ...Option) (Client, error) { } if c.PlistLocation == "" { - c.PlistLocation = "/Users/" + c.CurrentUser + "/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist" + c.PlistLocation = defaultLaunchServicesPlistLocation(c.CurrentUser) } return c, nil } + +func defaultLaunchServicesPlistLocation(currentUser string) string { + return "/Users/" + currentUser + "/" + launchServicesPlistPath +} diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index 86b648f..a300394 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -1,6 +1,7 @@ package client import ( + "errors" "os/user" "testing" @@ -20,15 +21,16 @@ func TestNewClientWithCurrentUser(t *testing.T) { client, err := NewClient(WithCurrentUser(expectedUser)) assert.NoError(t, err, "NewClient should not return an error") assert.Equal(t, expectedUser, client.CurrentUser, "CurrentUser should be set to the provided value") + assert.Equal(t, "/Users/testuser/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist", client.PlistLocation, "PlistLocation should use the provided current user") } func TestNewClientDefaultCurrentUser(t *testing.T) { - currentUser, err := user.Current() - assert.NoError(t, err, "user.Current should not return an error") - - client, err := NewClient() + client, err := newClient(func() (*user.User, error) { + return &user.User{Username: "systemuser"}, nil + }) assert.NoError(t, err, "NewClient should not return an error") - assert.Equal(t, currentUser.Username, client.CurrentUser, "CurrentUser should be set to the system's current user") + assert.Equal(t, "systemuser", client.CurrentUser, "CurrentUser should be set to the system's current user") + assert.Equal(t, "/Users/systemuser/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist", client.PlistLocation, "PlistLocation should use the system's current user") } func TestNewClientWithPlistLocation(t *testing.T) { @@ -37,3 +39,30 @@ func TestNewClientWithPlistLocation(t *testing.T) { assert.NoError(t, err, "NewClient should not return an error") assert.Equal(t, expectedPlistLocation, client.PlistLocation, "PlistLocation should be set to the provided value") } + +func TestNewClientWithCurrentUserSkipsCurrentUserLookup(t *testing.T) { + client, err := newClient(func() (*user.User, error) { + return nil, errors.New("current user lookup should not be called") + }, WithCurrentUser("testuser")) + + assert.NoError(t, err, "NewClient should not return an error") + assert.Equal(t, "testuser", client.CurrentUser, "CurrentUser should be set to the provided value") + assert.Equal(t, "/Users/testuser/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist", client.PlistLocation, "PlistLocation should use the provided current user") +} + +func TestNewClientReturnsCurrentUserLookupError(t *testing.T) { + expectedErr := errors.New("current user lookup failed") + + client, err := newClient(func() (*user.User, error) { + return nil, expectedErr + }) + + assert.ErrorIs(t, err, expectedErr, "NewClient should return the current user lookup error") + assert.Empty(t, client.CurrentUser, "CurrentUser should not be set when lookup fails") +} + +func TestDefaultLaunchServicesPlistLocation(t *testing.T) { + location := defaultLaunchServicesPlistLocation("testuser") + + assert.Equal(t, "/Users/testuser/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist", location, "default LaunchServices plist location should match the legacy path") +}