6 Commits
Author SHA1 Message Date
zeekayandHanzo Dev d034c38050 fix(auth): reject non-canonical base64 in bearer tokens
Go's base64 decoders are lenient in two independent ways, and both JWT
verifiers used them raw, so one credential validated under several spellings.

The unused low bits of the final quantum are ignored: an HMAC-SHA256
signature is 32 bytes in 43 base64url characters, leaving 2 slack bits and
so 4 accepted strings per admin token; an RSA-2048 signature is 256 bytes in
342 characters, leaving 4 slack bits and so 16 per IAM token. Separately,
\r and \n are skipped wherever they appear, which Encoding.Strict() does not
close -- so Strict alone would not have been a fix.

Every string-keyed structure over a credential silently splits under that: a
denylist misses the spellings it was not handed, an audit log reads one actor
as several, a rate limiter multiplies the budget by the class size.

pkg/token holds the one canonical encoding. Decode round-trips rather than
enumerating failure modes -- a segment is canonical exactly when it is the
encoding of the bytes it decodes to -- and rejects rather than normalizes.
token.ID is the hash to key revocation, audit and rate limiting on, so the
identity is never the submitted string.

Both verifiers now decode every segment through it. JWKS n/e stays lenient on
purpose: public key material from the trusted IAM endpoint, where every
spelling is the same key.

Also in pkg/admin: sign returns raw MAC bytes instead of text, dropping an
encode-then-decode round trip in the verify path; the header is now decoded
and required to declare HS256, closing alg confusion under future refactor;
and a comment referencing a long-deleted hashPassword is gone.

Tests: exactly one spelling authenticates on both paths; revocation keyed on
token.ID holds across all four; CR/LF, padding, standard alphabet and empty
segments rejected. TestValidateToken_TamperedSignature substituted a fixed
'X' that was a no-op whenever the token already ended in one -- a 1-in-64
flake that this bug made a 1-in-16 one -- now derived from the token.

LLM.md: admin auth section corrected (bcrypt cost 12, not SHA-256+salt; no
ADMIN_* env vars exist; middleware strips those headers rather than setting
them; package is not mounted).

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-01 18:13:16 -07:00
zeekay d95bbdfc03 fix: gofmt -s across repo (CI format check) 2026-06-02 11:37:59 -07:00
zeekay f7a8d7a9a2 security: bcrypt admin auth, JWKS per-kid cache, gRPC reflection dev-only, audit bounded+rand IDs, credential hashing 2026-04-07 20:14:39 -07:00
zeekay 4532862f22 fix: use query-param format for crypto bars (sandbox single path 404s)
Same issue as snapshots — Alpaca sandbox returns 404 for
/v1beta3/crypto/us/{sym}/bars but works with
/v1beta3/crypto/us/bars?symbols={sym}. Also handle the wrapped
response format {"bars":{"BTC/USD":[...]}} vs stock {"bars":[...]}.
2025-12-24 00:57:08 -07:00
zeekay 664e1496aa security: fix all critical findings from red team review
- JWT signature comparison now uses hmac.Equal (timing-safe)
- Password hashing upgraded to 100k iterations (PBKDF2-like)
- Remove hardcoded JWT secret fallback (random generated if unset)
- Remove BROKER_DEV_MODE auth bypass entirely
- Password comparison uses subtle.ConstantTimeCompare
- Remove wildcard CORS from SSE endpoint
- Update all tests to use API key auth instead of dev mode bypass
2025-12-24 10:56:15 -07:00
zeekay 4585060a98 feat: add admin JWT auth with HMAC-SHA256 signing
Password hashing with SHA-256 + salt (never plaintext). JWT tokens
for admin dashboard access with role-based claims. Middleware for
protected route groups. Comprehensive tests for auth flows, token
validation, expiration, and tampering detection.
2025-12-04 09:42:17 -08:00