Repository navigation
fix(security): R-9 — a purge destroys only what was retired first - #526
Merged
Merged
Conversation
Owner
Author
Separate security review (CONTRIBUTING / QP-9)A separate agent session reviewed this PR read-only. No finding at Medium or above. All five single-object purges lock and check the row as the first statement of their own transaction. The reference checks still follow. All six routes still require a global admin. The integration tests fail on the old code beside the permitted case. There is no restore path anywhere in Low and Info findings:
🤖 Generated with Claude Code |
The single-object purges of a document, conversation, workspace, connector and user checked references and nothing else, so a live row could be hard-deleted in one call, skipping Clark-Wilson's reversible step, and the only active admin could be purged (found again by the review of #525). Each purge now locks its row and refuses unless it is retired, inside its own transaction (`require_retired`); the services turn that into a 409 for a live row and a 404 for a missing one (`purge_refusals`), and every purge route sends it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KkWPqAcWbxfqz1aCP6Qsdx
jwvanderstam
force-pushed
the
fix/r9-purge-requires-retired
branch
from
November 8, 2026 21:35
4c82c64 to
0f52670
Compare
Conflicts were side-by-side additions to CHANGELOG, file-map and the docs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



QP-10 item 5, first part:
SECURITY.md's R-9 (QP-9 review, Low, admin-only).What was wrong
The five single-object purges checked references and nothing else:
The change
require_retired(cur, table, id)insrc/db/connection.pyruns in each purge's own transaction, before the existing reference check:SELECT deleted_at IS NOT NULL ... FOR UPDATE, so a restore cannot slip in between the check and theDELETE;sql.Identifier;NotRetiredErrorwhen the row is missing or still live.purge_refusals(what)insrc/services/errors.pyturns that error into aServiceError: 404 for a missing row, 409 "… must be retired before it is purged" for a live one. The five purge methods in the services use it.ServiceError; the document route already did.purge-allalready deletes retired documents only. The Users screen only offers purge on a retired user, so the UI path is unchanged.CLAUDE.md's Clark-Wilson section now names the precondition: the row is retired, and no active references exist.Tests
tests/integration/test_purge_requires_retired.py: all five purges against Postgres.tests/unit/test_purge_requires_retired.py: the check (including theFOR UPDATEstatement), the 404/409 mapping, and all five routes.Notes
CHANGELOG.md[Unreleased]and theSECURITY.mdR-9 row will conflict with #523, #524 and #525. #525 also edits R-9's row; whichever merges later needs a rebase.This touches
src/services/and every purge path, soCONTRIBUTING.mdasks for the separate agent-session review before merge.🤖 Generated with Claude Code
https://claude.ai/code/session_01KkWPqAcWbxfqz1aCP6Qsdx