From 3e1ce9f62cc23fb6d424e15221834e8ca69ae532 Mon Sep 17 00:00:00 2001 From: eding Date: Sun, 2 Aug 2026 02:35:51 +0200 Subject: [PATCH] fix: reseed Forgejo operational credentials --- cmd/bootstrap.go | 17 +++++++++++++++ cmd/bootstrap_test.go | 50 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/cmd/bootstrap.go b/cmd/bootstrap.go index be787b3..564c5a2 100644 --- a/cmd/bootstrap.go +++ b/cmd/bootstrap.go @@ -27,6 +27,8 @@ var bootstrapPublishAppFrom string var bootstrapMergeBootstrapPR bool var bootstrapManageNetworkBridges bool +var upsertOperationalSecret = bootstrap.UpsertOperationalSecret + var bootstrapCmd = &cobra.Command{ Use: "bootstrap", Short: "Bootstrap Talos and Flux from config or an interactive wizard.", @@ -216,11 +218,26 @@ func runBootstrap(cmd *cobra.Command, args []string) error { if err != nil { return err } + if bootstrapRegisterWebhook { + if err := seedForgejoOperationalCredentials(cfg); err != nil { + return err + } + } runner := bootstrap.Runner{Config: cfg, Mode: bootstrap.Mode(bootstrapMode), ConfirmRebuild: bootstrapYes, RegisterWebhook: bootstrapRegisterWebhook} return runner.Run() } +func seedForgejoOperationalCredentials(cfg config.Config) error { + if err := upsertOperationalSecret(cfg.SOPS.OperationalSecretsPath, cfg.SOPS.AgeKeyPath, "cicd/forgejo", "username", cfg.Git.Username); err != nil { + return fmt.Errorf("save Forgejo username for webhook registration: %w", err) + } + if err := upsertOperationalSecret(cfg.SOPS.OperationalSecretsPath, cfg.SOPS.AgeKeyPath, "cicd/forgejo", "token", cfg.Git.Token); err != nil { + return fmt.Errorf("save Forgejo token for webhook registration: %w", err) + } + return nil +} + func createForgejoRegistryToken(cfg config.Config) error { if _, err := bootstrap.ReadOperationalSecrets(cfg.SOPS.OperationalSecretsPath, cfg.SOPS.AgeKeyPath); err != nil { return err diff --git a/cmd/bootstrap_test.go b/cmd/bootstrap_test.go index ef50bf3..3342ea5 100644 --- a/cmd/bootstrap_test.go +++ b/cmd/bootstrap_test.go @@ -1,6 +1,12 @@ package cmd -import "testing" +import ( + "errors" + "strings" + "testing" + + "github.com/Pingu-Studio/MaidnCLI/internal/config" +) func TestCreateForgejoRegistryTokenRequiresConfig(t *testing.T) { originalConfigPath, originalCreate := bootstrapConfigPath, bootstrapCreateForgejoRegistryToken @@ -14,3 +20,45 @@ func TestCreateForgejoRegistryTokenRequiresConfig(t *testing.T) { t.Fatal("--create-forgejo-registry-token accepted a missing --config") } } + +func TestSeedForgejoOperationalCredentialsUsesEncryptedUpsertBoundary(t *testing.T) { + original := upsertOperationalSecret + t.Cleanup(func() { upsertOperationalSecret = original }) + cfg := config.Config{ + Git: config.GitConfig{Username: "webhook-bot", Token: "test-pat"}, + SOPS: config.SOPSConfig{OperationalSecretsPath: "secrets.sops.yaml", AgeKeyPath: "age-key.txt"}, + } + type call struct{ path, key, value string } + var calls []call + upsertOperationalSecret = func(secretsPath, ageKeyPath, path, key, value string) error { + if secretsPath != cfg.SOPS.OperationalSecretsPath || ageKeyPath != cfg.SOPS.AgeKeyPath { + t.Fatal("credential upsert used unexpected secret paths") + } + calls = append(calls, call{path, key, value}) + return nil + } + + if err := seedForgejoOperationalCredentials(cfg); err != nil { + t.Fatal(err) + } + if len(calls) != 2 || calls[0] != (call{"cicd/forgejo", "username", cfg.Git.Username}) || calls[1] != (call{"cicd/forgejo", "token", cfg.Git.Token}) { + t.Fatal("configured Forgejo credentials were not passed only to the encrypted upsert boundary") + } +} + +func TestSeedForgejoOperationalCredentialsHidesTokenOnUpsertFailure(t *testing.T) { + original := upsertOperationalSecret + t.Cleanup(func() { upsertOperationalSecret = original }) + cfg := config.Config{Git: config.GitConfig{Username: "webhook-bot", Token: "test-pat"}} + upsertOperationalSecret = func(_, _, _, key, _ string) error { + if key == "token" { + return errors.New("unavailable") + } + return nil + } + + err := seedForgejoOperationalCredentials(cfg) + if err == nil || !strings.Contains(err.Error(), "save Forgejo token for webhook registration") || strings.Contains(err.Error(), cfg.Git.Token) { + t.Fatal("credential upsert failure was not clear and token-safe") + } +}