Skip to content

Fix cross-project GitHub task IDOR; scope webhooks per project; add github.manage permission - #25

Merged
pikann merged 2 commits into
masterfrom
fix/jsonbody-tinygo-collision
Sep 14, 2026
Merged

pikann merged 2 commits into
masterfrom
fix/jsonbody-tinygo-collision

Conversation

@pikann

@pikann pikann commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Every handler taking a :taskId path param (branches, pull requests) trusted it without verifying it belongs to the caller's own project — only the request body's repo_id/pr_number were project-checked. A caller with tasks.read/tasks.write on their own project could substitute a foreign taskId to read or attach GitHub records to a different project's task.

Fixes

  • Added taskBelongsToProject, called first in every affected handler, plus defense-in-depth re-checks on rows reached via a repo_id/pull_request_id that has no project_id column of its own.
  • Webhook delivery lookup used full_name alone, which is only unique per-project — a delivery could be verified against a different project's secret. Moved the route to /projects/:projectId/webhook and scoped the lookup by that project_id.
  • A repository with no webhook secret configured skipped HMAC verification entirely and trusted the payload; it now fails closed.
  • Introduced a github.manage custom permission covering the project's GitHub settings tab (connect/disconnect token, link/unlink repos), replacing the broader projects.read/projects.write checks those routes used before.

Versioning

Bumped to 0.4.0 (minor) rather than a patch bump, since github.manage replaces existing permission checks — an installation with roles scoped to projects.read/projects.write for GitHub access will need to grant github.manage instead.

Verification

go test ./... and go vet ./... pass, including new regression tests for the cross-project cases and the webhook fixes.

🤖 Generated with Claude Code

pikann and others added 2 commits September 14, 2026 08:34
Every handler taking a :taskId path param (branches, pull requests)
trusted it without verifying it belongs to the caller's own project —
only the request body's repo_id/pr_number were project-checked. A
caller with tasks.read/write on their own project could substitute a
foreign taskId to read or attach GitHub records to a different
project's task. Added taskBelongsToProject, called first in every
affected handler, plus defense-in-depth re-checks on rows reached via
a repo_id/pull_request_id that lacks its own project_id column.

Also fixes two webhook bugs found while working on the above:
- full_name is only unique per-project, so the webhook lookup could
  resolve to a different project's row (and use its secret to verify
  a delivery meant for this one). Moved the webhook route to
  /projects/:projectId/webhook and scope the repository lookup by
  that project_id.
- a repository with no webhook secret configured skipped HMAC
  verification entirely and trusted the payload; it now fails closed.

Introduces a github.manage custom permission covering the project's
GitHub settings tab (connect/disconnect token, link/unlink repos),
replacing the broader projects.read/projects.write checks those
routes used before.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Minor bump: introduces the github.manage permission, replacing the
projects.read/projects.write checks the project settings routes used
before (existing role grants will need to add it).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@pikann
pikann merged commit 9682fd6 into master Sep 14, 2026
3 checks passed
@pikann
pikann deleted the fix/jsonbody-tinygo-collision branch September 14, 2026 09:29
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.

1 participant