Files
keepassgo/internal/browserbridge/bridge_test.go
T
2026-04-11 00:52:01 -07:00

183 lines
5.2 KiB
Go

package browserbridge
import (
"bytes"
"context"
"encoding/binary"
"encoding/json"
"os"
"path/filepath"
"testing"
keepassgov1 "git.julianfamily.org/keepassgo/proto/keepassgo/v1"
)
func TestReadRequestAndWriteResponse(t *testing.T) {
t.Parallel()
var input bytes.Buffer
body, err := json.Marshal(Request{
Action: "find-logins",
GRPCAddress: "127.0.0.1:47777",
BearerToken: "secret",
URL: "https://example.invalid/login",
})
if err != nil {
t.Fatalf("Marshal() error = %v", err)
}
if err := binary.Write(&input, binary.LittleEndian, uint32(len(body))); err != nil {
t.Fatalf("binary.Write() error = %v", err)
}
if _, err := input.Write(body); err != nil {
t.Fatalf("Write() error = %v", err)
}
req, err := ReadRequest(&input)
if err != nil {
t.Fatalf("ReadRequest() error = %v", err)
}
if req.Action != "find-logins" || req.BearerToken != "secret" {
t.Fatalf("ReadRequest() = %#v, want action and token preserved", req)
}
var output bytes.Buffer
if err := WriteResponse(&output, Response{Success: true, Version: "1"}); err != nil {
t.Fatalf("WriteResponse() error = %v", err)
}
var size uint32
if err := binary.Read(&output, binary.LittleEndian, &size); err != nil {
t.Fatalf("binary.Read() error = %v", err)
}
payload := make([]byte, size)
if _, err := output.Read(payload); err != nil {
t.Fatalf("Read() payload error = %v", err)
}
var resp Response
if err := json.Unmarshal(payload, &resp); err != nil {
t.Fatalf("Unmarshal() error = %v", err)
}
if !resp.Success || resp.Version != "1" {
t.Fatalf("response = %#v, want success version 1", resp)
}
}
func TestHandleRequestFindLogins(t *testing.T) {
t.Parallel()
client := fakeClient{
status: &keepassgov1.GetSessionStatusResponse{Locked: false, EntryCount: 2},
matches: []*keepassgov1.BrowserLoginMatch{
{Id: "vault-console", Title: "Vault Console", Username: "dannyocean", Url: "https://vault.example.invalid", Quality: "exact-host"},
},
}
resp := HandleRequest(context.Background(), Request{
Action: "find-logins",
BearerToken: "secret",
URL: "https://vault.example.invalid/login",
}, client)
if !resp.Success {
t.Fatalf("HandleRequest() success = false, error = %q", resp.Error)
}
if len(resp.Matches) != 1 || resp.Matches[0].ID != "vault-console" {
t.Fatalf("HandleRequest().Matches = %#v, want vault-console", resp.Matches)
}
}
func TestHandleRequestGetLogin(t *testing.T) {
t.Parallel()
client := fakeClient{
status: &keepassgov1.GetSessionStatusResponse{Locked: false, EntryCount: 1},
credential: &keepassgov1.GetBrowserCredentialResponse{
Id: "vault-console",
Username: "dannyocean",
Password: "token-1",
Url: "https://vault.example.invalid",
},
}
resp := HandleRequest(context.Background(), Request{
Action: "get-login",
BearerToken: "secret",
EntryID: "vault-console",
URL: "https://vault.example.invalid/login",
}, client)
if !resp.Success {
t.Fatalf("HandleRequest() success = false, error = %q", resp.Error)
}
if resp.Credential == nil || resp.Credential.ID != "vault-console" {
t.Fatalf("HandleRequest().Credential = %#v, want vault-console", resp.Credential)
}
}
func TestHandleRequestRequiresBearerToken(t *testing.T) {
t.Parallel()
resp := HandleRequest(context.Background(), Request{Action: "status"}, fakeClient{})
if resp.Success {
t.Fatal("HandleRequest().Success = true, want false without token")
}
}
func TestInstallManifest(t *testing.T) {
t.Parallel()
tmp := t.TempDir()
binaryPath := filepath.Join(tmp, "keepassgo-browser-bridge")
if err := os.WriteFile(binaryPath, []byte("#!/bin/sh\n"), 0o755); err != nil {
t.Fatalf("WriteFile(binary) error = %v", err)
}
path, err := InstallManifest(BrowserFirefox, binaryPath, "", filepath.Join(tmp, "firefox-host.json"))
if err != nil {
t.Fatalf("InstallManifest() error = %v", err)
}
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("ReadFile() error = %v", err)
}
var manifest NativeHostManifest
if err := json.Unmarshal(data, &manifest); err != nil {
t.Fatalf("Unmarshal() error = %v", err)
}
if manifest.Path != binaryPath {
t.Fatalf("manifest.Path = %q, want %q", manifest.Path, binaryPath)
}
if len(manifest.AllowedExtensions) != 1 || manifest.AllowedExtensions[0] != DefaultFirefoxExtensionID() {
t.Fatalf("manifest.AllowedExtensions = %#v, want default firefox extension id", manifest.AllowedExtensions)
}
}
type fakeClient struct {
status *keepassgov1.GetSessionStatusResponse
matches []*keepassgov1.BrowserLoginMatch
credential *keepassgov1.GetBrowserCredentialResponse
err error
}
func (f fakeClient) Status(context.Context) (*keepassgov1.GetSessionStatusResponse, error) {
if f.err != nil {
return nil, f.err
}
if f.status == nil {
return &keepassgov1.GetSessionStatusResponse{}, nil
}
return f.status, nil
}
func (f fakeClient) FindBrowserLogins(context.Context, string) ([]*keepassgov1.BrowserLoginMatch, error) {
if f.err != nil {
return nil, f.err
}
return f.matches, nil
}
func (f fakeClient) GetBrowserCredential(context.Context, string, string) (*keepassgov1.GetBrowserCredentialResponse, error) {
if f.err != nil {
return nil, f.err
}
if f.credential == nil {
return &keepassgov1.GetBrowserCredentialResponse{}, nil
}
return f.credential, nil
}