feat(api): pending-changes + audit routes
Owner-only routes wired with an applyPendingChange dispatch helper covering page/project/task/ref/resource/source_doc create/update/delete. Approve and reject emit their own audit_log entries (actions already in the CHECK vocab) so the audit trail is self-contained. Documents a latent bug in security-followups.md: pending_changes.action CHECK constraint blocks 'upsert' / 'add_dependency' / 'remove_dependency' divertToPending paths in refs/resources routes when an agent at suggest tier hits them. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -36,3 +36,30 @@ Before closing Plan 1, decide whether to:
|
||||
|
||||
Owner-only authn (Plan 1) means there's only one tenant in practice today, so (b) is
|
||||
defensible for the alpha. (a) is the right long-term answer.
|
||||
|
||||
## Migration 006 (audit + pending_changes) — Plan 2 finding
|
||||
|
||||
**[HIGH] `pending_changes.action` CHECK constraint blocks extended actions emitted
|
||||
by some routes.** The table's CHECK accepts only `('create','update','delete')`,
|
||||
but the following routes call `divertToPending` with actions outside that set:
|
||||
|
||||
- `lib/api/routes/refs.js` (`POST /refs/upsert`) → `action: 'upsert'`
|
||||
- `lib/api/routes/resources.js` (`POST /resources/:id/dependencies`) → `action: 'add_dependency'`
|
||||
- `lib/api/routes/resources.js` (`DELETE /resources/:id/dependencies/:dep_id`) → `action: 'remove_dependency'`
|
||||
|
||||
Today the owner bearer token always lands in `allow` tier and these paths never
|
||||
hit `divertToPending`. The bug surfaces only when an agent at `suggest` tier
|
||||
calls one of these endpoints, in which case the `INSERT INTO pending_changes`
|
||||
fails with a CHECK violation and the request 500s.
|
||||
|
||||
**Mitigation options:**
|
||||
1. Widen the CHECK constraint to include `'upsert'`, `'add_dependency'`,
|
||||
`'remove_dependency'`. Add the matching dispatch arms in
|
||||
`lib/api/routes/pending_changes.js::applyPendingChange`.
|
||||
2. Normalize at `divertToPending` entry — collapse `'upsert'` → `'create'`,
|
||||
reject the dependency variants with `405 Method Not Allowed for agents`.
|
||||
3. Move dependency mutations to owner-only endpoints (remove `requireWrite` and
|
||||
the suggest branch) since dependencies are infra-level wiring.
|
||||
|
||||
Recommended: (1) for upsert (legitimate suggestion path) and (3) for
|
||||
add_dependency / remove_dependency (infra wiring, owner-only is the right scope).
|
||||
|
||||
Reference in New Issue
Block a user