fix(tradein): scope GET /history to caller, close cross-pilot data leak (#656) #670
No reviewers
Labels
No labels
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
Fable 5 ревью
feedback/max
generative
GG-форсайт
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
вторичка
ИРД
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#670
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/tradein-656-history-scope"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
P1 security: GET /history returned latest estimates of ALL users -> cross-pilot leak.
Adds trade_in_estimates.created_by (migration 083), populated from X-Authenticated-User on estimate creation. /history now: non-admin forced to own rows (legacy NULL rows excluded, no owner); admin sees all or filters via ?account=; 401 if header missing, 403 if user not in roles.yaml. _empty_estimate rows also tagged.
Decisions: created_by nullable; index (created_by, created_at DESC).
Open question: kopylov is role pilot -> sees only own history. If he needs the cross-account admin view, promote him to admin in auth/roles.yaml (out of scope here).
Tests: tests/test_history_scope.py (5 cases) + estimator regression (15) green; ruff clean.
Milestone 6 (Praktika demo). Closes #656
Security fix #656 (cross-pilot data leak in GET /history). Verified: (1) no SQL injection - f-string interpolates only literal WHERE clauses, all values bound (:owner/:account/:limit); (2) fail-safe scoping - non-admin -> own rows only, attacker ?account ignored (test_non_admin_account_param_is_ignored), unknown user -> 403, missing header -> 401; (3) both INSERT paths (estimator.py:1016,2048) set created_by, no orphan write; (4) role==admin matches Literal[admin,pilot]; (5) migration 083 idempotent + indexed, BEGIN/COMMIT. Tests cover all branches incl. attack vector. NOTE (non-blocking): (created_by, created_at) index does not optimally serve admin no-filter ORDER BY created_at path - fine for small pilot table. APPROVE.