Files
goclaw/internal/http/secure_cli_env_response_test.go
Duy /zuey/andGitHub c4e0bd2be5 fix(cli-credentials): stabilize git preset creation
Squash merge PR #96 after PR #92. PR checks were green; dev beta CI will be monitored after merge.
2026-05-31 11:05:47 +07:00

138 lines
4.6 KiB
Go

package http
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/google/uuid"
"github.com/nextlevelbuilder/goclaw/internal/store"
)
type fakeSecureCLIStore struct {
binary *store.SecureCLIBinary
user *store.SecureCLIUserCredential
created *store.SecureCLIBinary
}
func (s *fakeSecureCLIStore) Create(_ context.Context, b *store.SecureCLIBinary) error {
cp := *b
s.created = &cp
return nil
}
func (s *fakeSecureCLIStore) Get(context.Context, uuid.UUID) (*store.SecureCLIBinary, error) {
cp := *s.binary
return &cp, nil
}
func (s *fakeSecureCLIStore) Update(context.Context, uuid.UUID, map[string]any) error { return nil }
func (s *fakeSecureCLIStore) Delete(context.Context, uuid.UUID) error { return nil }
func (s *fakeSecureCLIStore) List(context.Context) ([]store.SecureCLIBinary, error) {
cp := *s.binary
return []store.SecureCLIBinary{cp}, nil
}
func (s *fakeSecureCLIStore) LookupByBinary(context.Context, string, *uuid.UUID, string) (*store.SecureCLIBinary, error) {
return nil, nil
}
func (s *fakeSecureCLIStore) ListEnabled(context.Context) ([]store.SecureCLIBinary, error) {
return nil, nil
}
func (s *fakeSecureCLIStore) ListForAgent(context.Context, uuid.UUID) ([]store.SecureCLIBinary, error) {
return nil, nil
}
func (s *fakeSecureCLIStore) IsRegisteredBinary(context.Context, string) (bool, error) {
return false, nil
}
func (s *fakeSecureCLIStore) GetUserCredentials(context.Context, uuid.UUID, string) (*store.SecureCLIUserCredential, error) {
cp := *s.user
return &cp, nil
}
func (s *fakeSecureCLIStore) SetUserCredentials(context.Context, uuid.UUID, string, []byte) error {
return nil
}
func (s *fakeSecureCLIStore) SetUserCredentialsTyped(context.Context, uuid.UUID, string, []byte, *string, *string) error {
return nil
}
func (s *fakeSecureCLIStore) DeleteUserCredentials(context.Context, uuid.UUID, string) error {
return nil
}
func (s *fakeSecureCLIStore) ListUserCredentials(context.Context, uuid.UUID) ([]store.SecureCLIUserCredential, error) {
cp := *s.user
return []store.SecureCLIUserCredential{cp}, nil
}
func TestSecureCLIGetSanitizesMixedEnv(t *testing.T) {
id := uuid.New()
h := NewSecureCLIHandler(&fakeSecureCLIStore{
binary: &store.SecureCLIBinary{
BaseModel: store.BaseModel{ID: id},
BinaryName: "gh",
EncryptedEnv: []byte(`{"TOKEN":"secret-token","PUBLIC_BASE_URL":{"kind":"value","value":"https://goclaw.sh"}}`),
},
}, nil)
req := httptest.NewRequest(http.MethodGet, "/v1/cli-credentials/"+id.String(), nil)
req.SetPathValue("id", id.String())
rec := httptest.NewRecorder()
h.handleGet(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
}
if strings.Contains(rec.Body.String(), "secret-token") {
t.Fatalf("sensitive env leaked in response: %s", rec.Body.String())
}
var got struct {
Env map[string]store.SecureCLIEnvResponseEntry `json:"env"`
}
if err := json.Unmarshal(rec.Body.Bytes(), &got); err != nil {
t.Fatal(err)
}
if !got.Env["TOKEN"].Masked || got.Env["TOKEN"].Value != nil {
t.Fatalf("TOKEN not masked: %#v", got.Env["TOKEN"])
}
if got.Env["PUBLIC_BASE_URL"].Value == nil || *got.Env["PUBLIC_BASE_URL"].Value != "https://goclaw.sh" {
t.Fatalf("value env not returned: %#v", got.Env["PUBLIC_BASE_URL"])
}
}
func TestSecureCLIUserCredentialsGetDoesNotReturnLegacySensitiveRaw(t *testing.T) {
binaryID := uuid.New()
h := NewSecureCLIHandler(&fakeSecureCLIStore{
user: &store.SecureCLIUserCredential{
ID: uuid.New(),
BinaryID: binaryID,
UserID: "user-1",
EncryptedEnv: []byte(`{"TOKEN":"secret-token","REGION":{"kind":"value","value":"asia-southeast1"}}`),
},
}, nil)
req := httptest.NewRequest(http.MethodGet, "/v1/cli-credentials/"+binaryID.String()+"/user-credentials/user-1", nil)
req.SetPathValue("id", binaryID.String())
req.SetPathValue("userId", "user-1")
rec := httptest.NewRecorder()
h.handleGetUserCredentials(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
}
if strings.Contains(rec.Body.String(), "secret-token") {
t.Fatalf("legacy sensitive env leaked in response: %s", rec.Body.String())
}
var got struct {
Env map[string]store.SecureCLIEnvResponseEntry `json:"env"`
}
if err := json.Unmarshal(rec.Body.Bytes(), &got); err != nil {
t.Fatal(err)
}
if !got.Env["TOKEN"].Masked || got.Env["TOKEN"].Value != nil {
t.Fatalf("TOKEN not masked: %#v", got.Env["TOKEN"])
}
if got.Env["REGION"].Value == nil || *got.Env["REGION"].Value != "asia-southeast1" {
t.Fatalf("REGION not returned: %#v", got.Env["REGION"])
}
}