fix(store): scope SetDomainStatus by project (IDOR); scheduler reuses DeriveStatus
handleCheck's error branch wrote last_check_status via an id-only UPDATE, so an authenticated caller's own valid project id paired with a foreign domain id in the URL could flip a stranger's domain to "error" even though Check itself is project-scoped and would 404/error out first. Add project_id to the WHERE clause (queries/domains.sql + generated db/domains.sql.go), thread projectID through Store/TenantStore/SchedStore SetDomainStatus, and pass pid from context at both call sites in handleCheck plus the scheduler. Also collapse checkDomain's inline status derivation in scheduler.go into a call to service.DeriveStatus, the same helper handleCheck already uses, so there's a single source of truth for "drift vs in_sync" instead of two copies that could drift apart. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
This commit is contained in:
@@ -40,11 +40,13 @@ type mockTenantStore struct {
|
||||
|
||||
setDomainTemplateErr error
|
||||
|
||||
// statusCalls records every SetDomainStatus(domainID, status) call, in
|
||||
// order, so tests can assert what the handler persisted.
|
||||
// statusCalls records every SetDomainStatus(domainID, projectID, status)
|
||||
// call, in order, so tests can assert what the handler persisted — and,
|
||||
// crucially, which projectID it scoped the write to (IDOR regression).
|
||||
statusCalls []struct {
|
||||
domainID uuid.UUID
|
||||
status string
|
||||
domainID uuid.UUID
|
||||
projectID uuid.UUID
|
||||
status string
|
||||
}
|
||||
setDomainStatusErr error
|
||||
}
|
||||
@@ -135,12 +137,14 @@ func (m *mockTenantStore) SetDomainTemplate(_ context.Context, domainID, project
|
||||
}
|
||||
|
||||
// SetDomainStatus records the call for assertion instead of actually mutating
|
||||
// m.domains — handleCheck tests only need to verify what was written.
|
||||
func (m *mockTenantStore) SetDomainStatus(_ context.Context, domainID uuid.UUID, status string) error {
|
||||
// m.domains — handleCheck tests only need to verify what was written, and
|
||||
// which projectID it was scoped to (IDOR regression coverage).
|
||||
func (m *mockTenantStore) SetDomainStatus(_ context.Context, domainID, projectID uuid.UUID, status string) error {
|
||||
m.statusCalls = append(m.statusCalls, struct {
|
||||
domainID uuid.UUID
|
||||
status string
|
||||
}{domainID, status})
|
||||
domainID uuid.UUID
|
||||
projectID uuid.UUID
|
||||
status string
|
||||
}{domainID, projectID, status})
|
||||
return m.setDomainStatusErr
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user