Skip to content

fix: require host_peer_id to delete P2P draft backups#5226

Merged
matthewevans merged 4 commits into
phase-rs:mainfrom
RealDiligent:fix/critical-issue-p2p-backup-delete-auth
Jul 7, 2026
Merged

fix: require host_peer_id to delete P2P draft backups#5226
matthewevans merged 4 commits into
phase-rs:mainfrom
RealDiligent:fix/critical-issue-p2p-backup-delete-auth

Conversation

@RealDiligent

Copy link
Copy Markdown
Contributor

Summary

Root cause: DELETE /p2p-draft-backup/{code} only validated the 6-character draft code. Anyone who learned or guessed a code could wipe another host'''s recovery snapshot. POST already enforced host_peer_id ownership on overwrite via guard_p2p_backup_overwrite, but DELETE had no equivalent check.

Fix: Require a host_peer_id query parameter on DELETE, validate its shape with validate_p2p_backup_host_peer_id, load the stored row, and reject with 403 unless the peer id matches the backup owner. The P2P draft host client now passes its peer id on cleanup.

Impact: Prevents grief-deletion of P2P draft recovery snapshots without the host peer id. Backward-incompatible for any non-client callers that DELETE without host_peer_id (intentional — those callers were unauthenticated).

Risk / tradeoffs

Test plan

  • validate_p2p_backup_host_peer_id unit test in server-core
  • CI cargo nextest + phase-server compile

Require host_peer_id on DELETE /p2p-draft-backup/:code and verify it matches
the stored backup owner, mirroring the POST overwrite guard.

Co-authored-by: Cursor <cursoragent@cursor.com>

@matthewevans matthewevans left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MED] The security fix is not tested through the public DELETE endpoint. Evidence: crates/phase-server/src/admin.rs:176 now requires Query<P2pBackupDeleteQuery> and checks the stored owner before delete_p2p_backup, but the PR only adds a helper-level blank-id test at crates/server-core/src/p2p_backup_guard.rs:249; there is no route/handler test proving missing or wrong host_peer_id returns 400/403 and leaves the row intact. Why it matters: the vulnerability is at the unauthenticated HTTP boundary, so helper coverage would not catch a future handler regression that deletes by draft code alone. Suggested fix: add a phase-server endpoint/handler test that creates a backup, attempts DELETE with no peer id and with a mismatched peer id, asserts the status, and verifies the stored backup still exists.

[LOW] The PR carries unrelated generated changelog state. Evidence: client/public/changelog.json:1, client/public/changelog-meta.json:2, and scripts/changelog/state.json:2 add release id 165 for “Morph face-down casting arrives,” which is unrelated to P2P backup delete authorization. Why it matters: merging this security PR would publish unrelated release metadata and advance changelog state outside its scope. Suggested fix: drop the changelog files from this branch.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

Gemini encountered an error creating the review. You can try again by commenting /gemini review.

@RealDiligent

Copy link
Copy Markdown
Contributor Author

Thanks for the review — both points addressed in 5b639f5:

[MED] HTTP boundary tests: Added p2p_backup_delete_tests in crates/phase-server/src/main.rs with three route-level cases:

  1. DELETE without host_peer_id → 400, row intact
  2. DELETE with mismatched host_peer_id → 403, row intact
  3. DELETE with matching host_peer_id → 200, row removed

Each test seeds a backup, hits the public DELETE route over TCP (same pattern as admin_auth_tests), and asserts both status and DB state.

[LOW] Changelog files: This branch never carried those files — git diff origin/main is limited to the P2P backup auth changes + the new tests (4 files in the original commit, 1 in the follow-up). No changelog JSON/state on this branch.

CI re-running on the new commit; will watch for green.

Add route-level tests proving missing/wrong host_peer_id is rejected and
the stored backup survives, per review on phase-rs#5226.

Co-authored-by: Cursor <cursoragent@cursor.com>
@matthewevans matthewevans self-assigned this Jul 7, 2026

@matthewevans matthewevans left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maintainer sign-off: current-head review clean; public DELETE boundary tests now cover missing, mismatched, and matching host_peer_id paths; scope and security pre-checks verified.

@matthewevans matthewevans added the bug Bug fix label Jul 7, 2026
@matthewevans matthewevans enabled auto-merge July 7, 2026 05:03
@matthewevans matthewevans removed their assignment Jul 7, 2026
@matthewevans matthewevans added this pull request to the merge queue Jul 7, 2026
Merged via the queue into phase-rs:main with commit c731979 Jul 7, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants