package identityprovider import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "github.com/gin-gonic/gin" "vpn-platform/backend/internal/store" ) func TestAdminNeverEchoesFeishuSecretAndKeepsEncryptedValue(t *testing.T) { dir := t.TempDir() t.Setenv("VPN_DATA_DIR", dir) if err := store.Init(dir); err != nil { t.Fatal(err) } gin.SetMode(gin.TestMode) server := gin.New() p := New() server.PUT("/config", func(c *gin.Context) { c.Set("username", "admin"); p.Configure(c) }) server.GET("/config", p.Status) body := []byte(`{"app_id":"cli_test","app_secret":"feishu-private-value","tenant_key":"tenant_1","enabled":false}`) resp := httptest.NewRecorder() request := httptest.NewRequest(http.MethodPut, "/config", bytes.NewReader(body)) request.RemoteAddr = "127.0.0.1:12345" request.Header.Set("X-Forwarded-Proto", "https") server.ServeHTTP(resp, request) if resp.Code != 200 || strings.Contains(resp.Body.String(), "feishu-private-value") { t.Fatalf("unsafe response: %d %s", resp.Code, resp.Body.String()) } rejected := httptest.NewRecorder() server.ServeHTTP(rejected, httptest.NewRequest(http.MethodPut, "/config", bytes.NewReader(body))) if rejected.Code != http.StatusForbidden { t.Fatal("plaintext management request accepted credentials") } cfg, err := current() if err != nil || cfg.AppSecret == "feishu-private-value" { t.Fatal("app secret stored in plaintext") } plain, err := store.DecryptSecret(cfg.AppSecret) if err != nil || plain != "feishu-private-value" { t.Fatal("app secret not recoverable") } var saved map[string]any if json.Unmarshal(resp.Body.Bytes(), &saved) != nil || saved["client_id"] == "" || saved["client_secret"] != nil { t.Fatal("client secret must remain server-side") } resp = httptest.NewRecorder() server.ServeHTTP(resp, httptest.NewRequest(http.MethodGet, "/config", nil)) if resp.Code != 200 || strings.Contains(resp.Body.String(), "feishu-private-value") || strings.Contains(resp.Body.String(), cfg.ClientSecret) { t.Fatal("secret leaked from status") } }