mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-06-06 05:51:19 +08:00
Address multiple vulnerabilities found during security review: - Remove unauthenticated POST /api/crypto/decrypt decryption oracle (route, handler, dead frontend helper) + regression test. Transport encryption is one-directional; the server never needs to decrypt arbitrary client payloads. - Redact secrets in config-update logs: handler_ai_model/handler_exchange logged %+v of decrypted requests, leaking API keys / secret keys / passphrases / private keys. Use named types shared with the log sanitizer so the masking can never drift again; extend masking to passphrase + lighter_api_key_private_key. - crypto: require a valid timestamp in DecryptPayload (a missing ts previously skipped replay protection entirely). - crypto: EncryptedString.Value() now fails closed instead of silently persisting plaintext secrets when encryption errors. - auth: per-IP token-bucket rate limiting on /login and /register against online brute-force; raise registration password minimum 6 -> 8; add dummy bcrypt compare on unknown-email login to close the user-enumeration timing channel. - IDOR: getTraderFromQuery no longer falls back to the global in-memory trader map; trader access is strictly scoped to the authenticated caller. - Bump Go 1.25.10 -> 1.25.11 to resolve reachable net/textproto and crypto/x509 stdlib advisories (govulncheck now reports 0 affecting vulnerabilities).
55 lines
1.5 KiB
Go
55 lines
1.5 KiB
Go
package api
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestIPRateLimiterBurstThenThrottle verifies that a client gets `burst`
|
|
// immediate attempts and is then throttled until tokens refill.
|
|
func TestIPRateLimiterBurstThenThrottle(t *testing.T) {
|
|
// 1 token/sec, burst of 3.
|
|
l := newIPRateLimiter(1.0, 3)
|
|
now := time.Unix(1_700_000_000, 0)
|
|
|
|
// First 3 requests in the same instant are allowed (the burst).
|
|
for i := 0; i < 3; i++ {
|
|
if !l.allow("1.2.3.4", now) {
|
|
t.Fatalf("request %d in burst should be allowed", i+1)
|
|
}
|
|
}
|
|
// 4th in the same instant is throttled.
|
|
if l.allow("1.2.3.4", now) {
|
|
t.Fatalf("request beyond burst should be throttled")
|
|
}
|
|
|
|
// After 1 second, one token refills → exactly one more request allowed.
|
|
now = now.Add(time.Second)
|
|
if !l.allow("1.2.3.4", now) {
|
|
t.Fatalf("one token should have refilled after 1s")
|
|
}
|
|
if l.allow("1.2.3.4", now) {
|
|
t.Fatalf("only one token should refill per second")
|
|
}
|
|
}
|
|
|
|
// TestIPRateLimiterIsolatesClients verifies one IP exhausting its bucket does
|
|
// not throttle a different IP.
|
|
func TestIPRateLimiterIsolatesClients(t *testing.T) {
|
|
l := newIPRateLimiter(1.0, 2)
|
|
now := time.Unix(1_700_000_000, 0)
|
|
|
|
// Exhaust IP A.
|
|
if !l.allow("10.0.0.1", now) || !l.allow("10.0.0.1", now) {
|
|
t.Fatalf("IP A burst should be allowed")
|
|
}
|
|
if l.allow("10.0.0.1", now) {
|
|
t.Fatalf("IP A should be throttled after burst")
|
|
}
|
|
|
|
// IP B is unaffected.
|
|
if !l.allow("10.0.0.2", now) {
|
|
t.Fatalf("IP B should be allowed regardless of IP A")
|
|
}
|
|
}
|