Skip to content

fix(webhook,worker): permission-gate cancel and memory commands (#13)#47

Closed
BunsDev wants to merge 1 commit into
mainfrom
fix/issue-13-command-gates
Closed

fix(webhook,worker): permission-gate cancel and memory commands (#13)#47
BunsDev wants to merge 1 commit into
mainfrom
fix/issue-13-command-gates

Conversation

@BunsDev

@BunsDev BunsDev commented Jul 7, 2026

Copy link
Copy Markdown
Member

Follow-up to #39. Code review after merge found that two command paths bypassed the documented write-access gate.

The bug

  • cancel (security): the router tombstoned the review-supersession registry (register_pr_review(..., "cancelled:<uuid>")) synchronously in the webhook handler — before any permission check — and acked with a commander-less CommandReply, so the worker's gate never fired. Any GitHub user able to comment on a public repo could kill queued PR reviews (drive-by DoS on the review lane) and receive a confirmation, contradicting the README ("every command except status requires write access").
  • remember/forget: acks carried no commander, so they were ungated. Harmless today (persistence is deferred to Define hosted memory governance contract between coven-github and coven-code #6), but the gate must exist before Define hosted memory governance contract between coven-github and coven-code #6 wires real side effects.
  • Same class, also closed: a commanded review registered its supersession generation in the router pre-gate, letting a below-write commander supersede queued auto-reviews before being declined.

The fix

  • New adapter-only TaskKind::CancelReviews { pr_number } carrying commander; the router no longer mutates the registry. The worker runs the permission gate first (below write → polite decline on the marker status surface, zero side effects), then tombstones and acks. No Check Run, no coven-code session.
  • The permission gate now precedes the CommandReply fast path; remember/forget acks carry the commander. Clarification and status replies remain deliberately ungated.
  • Commanded reviews register their supersession generation worker-side, after the gate passes; the dequeue skip is auto-review-only.
  • Gate fails closed: a permission-lookup API error aborts with no side effects.

Locked contract v2 untouched — CancelReviews, like CommandReply, never reaches a session brief.

Tests

  • Router: cancel yields a gated CancelReviews and does not tombstone (asserted against the registry through the real webhook handler with a valid HMAC signature); memory acks carry the commander; commanded review does not supersede queued reviews in the router.
  • Worker (wiremock): write-access cancel tombstones + acks with exactly the expected API calls and no Check Run; below-write cancel declines without tombstoning (registry asserted unchanged); below-write CommandReply is declined before the fast path; commanded review registers only after the gate passes.

Gates

cargo check --all-targets ✅ · cargo clippy --all-targets -- -D warnings ✅ · cargo test --all ✅ — commit GPG-signed and DCO signed-off.

PR #39 let router-side command handling mutate the PR review supersession registry before the worker permission gate, so drive-by commenters could cancel or supersede queued reviews and receive ungated acknowledgements. Memory command acknowledgements also bypassed the gate because CommandReply returned before commander permission checks.

Route cancel into an adapter-only CancelReviews task, move the cancel tombstone into the worker after the existing write-permission gate, and gate remember/forget replies by carrying the commander. Keep status and clarification replies ungated, and register command-created reviews only after the worker authorizes the commander.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Val Alexander <bunsthedev@gmail.com>
Copilot AI review requested due to automatic review settings July 7, 2026 04:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens the adapter’s permission-gated command handling by moving cancellation and certain command replies fully behind the worker-side write-access gate, preventing drive-by users from triggering side effects (notably review-lane cancellation) in the webhook/router.

Changes:

  • Introduces TaskKind::CancelReviews { pr_number } and routes cancel to a worker-executed, permission-gated cancellation flow instead of mutating the supersession registry in the webhook handler.
  • Ensures remember/forget acknowledgements carry a commander so they are permission-gated like other maintainer commands.
  • Moves commanded-review supersession registration worker-side (post-gate) and adjusts auto-review supersession checks to be auto-review-only.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

File Description
crates/worker/src/lib.rs Adds worker-side permission gating ahead of adapter-only fast paths; implements CancelReviews side effects (tombstone + status-surface ack) post-gate.
crates/worker/src/brief.rs Marks CancelReviews as adapter-only (should never reach briefing) via unreachable! arms.
crates/webhook/src/routes.rs Stops router-side supersession mutations for commanded reviews/cancel; routes cancel to CancelReviews and gates memory acks by attaching commander.
crates/github/src/{lib.rs,tasks.rs} Extends task model/list rendering for the new CancelReviews task kind.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/worker/src/lib.rs
Comment on lines +141 to +148
Err(e) => {
if !adapter_only {
task_store
.register_failed(&task, &familiar.display_name)
.await;
}
return Err(e);
}
Comment thread crates/worker/src/lib.rs
Comment on lines +222 to +226
let body = format!(
"Cancelled queued reviews for PR #{}. Work already running will finish; `@{} review` re-arms the lane.",
pr_number,
familiar.bot_username.trim_end_matches("[bot]")
);
@BunsDev BunsDev closed this Jul 7, 2026
@BunsDev
BunsDev deleted the fix/issue-13-command-gates branch July 7, 2026 04:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants