Add browser extension gRPC bridge
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user