183 lines
5.2 KiB
Go
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
|
|
}
|