From c1546f6687e709c585013edd3fc5e86aed32301f Mon Sep 17 00:00:00 2001 From: Delicious233 <101502465+DeliciousBuding@users.noreply.github.com> Date: Mon, 17 Aug 2026 18:04:25 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(hub):=20=E6=A0=A1=E9=AA=8C=20TokenDance?= =?UTF-8?q?=20ID=20client=5Fsecret=20=E5=BC=BA=E5=BA=A6=E5=B9=B6=E6=B8=85?= =?UTF-8?q?=E9=99=A4=E7=A7=8D=E5=AD=90=20secret=20=E6=9A=B4=E9=9C=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit validateTokenDanceID 对 ClientSecret 只查非空,不校验长度或弱值。.env.example 注释泄露种子 SQL 的 client_secret 值。现对齐 JWT secret 的 32 字符下限 + isKnownWeakSecret 黑名单,新增 agenthub-dev-secret 前缀族,拒绝种子值;.env.example 注释改为指向 setup 脚本输出。补两条测试覆盖短 secret 与种子 secret 拒收。 Co-authored-by: Cursor --- hub-server/.env.example | 2 +- hub-server/internal/config/config_test.go | 66 +++++++++++++++++++ hub-server/internal/config/config_validate.go | 15 +++++ 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/hub-server/.env.example b/hub-server/.env.example index e9b80d291..1c1ebf028 100644 --- a/hub-server/.env.example +++ b/hub-server/.env.example @@ -41,7 +41,7 @@ AGENTHUB_AGENT_TEAM_MAX_TEAM_RUN_BUDGET_USAGE_PCT=95 # d. Save the returned client_id and client_secret below # 3. If TokenDance ID is NOT running, use the seed SQL: # sqlite3 ../tokendance-id/data/tokendance.db < scripts/seed-tokendance-client.sql -# The seed uses client_id=agenthub-desktop with secret=agenthub-dev-secret-change-me +# The seed uses client_id=agenthub-desktop; copy the generated secret from the setup script output. AGENTHUB_TOKENDANCE_ID_ISSUER_URL=http://localhost:3000 # AGENTHUB_TOKENDANCE_ID_JWKS_URI= ← auto-derived from issuer_url + /oidc/jwks diff --git a/hub-server/internal/config/config_test.go b/hub-server/internal/config/config_test.go index 9dc82a07d..764395601 100644 --- a/hub-server/internal/config/config_test.go +++ b/hub-server/internal/config/config_test.go @@ -735,6 +735,72 @@ func TestValidateTokenDanceIDRequiresRedirectURI(t *testing.T) { } } +func TestValidateTokenDanceIDRejectsShortClientSecret(t *testing.T) { + cfg := &Config{ + DB: DBConfig{ + Host: "localhost", + Port: 5432, + User: "agenthub", + Name: "agenthub", + MaxOpenConns: 2, + MaxIdleConns: 1, + ConnMaxLifetime: 30 * time.Minute, + ConnMaxIdleTime: 5 * time.Minute, + }, + Redis: RedisConfig{Host: "localhost", Port: 6379}, + JWT: JWTConfig{ + Secret: "strong-agenthub-secret-padded-to-minimum-32-chars..", + }, + TokenDanceID: TokenDanceIDConfig{ + IssuerURL: "https://id.example", + ClientID: "agenthub-client", + ClientSecret: "short-but-present-secret", + RedirectURI: "http://127.0.0.1/callback", + }, + } + + err := cfg.Validate() + if err == nil { + t.Fatal("expected short TokenDance ID client secret to be rejected") + } + if !strings.Contains(err.Error(), "tokendance_id.client_secret too short") { + t.Fatalf("Validate() error = %q, want tokendance_id.client_secret too short", err.Error()) + } +} + +func TestValidateTokenDanceIDRejectsSeedSecret(t *testing.T) { + cfg := &Config{ + DB: DBConfig{ + Host: "localhost", + Port: 5432, + User: "agenthub", + Name: "agenthub", + MaxOpenConns: 2, + MaxIdleConns: 1, + ConnMaxLifetime: 30 * time.Minute, + ConnMaxIdleTime: 5 * time.Minute, + }, + Redis: RedisConfig{Host: "localhost", Port: 6379}, + JWT: JWTConfig{ + Secret: "strong-agenthub-secret-padded-to-minimum-32-chars..", + }, + TokenDanceID: TokenDanceIDConfig{ + IssuerURL: "https://id.example", + ClientID: "agenthub-client", + ClientSecret: "agenthub-dev-secret-change-me-padded-to-32!!", + RedirectURI: "http://127.0.0.1/callback", + }, + } + + err := cfg.Validate() + if err == nil { + t.Fatal("expected seed SQL TokenDance ID client secret to be rejected") + } + if !strings.Contains(err.Error(), "tokendance_id.client_secret must be a strong") { + t.Fatalf("Validate() error = %q, want tokendance_id.client_secret must be a strong", err.Error()) + } +} + // --- DSN / Addr edge cases --- func TestDBConfigDSNZeroValues(t *testing.T) { diff --git a/hub-server/internal/config/config_validate.go b/hub-server/internal/config/config_validate.go index 9603fb728..4b8471d74 100644 --- a/hub-server/internal/config/config_validate.go +++ b/hub-server/internal/config/config_validate.go @@ -84,6 +84,7 @@ var knownHardcodedSecrets = []string{ "password", "1234567890123456", "aaaaaaaaaaaaaaaa", + "agenthub-dev-secret-change-me", } // weakSecretPrefixes blocks the publicly-documented placeholder families. @@ -97,9 +98,12 @@ var knownHardcodedSecrets = []string{ // family (e.g. change-me-production-min-length-32-chars, 41 chars). // Both PG_PASSWORD and JWT_SECRET in .env.example used this prefix; the // JWT variant was long enough to pass the length gate before this entry. +// - "agenthub-dev-secret": the seed SQL OIDC client_secret family +// (agenthub-dev-secret-change-me and derivatives). var weakSecretPrefixes = []string{ "dev-secret-change-in-production", "change-me-production", + "agenthub-dev-secret", } // isKnownWeakSecret reports whether the given secret is a hardcoded default @@ -165,6 +169,17 @@ func (c *Config) validateTokenDanceID() error { if c.TokenDanceID.RedirectURI == "" { return fmt.Errorf("tokendance_id.redirect_uri is required when tokendance_id.client_id is set") } + // OIDC client_secret must meet the same strength bar as JWT secrets: + // 32-char minimum and no known hardcoded/placeholder values. The + // seed SQL default (agenthub-dev-secret-change-me) and the + // .env.example documented placeholders must be rejected so an + // operator cannot accidentally run production with the seed value. + if len(c.TokenDanceID.ClientSecret) < 32 { + return fmt.Errorf("tokendance_id.client_secret too short: minimum 32 characters required (got %d)", len(c.TokenDanceID.ClientSecret)) + } + if isKnownWeakSecret(c.TokenDanceID.ClientSecret) { + return errors.New("tokendance_id.client_secret must be a strong, non-default value; the seed SQL default and documented dev placeholders are rejected") + } } return nil } From 0e032eeb2f91f907ca377d048fc46b8a3bcd5cee Mon Sep 17 00:00:00 2001 From: Delicious233 <101502465+DeliciousBuding@users.noreply.github.com> Date: Mon, 17 Aug 2026 18:42:05 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(hub):=20=E6=9E=84=E9=80=A0=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=20secret=20=E5=80=BC=E9=81=BF=E5=85=8D=E5=AD=97?= =?UTF-8?q?=E9=9D=A2=E9=87=8F=E6=B3=84=E6=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- hub-server/internal/config/config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hub-server/internal/config/config_test.go b/hub-server/internal/config/config_test.go index 764395601..1c1fb7cc7 100644 --- a/hub-server/internal/config/config_test.go +++ b/hub-server/internal/config/config_test.go @@ -787,7 +787,7 @@ func TestValidateTokenDanceIDRejectsSeedSecret(t *testing.T) { TokenDanceID: TokenDanceIDConfig{ IssuerURL: "https://id.example", ClientID: "agenthub-client", - ClientSecret: "agenthub-dev-secret-change-me-padded-to-32!!", + ClientSecret: weakSecretPrefixes[len(weakSecretPrefixes)-1] + strings.Repeat("x", 32), RedirectURI: "http://127.0.0.1/callback", }, }