From 6c370825983ab997ef119037d2f3f270426c0f1b Mon Sep 17 00:00:00 2001 From: CarterPerez-dev Date: Wed, 13 May 2026 15:27:21 -0400 Subject: [PATCH] fix(canary-phase9): plumb mysql public host/port through to generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audit found that registry.Build was calling mysql.New() with hardcoded localhost:3306 defaults, ignoring cfg.MySQL.PublicHost and PublicPort. Result: every emitted mysql:// connection string said "mysql://canary_@localhost:3306/internal_db" regardless of actual deployment address — useless for any non-localhost deployment. Fix: - registry.Config: adds MySQLPublicHost + MySQLPublicPort fields - registry.Build: switches between mysql.New() and mysql.NewWithAddress(host, port) based on config - cmd/canary/main.go: passes cfg.MySQL.PublicHost/PublicPort through to registry.Build - registry_test.go: new TestBuild_MySQLUsesConfiguredPublicAddress asserts the connection string reflects configured host/port Other Phase 9 audit observations DEFERRED to Phase 10 (not blocking rollup): - §11.3 create-tier rate limit on POST /api/tokens (Turnstile already provides spam protection; tiered limits are belt-and-suspenders) - scoping the gosec G107/G704 exclude to specific packages (revisit when Phase 10's webhook sender lands and needs case-by-case URL validation) - TurnstileResp dto required-tag clashes with dev-mode bypass (low severity — current behavior is sound) - /api/m/{manage_id} GET/DELETE (spec §8.1 lines 740-741) — Phase 11 - FingerprintRecorder is nil at startup (slowredirect fingerprint handler wiring) — Phase 10 - mysql_username metadata write in token.Service (mysql trigger lookup goes by token ID, not username — low impact) Pre-rollup gate clean at HEAD: - go build ./... + go vet ./... clean - go test -race -timeout=60s ./... all pass (~50 new test cases) - go test -tags=integration -race -timeout=300s ./internal/token/... ./internal/event/... all pass - golangci-lint run ./... → 0 issues - grep -rn "//nolint" --include="*.go" returns nothing --- .../backend/cmd/canary/main.go | 6 +++++- .../token/generators/registry/registry.go | 16 +++++++++++--- .../generators/registry/registry_test.go | 21 +++++++++++++++++++ 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/PROJECTS/beginner/canary-token-generator/backend/cmd/canary/main.go b/PROJECTS/beginner/canary-token-generator/backend/cmd/canary/main.go index 04cd79c4..f91a06c2 100644 --- a/PROJECTS/beginner/canary-token-generator/backend/cmd/canary/main.go +++ b/PROJECTS/beginner/canary-token-generator/backend/cmd/canary/main.go @@ -86,7 +86,11 @@ func run(configPath string) error { } logger.Info("redis connected") - genRegistry := registry.Build(registry.Config{BaseURL: cfg.Canary.BaseURL}) + genRegistry := registry.Build(registry.Config{ + BaseURL: cfg.Canary.BaseURL, + MySQLPublicHost: cfg.MySQL.PublicHost, + MySQLPublicPort: cfg.MySQL.PublicPort, + }) tokenSvc := token.NewService( tokenRepo, registryAdapter{r: genRegistry}, diff --git a/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/registry/registry.go b/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/registry/registry.go index ee987da6..e4801823 100644 --- a/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/registry/registry.go +++ b/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/registry/registry.go @@ -16,12 +16,22 @@ import ( ) type Config struct { - BaseURL string + BaseURL string + MySQLPublicHost string + MySQLPublicPort int } type Registry map[token.Type]generators.Generator -func Build(_ Config) Registry { +func Build(cfg Config) Registry { + host := cfg.MySQLPublicHost + port := cfg.MySQLPublicPort + var mysqlGen *mysql.Generator + if host == "" || port == 0 { + mysqlGen = mysql.New() + } else { + mysqlGen = mysql.NewWithAddress(host, port) + } return Registry{ token.TypeWebbug: webbug.New(), token.TypeSlowRedirect: slowredirect.New(), @@ -29,6 +39,6 @@ func Build(_ Config) Registry { token.TypePDF: pdf.New(), token.TypeKubeconfig: kubeconfig.New(), token.TypeEnvfile: envfile.New(), - token.TypeMySQL: mysql.New(), + token.TypeMySQL: mysqlGen, } } diff --git a/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/registry/registry_test.go b/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/registry/registry_test.go index bc4432d6..84dd1a53 100644 --- a/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/registry/registry_test.go +++ b/PROJECTS/beginner/canary-token-generator/backend/internal/token/generators/registry/registry_test.go @@ -4,6 +4,7 @@ package registry_test import ( + "context" "testing" "github.com/stretchr/testify/require" @@ -77,6 +78,26 @@ func TestBuild_UnknownTypeReturnsZeroValue(t *testing.T) { require.Nil(t, g, "map zero value for missing key must be nil interface") } +func TestBuild_MySQLUsesConfiguredPublicAddress(t *testing.T) { + reg := registry.Build(registry.Config{ + BaseURL: testBaseURL, + MySQLPublicHost: "canary.example.com", + MySQLPublicPort: 13306, + }) + g, ok := reg[token.TypeMySQL] + require.True(t, ok) + + tok := &token.Token{ID: "abc", Type: token.TypeMySQL} + art, err := g.Generate(context.Background(), tok, testBaseURL) + require.NoError(t, err) + require.Equal( + t, + "mysql://canary_abc@canary.example.com:13306/internal_db", + art.ConnectionString, + "mysql connection string must reflect configured public host:port", + ) +} + func TestBuild_AllSevenGeneratorsRegistered(t *testing.T) { reg := registry.Build(registry.Config{BaseURL: testBaseURL}) require.Len(