Complete browser extension gRPC flow
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -88,6 +89,35 @@ func TestHandleRequestFindLogins(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleRequestStatusIncludesPendingApprovalCounts(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client := fakeClient{
|
||||
status: &keepassgov1.GetSessionStatusResponse{
|
||||
Locked: false,
|
||||
EntryCount: 2,
|
||||
PendingApprovalCount: 3,
|
||||
TokenPendingApprovalCount: 1,
|
||||
},
|
||||
}
|
||||
resp := HandleRequest(context.Background(), Request{
|
||||
Action: "status",
|
||||
BearerToken: "secret",
|
||||
}, client)
|
||||
if !resp.Success {
|
||||
t.Fatalf("HandleRequest(status) success = false, error = %q", resp.Error)
|
||||
}
|
||||
if resp.Status == nil {
|
||||
t.Fatal("HandleRequest(status).Status = nil, want status")
|
||||
}
|
||||
if got := resp.Status.PendingApprovalCount; got != 3 {
|
||||
t.Fatalf("HandleRequest(status).PendingApprovalCount = %d, want 3", got)
|
||||
}
|
||||
if got := resp.Status.TokenPendingApprovalCount; got != 1 {
|
||||
t.Fatalf("HandleRequest(status).TokenPendingApprovalCount = %d, want 1", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleRequestGetLogin(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -181,6 +211,76 @@ func TestChromiumExtensionIDFromManifestKey(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestManifestSetChromiumIncludesAllOrigins(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
manifest, err := ManifestSet(BrowserChromium, "/tmp/keepassgo-browser-bridge", []string{
|
||||
"mjlnpdomnblnbblhacolncflebbgafhj",
|
||||
"ddfbfpcgdjkffmjnialjpookcoedahcn",
|
||||
"mjlnpdomnblnbblhacolncflebbgafhj",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("ManifestSet() error = %v", err)
|
||||
}
|
||||
want := []string{
|
||||
"chrome-extension://ddfbfpcgdjkffmjnialjpookcoedahcn/",
|
||||
"chrome-extension://mjlnpdomnblnbblhacolncflebbgafhj/",
|
||||
}
|
||||
if !slices.Equal(manifest.AllowedOrigins, want) {
|
||||
t.Fatalf("ManifestSet().AllowedOrigins = %#v, want %#v", manifest.AllowedOrigins, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiscoverInstalledExtensionIDsInRoot(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
root := t.TempDir()
|
||||
writeExtensionManifest(t, filepath.Join(root, "Default", "Extensions", "mjlnpdomnblnbblhacolncflebbgafhj", "1.0.0", "manifest.json"), browserExtensionName)
|
||||
writeExtensionManifest(t, filepath.Join(root, "Profile 1", "Extensions", "ddfbfpcgdjkffmjnialjpookcoedahcn", "1.2.0", "manifest.json"), browserExtensionName)
|
||||
writeExtensionManifest(t, filepath.Join(root, "Profile 2", "Extensions", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "3.4.5", "manifest.json"), "Bellagio Notes")
|
||||
writeExtensionManifest(t, filepath.Join(root, "Profile 3", "Extensions", "mjlnpdomnblnbblhacolncflebbgafhj", "1.1.0", "manifest.json"), browserExtensionName)
|
||||
|
||||
got, err := DiscoverInstalledExtensionIDsInRoot(root)
|
||||
if err != nil {
|
||||
t.Fatalf("DiscoverInstalledExtensionIDsInRoot() error = %v", err)
|
||||
}
|
||||
want := []string{
|
||||
"ddfbfpcgdjkffmjnialjpookcoedahcn",
|
||||
"mjlnpdomnblnbblhacolncflebbgafhj",
|
||||
}
|
||||
if !slices.Equal(got, want) {
|
||||
t.Fatalf("DiscoverInstalledExtensionIDsInRoot() = %#v, want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureNativeHostManifestsInstallsFirefoxAndDiscoveredChromium(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
t.Setenv("HOME", filepath.Join(tmp, "home"))
|
||||
appDir := filepath.Join(tmp, "app")
|
||||
if err := os.MkdirAll(appDir, 0o755); err != nil {
|
||||
t.Fatalf("MkdirAll(appDir) error = %v", err)
|
||||
}
|
||||
appBinaryPath := filepath.Join(appDir, "keepassgo")
|
||||
if err := os.WriteFile(appBinaryPath, []byte("#!/bin/sh\n"), 0o755); err != nil {
|
||||
t.Fatalf("WriteFile(appBinaryPath) error = %v", err)
|
||||
}
|
||||
bridgeBinaryPath := filepath.Join(appDir, "keepassgo-browser-bridge")
|
||||
if err := os.WriteFile(bridgeBinaryPath, []byte("#!/bin/sh\n"), 0o755); err != nil {
|
||||
t.Fatalf("WriteFile(bridgeBinaryPath) error = %v", err)
|
||||
}
|
||||
home := filepath.Join(tmp, "home")
|
||||
writeExtensionManifest(t, filepath.Join(home, ".config", "chromium", "Default", "Extensions", "mjlnpdomnblnbblhacolncflebbgafhj", "1.0.0", "manifest.json"), browserExtensionName)
|
||||
writeExtensionManifest(t, filepath.Join(home, ".config", "google-chrome", "Profile 7", "Extensions", "ddfbfpcgdjkffmjnialjpookcoedahcn", "1.0.0", "manifest.json"), browserExtensionName)
|
||||
|
||||
if err := EnsureNativeHostManifests(appBinaryPath); err != nil {
|
||||
t.Fatalf("EnsureNativeHostManifests() error = %v", err)
|
||||
}
|
||||
|
||||
assertManifestContainsExtension(t, filepath.Join(home, ".mozilla", "native-messaging-hosts", NativeHostName+".json"), "allowed_extensions", DefaultFirefoxExtensionID())
|
||||
assertManifestContainsExtension(t, filepath.Join(home, ".config", "chromium", "NativeMessagingHosts", NativeHostName+".json"), "allowed_origins", "chrome-extension://mjlnpdomnblnbblhacolncflebbgafhj/")
|
||||
assertManifestContainsExtension(t, filepath.Join(home, ".config", "google-chrome", "NativeMessagingHosts", NativeHostName+".json"), "allowed_origins", "chrome-extension://ddfbfpcgdjkffmjnialjpookcoedahcn/")
|
||||
}
|
||||
|
||||
type fakeClient struct {
|
||||
status *keepassgov1.GetSessionStatusResponse
|
||||
matches []*keepassgov1.BrowserLoginMatch
|
||||
@@ -188,6 +288,51 @@ type fakeClient struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func writeExtensionManifest(t *testing.T, path, name string) {
|
||||
t.Helper()
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
t.Fatalf("MkdirAll(%q) error = %v", filepath.Dir(path), err)
|
||||
}
|
||||
data, err := json.Marshal(map[string]string{"name": name})
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal(manifest %q) error = %v", path, err)
|
||||
}
|
||||
if err := os.WriteFile(path, append(data, '\n'), 0o644); err != nil {
|
||||
t.Fatalf("WriteFile(%q) error = %v", path, err)
|
||||
}
|
||||
}
|
||||
|
||||
func assertManifestContainsExtension(t *testing.T, path, field, want string) {
|
||||
t.Helper()
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile(%q) error = %v", path, err)
|
||||
}
|
||||
var manifest map[string]any
|
||||
if err := json.Unmarshal(data, &manifest); err != nil {
|
||||
t.Fatalf("Unmarshal(%q) error = %v", path, err)
|
||||
}
|
||||
valuesAny, ok := manifest[field]
|
||||
if !ok {
|
||||
t.Fatalf("manifest %q missing field %q", path, field)
|
||||
}
|
||||
valuesRaw, ok := valuesAny.([]any)
|
||||
if !ok {
|
||||
t.Fatalf("manifest %q field %q = %#v, want []any", path, field, valuesAny)
|
||||
}
|
||||
values := make([]string, 0, len(valuesRaw))
|
||||
for _, raw := range valuesRaw {
|
||||
text, ok := raw.(string)
|
||||
if !ok {
|
||||
t.Fatalf("manifest %q field %q value = %#v, want string", path, field, raw)
|
||||
}
|
||||
values = append(values, text)
|
||||
}
|
||||
if !slices.Contains(values, want) {
|
||||
t.Fatalf("manifest %q field %q = %#v, want to contain %q", path, field, values, want)
|
||||
}
|
||||
}
|
||||
|
||||
func (f fakeClient) Status(context.Context) (*keepassgov1.GetSessionStatusResponse, error) {
|
||||
if f.err != nil {
|
||||
return nil, f.err
|
||||
|
||||
Reference in New Issue
Block a user