{"record":{"id":"9e36e9c95dbbf98d","repo":"fish2018/pansou","slug":"secret-cannot-be-empty","errorCode":null,"errorMessage":"secret cannot be empty","messagePattern":"secret cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"util/jwt.go","lineNumber":22,"sourceCode":"\t\"errors\"\n\t\"time\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n)\n\n// Claims JWT载荷结构\ntype Claims struct {\n\tUsername string `json:\"username\"`\n\tjwt.RegisteredClaims\n}\n\n// GenerateToken 生成JWT token\nfunc GenerateToken(username string, secret string, expiry time.Duration) (string, error) {\n\tif username == \"\" {\n\t\treturn \"\", errors.New(\"username cannot be empty\")\n\t}\n\tif secret == \"\" {\n\t\treturn \"\", errors.New(\"secret cannot be empty\")\n\t}\n\n\texpirationTime := time.Now().Add(expiry)\n\tclaims := &Claims{\n\t\tUsername: username,\n\t\tRegisteredClaims: jwt.RegisteredClaims{\n\t\t\tExpiresAt: jwt.NewNumericDate(expirationTime),\n\t\t\tIssuedAt:  jwt.NewNumericDate(time.Now()),\n\t\t\tIssuer:    \"pansou\",\n\t\t},\n\t}\n\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)\n\treturn token.SignedString([]byte(secret))\n}\n\n// ValidateToken 验证JWT token\nfunc ValidateToken(tokenString string, secret string) (*Claims, error) {","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/util/jwt.go#L4-L40","documentation":"Sentinel validation error in GenerateToken (util/jwt.go:22): it fires when the caller passes an empty string as the JWT signing secret, so no token can be signed. It is a guard against misconfiguration, not a crypto failure; the caller (LoginHandler) should supply the configured secret before calling.","triggerScenarios":"部署时未配置或错误传入空的 JWT 密钥（如配置文件缺失、环境变量为空），用户调用登录接口触发 token 生成。","commonSituations":"JWT_SECRET env var unset; config file lacks the secret field; secret loaded after this call; empty default in config struct.","solutions":["Set the JWT secret in configuration/env before the server starts","Fail fast at startup if the secret is empty rather than at first login","Check that config loading actually populates the secret key used by LoginHandler"],"exampleFix":"// before\ntoken, err := util.GenerateToken(username, cfg.JWTSecret, expiry)\n// after\nif cfg.JWTSecret == \"\" {\n    log.Fatal(\"JWT secret is not configured\")\n}\ntoken, err := util.GenerateToken(username, cfg.JWTSecret, expiry)","handlingStrategy":"validation","validationCode":"if os.Getenv(\"JWT_SECRET\") == \"\" { log.Fatal(\"JWT_SECRET not set\") }","typeGuard":null,"tryCatchPattern":"token, err := util.GenerateToken(username, secret, expiry)\nif err != nil { log.Fatalf(\"token signing failed: %v\", err) }","preventionTips":["Fail fast at startup when the secret is missing","Load the secret before starting the HTTP server","Never commit empty/default secrets"],"tags":["jwt","config","secret","go"],"backgroundTag":"missing-env-var","analyzedSha":"beaa56133755a548ebc51b090b3816e2ae044aa6","analyzedAt":"2026-09-07T00:31:18.025Z","contentChangedAt":"2026-09-07T00:31:18.025Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}