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

@@ -46,7 +46,13 @@ export function LoginPage() {
const handleResetAccount = async () => {
if (!window.confirm(t('forgotAccountConfirm', language))) return
try {
const res = await fetch('/api/reset-account', { method: 'POST' })
const res = await fetch('/api/reset-account', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
// Server-side guard against accidental/drive-by triggers.
// Phrase must match handler_user.go resetAccountConfirmPhrase.
body: JSON.stringify({ confirm: 'I_UNDERSTAND_THIS_DELETES_EVERYTHING' }),
})
if (res.ok) {
localStorage.removeItem('auth_token')
localStorage.removeItem('auth_user')

View File

@@ -269,6 +269,9 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
body: JSON.stringify({
email,
new_password: newPassword,
// Server-side guard against accidental/drive-by triggers.
// Phrase must match handler_user.go resetPasswordConfirmPhrase.
confirm: 'I_UNDERSTAND_THIS_RESETS_MY_PASSWORD',
}),
})

View File

@@ -494,7 +494,7 @@ export const translations = {
loginNow: 'Sign in now',
forgotPassword: 'Forgot password?',
forgotAccount: 'Forgot account?',
forgotAccountConfirm: 'This will clear all account data and allow you to register a new account. Continue?',
forgotAccountConfirm: '⚠️ This will permanently delete EVERYTHING: users, traders, strategies, AI model API keys, exchange API keys, and your CLAW402 wallet. Export anything you need to keep (especially wallet private keys) BEFORE continuing. Re-registration will NOT restore them. Continue?',
forgotAccountSuccess: 'Account reset successful! You can now register a new account.',
rememberMe: 'Remember me',
resetPassword: 'Reset Password',
@@ -1824,7 +1824,7 @@ export const translations = {
loginNow: '立即登录',
forgotPassword: '忘记密码?',
forgotAccount: '忘记账户?',
forgotAccountConfirm: '这将清除所有账户数据,允许您重新注册新账户。是否继续?',
forgotAccountConfirm: '⚠️ 这将永久删除全部数据用户、Trader、策略、AI 模型 API Key、交易所 API Key以及您的 CLAW402 钱包。请务必在继续前导出需要保留的内容(尤其是钱包私钥)。重新注册不会恢复任何数据。确定要继续',
forgotAccountSuccess: '账户已重置!现在可以注册新账户了。',
rememberMe: '记住我',
resetPassword: '重置密码',
@@ -3089,7 +3089,7 @@ export const translations = {
loginNow: 'Masuk sekarang',
forgotPassword: 'Lupa kata sandi?',
forgotAccount: 'Lupa akun?',
forgotAccountConfirm: 'Ini akan menghapus semua data akun dan memungkinkan Anda mendaftar akun baru. Lanjutkan?',
forgotAccountConfirm: '⚠️ Ini akan MENGHAPUS PERMANEN semua data: pengguna, trader, strategi, kunci API model AI, kunci API bursa, dan dompet CLAW402 Anda. Ekspor apa pun yang ingin Anda simpan (terutama kunci privat dompet) SEBELUM melanjutkan. Pendaftaran ulang TIDAK akan memulihkannya. Lanjutkan?',
forgotAccountSuccess: 'Akun berhasil direset! Anda sekarang dapat mendaftar akun baru.',
rememberMe: 'Ingat saya',
resetPassword: 'Reset Kata Sandi',