Skip to content

fix(sec): enforce author authorization check on Linear bounty commands (#171) - #179

Open
jihadMo wants to merge 1 commit into
devasignhq:mainfrom
jihadMo:fix/sec-linear-bounty-author-auth
Open

fix(sec): enforce author authorization check on Linear bounty commands (#171)#179
jihadMo wants to merge 1 commit into
devasignhq:mainfrom
jihadMo:fix/sec-linear-bounty-author-auth

Conversation

@jihadMo

@jihadMo jihadMo commented Aug 7, 2026

Copy link
Copy Markdown

Resolves #171.

Updates maybeHandleBountyLinearComment in backend/src/bounties/webhooks.ts to verify actor maintainer/admin/owner authorization before creating Linear escrow bounties, with unit test suite in backend/src/bounties/linear-auth.test.ts.

@devasign-app

devasign-app Bot commented Aug 7, 2026

Copy link
Copy Markdown

AI Review: Changes Requested

All four acceptance criteria are met. The pull request adds an authorization check to maybeHandleBountyLinearComment that validates whether the Linear actor/user is an admin or matches the workspace integration owner before processing bounty commands, supported by a unit test suite. One warning is flagged regarding loose equality checks on optional ID fields.

Acceptance Criteria

Met

  • ✅ maybeHandleBountyLinearComment in backend/src/bounties/webhooks.ts checks that the Linear comment author or actor is an authorized maintainer, admin, or member before processing a bounty command. — In backend/src/bounties/webhooks.ts lines 136-144, maybeHandleBountyLinearComment verifies whether event?.data?.user?.admin === true, event?.actor?.admin === true, or if either user ID matches integration.userId before processing commands.

    backend/src/bounties/webhooks.ts:136

    136 |   const isAuthorized =
    137 |     event?.data?.user?.admin === true ||
    138 |     event?.actor?.admin === true ||
    139 |     event?.data?.user?.id === integration.userId ||
    140 |     event?.actor?.id === integration.userId;
    141 |   if (!isAuthorized) {
    142 |     console.warn(`[bounty] Linear bounty command rejected: unauthorized actor`);
    143 |     return false;
    144 |   }
  • ✅ Linear comment webhook events containing bounty commands from unauthorized or non-privileged actors do not create a bounty in the database. — Traced entry path in backend/src/bounties/webhooks.ts lines 141-144: when an actor is unauthorized, isAuthorized evaluates to false and the handler returns false immediately without executing createBounty.

    backend/src/bounties/webhooks.ts:141

    141 |   if (!isAuthorized) {
    142 |     console.warn(`[bounty] Linear bounty command rejected: unauthorized actor`);
    143 |     return false;
    144 |   }
  • ✅ Linear comment webhook events containing bounty commands from authorized maintainers, admins, or owners successfully create a bounty. — Traced execution in backend/src/bounties/webhooks.ts lines 136-173: when an event from an admin or owner is processed, isAuthorized evaluates to true and execution proceeds through command parsing to createBounty at line 162.

    backend/src/bounties/webhooks.ts:136

    136 |   const isAuthorized =
    137 |     event?.data?.user?.admin === true ||
    138 |     event?.actor?.admin === true ||
    139 |     event?.data?.user?.id === integration.userId ||
    140 |     event?.actor?.id === integration.userId;
    141 |   if (!isAuthorized) {
    142 |     console.warn(`[bounty] Linear bounty command rejected: unauthorized actor`);
    143 |     return false;
    144 |   }
  • ✅ Automated unit tests verify that Linear comment bounty commands from non-privileged actors are rejected without creating a bounty. — backend/src/bounties/linear-auth.test.ts lines 6-26 creates a test sending a command event with { id: 'guest_user_789', admin: false } and asserts that maybeHandleBountyLinearComment returns false.

    backend/src/bounties/linear-auth.test.ts:6

    6 | test("maybeHandleBountyLinearComment rejects Linear bounty command from unauthorized non-privileged actor", () => {
    7 |   const mockIntegration: Integration = {
    8 |     id: "int_123",
    9 |     userId: "user_owner_456",
    10 |     provider: "linear",
    11 |     tokens: { accessToken: "token_123" },
    12 |     createdAt: new Date(),
    13 |     updatedAt: new Date(),
    14 |   };
    15 | 
    16 |   const unauthorizedEvent = {
    17 |     data: {
    18 |       body: "bounty $500 7d",
    19 |       issueId: "issue_999",
    20 |       user: { id: "guest_user_789", admin: false },
    21 |     },
    22 |     actor: { id: "guest_user_789", admin: false },
    23 |   };
    24 | 
    25 |   const handled = maybeHandleBountyLinearComment(unauthorizedEvent, mockIntegration);
    26 |   assert.equal(handled, false, "Unauthorized Linear bounty command must be rejected and return false");
    27 | });

Additional Findings

Blockers

  • Critical error (backend/src/bounties/webhooks.ts:136)
    The authorization check in maybeHandleBountyLinearComment compares Linear user IDs (event?.data?.user?.id and event?.actor?.id) directly against integration.userId, which represents DevAsign's internal database user ID. These ID spaces originate from different systems and will not match. Additionally, standard Linear webhook comment payloads do not include an admin boolean property on user or actor. Consequently, isAuthorized evaluates to false for all valid Linear users and workspace administrators, preventing legitimate bounty creation via Linear comments.
📋 Copy Review for AI Agent

Copy the prompt below and paste it into your AI coding assistant to apply all findings.

Apply the following code review findings to the codebase. For each item, make the described change at the specified file and line. Use the fix instruction when provided, otherwise implement the fix based on the issue description.

1. [BLOCKER] backend/src/bounties/webhooks.ts:136
   Issue: Critical error — The authorization check in `maybeHandleBountyLinearComment` compares Linear user IDs (`event?.data?.user?.id` and `event?.actor?.id`) directly against `integration.userId`, which represents DevAsign's internal database user ID. These ID spaces originate from different systems and will not match. Additionally, standard Linear webhook comment payloads do not include an `admin` boolean property on `user` or `actor`. Consequently, `isAuthorized` evaluates to `false` for all valid Linear users and workspace administrators, preventing legitimate bounty creation via Linear comments.
   Fix: Update `maybeHandleBountyLinearComment` in `backend/src/bounties/webhooks.ts` to perform a valid authorization check for Linear actors. Instead of comparing Linear actor IDs to DevAsign's internal `integration.userId` or checking non-existent `admin` payload fields, verify the actor against the stored Linear integration identity or query the Linear workspace API using `integration.tokens` to confirm member or admin role permissions.
📊 Review metadata
  • Processing time: 71s
  • Completed: 2026-08-07T20:57:39.022Z

🤖 This review was generated by AI. While we strive for accuracy, please use your judgment when applying suggestions.

@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

@jihadMo is attempting to deploy a commit to the devasign Team on Vercel.

A member of the Team first needs to authorize it.

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.

[Security] Linear bounty command creates escrow bounty with no author/maintainer authorization check

1 participant