fix(security): harden auth flows and lock down telegram bot tool

- config: require JWT_SECRET >=32 bytes and reject the historical
  default fallback; MustInit aborts startup under an insecure config
- api: CORS now uses CORS_ALLOWED_ORIGINS allowlist with safe
  localhost defaults instead of returning Access-Control-Allow-Origin: *
- api: /api/reset-password and /api/reset-account stay public so
  recovery still works, but require an explicit confirm phrase in the
  body to block accidental and drive-by triggers
- api: drop adoptOrphanRecords so wiping the account no longer hands
  the next registrant the previous owner's wallet keys and exchange
  API credentials
- api: getTraderFromQuery now does a soft ownership check; equity-history
  is restricted to traders with show_in_competition=true and
  GetOrderFills joins on trader_id
- telegram: bot api_request tool uses a default-deny method+path
  allowlist so prompt injection cannot reach password, exchange key,
  AI provider or wallet endpoints
- ci: drop @master / @main on trivy-action and trufflehog; pin to
  released versions with a TODO to move to SHA + Dependabot
- web: reset flows send the required confirm phrase; "Forgot account"
  copy (en/zh/id) warns that wallet and exchange keys will be lost
- docker-compose: keep ./.env mount for onboarding wallet persistence
  with an inline note on the tradeoff, drop the host-exposed pprof port
This commit is contained in:
tinkle-community
2026-05-29 07:51:26 +08:00
parent 70db3f5ba3
commit 99361cb085
13 changed files with 379 additions and 86 deletions

View File

@@ -119,12 +119,25 @@ func (s *Server) handleCompetition(c *gin.Context) {
c.JSON(http.StatusOK, competition)
}
// handleEquityHistory Return rate historical data
// Query directly from database, not dependent on trader in memory (so historical data can be retrieved after restart)
// handleEquityHistory returns equity history for a trader. This endpoint is
// PUBLIC (used by the competition leaderboard), so it cannot use the
// authenticated getTraderFromQuery helper. Instead, it validates that the
// requested trader has explicitly opted into the public competition via
// show_in_competition=true. Traders without that flag are not exposed.
func (s *Server) handleEquityHistory(c *gin.Context) {
_, traderID, err := s.getTraderFromQuery(c)
if err != nil {
SafeBadRequest(c, "Invalid trader ID")
traderID := c.Query("trader_id")
if traderID == "" {
SafeBadRequest(c, "trader_id is required")
return
}
trader, err := s.store.Trader().GetByID(traderID)
if err != nil || trader == nil {
SafeNotFound(c, "Trader")
return
}
if !trader.ShowInCompetition {
// Do not leak that a private trader exists; report not found.
SafeNotFound(c, "Trader")
return
}