* security: fix four audit findings -- env race, signing key, upload traversal, password hash
1. Race condition in os.environ (HIGH): Per-session _agent_lock didn't
prevent cross-session env writes from interleaving. Added global
_ENV_LOCK in streaming.py that serializes the entire env save/restore
block across all sessions.
2. Predictable signing key (MEDIUM): sha256(STATE_DIR) was deterministic.
Now generates a random 32-byte key on first startup and persists it to
STATE_DIR/.signing_key (chmod 600). Existing sessions invalidated on
first restart (acceptable for a security fix).
3. Upload path traversal (MEDIUM): Filename '..' survived the regex
sanitization (dots are allowed chars). Added explicit rejection of
dot-only names and safe_resolve_ws() check to verify the resolved
path stays within the workspace.
4. Weak password hashing (MEDIUM): Replaced bare SHA-256 with PBKDF2-
SHA256 (600k iterations per OWASP). Uses stdlib hashlib.pbkdf2_hmac,
no new dependencies. Note: existing passwords must be re-set after
this change (hash format changed).
Closes#106
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: use random signing key as PBKDF2 salt (replaces predictable STATE_DIR salt)
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>