Skip to content

fix: add authentication to POST /tickets and PATCH /tickets/{ticket_id} endpoints (Fixes #1018)#1032

Merged
ritesh-1918 merged 8 commits into
ritesh-1918:gssocfrom
zeroknowledge0x:fix/tickets-auth-1018
Jun 1, 2026
Merged

fix: add authentication to POST /tickets and PATCH /tickets/{ticket_id} endpoints (Fixes #1018)#1032
ritesh-1918 merged 8 commits into
ritesh-1918:gssocfrom
zeroknowledge0x:fix/tickets-auth-1018

Conversation

@zeroknowledge0x
Copy link
Copy Markdown

@zeroknowledge0x zeroknowledge0x commented Jun 1, 2026

Fixes #1018, #1031

Summary

Add authentication, tenant isolation, and concurrency fixes to ticket and correction endpoints.

Changes

Ticket Endpoints (Fixes #1018)

Log Correction Endpoint (Fixes #1031)

  • Authentication: Added Depends(get_current_user) — only authenticated users can log corrections
  • Rate Limiting: @limiter.limit("10/minute") to prevent abuse
  • Async File I/O: Run file operations in thread pool via run_in_executor — prevents blocking the event loop
  • File Locking: FileLock prevents race conditions during read-modify-write cycle
  • Entry Cap: CORRECTIONS_LOG_MAX env var (default 10000) bounds log size
  • User ID Logging: Correction entries now include authenticated user_id

Testing

  • Unit tests added for all ticket endpoints (authentication, tenant isolation)
  • Unit tests added for log_correction endpoint (auth, rate limiting, async I/O)
  • All ticket endpoints now return 401 for unauthenticated requests
  • Tenant isolation prevents cross-company data access

Security

  • Prevents identity spoofing on ticket creation (user_id from JWT, not request body)
  • Prevents unauthenticated database breach via /tickets endpoints
  • Prevents event loop blocking from synchronous file I/O
  • Prevents race conditions and JSON corruption from concurrent writes

Summary by CodeRabbit

  • New Features

    • Ticket endpoints now require authentication; creation/saving bind ownership, resolve tenant/company from the authenticated profile, and enforce tenant isolation on reads/updates.
    • Correction logging is rate-limited, requires authentication, records the caller’s ID, and persists entries with capped storage and safer concurrent writes.
  • Bug Fixes

    • Tenant mismatches are rejected (403); ticket saves reject spoofed user IDs and return 404/503 for missing profile/linkage.
  • Tests

    • Added tests for correction logging, authentication, tenant isolation, and ticket operations.

…d} endpoints (Fixes ritesh-1918#1018)

- Add Depends(get_current_user) to POST /tickets endpoint
- Add Depends(get_current_user) to PATCH /tickets/{ticket_id} endpoint
- Prevent unauthenticated access to ticket creation and updates
- Prevent identity spoofing by requiring authenticated user
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 1, 2026

@zeroknowledge0x is attempting to deploy a commit to the ritesh Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0060f34c-4ac4-4205-b9bf-11301f59a8ad

📥 Commits

Reviewing files that changed from the base of the PR and between aee8445 and 7037149.

📒 Files selected for processing (1)
  • backend/tests/test_log_correction.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/tests/test_log_correction.py

📝 Walkthrough

Walkthrough

Adds authenticated user dependencies and tenant isolation to ticket endpoints, secures and rate-limits /ai/log_correction, includes authenticated user_id in correction entries, replaces unsafe file I/O with FileLock + executor-based persistence, and adds tests for the new behaviors.

Changes

Authentication and Tenant Isolation for Tickets & Correction Logging

Layer / File(s) Summary
Correction logging with concurrency safety
backend/main.py
Adds logger, aiofiles, and FileLock imports; centralizes extract_token/get_current_user; rate-limits /ai/log_correction (10/min); requires get_current_user; includes user_id in persisted correction entries; replaces blocking read/append/write with a FileLock-guarded run_in_executor read-modify-write with an entry cap.
Ticket endpoint auth & tenant isolation
backend/main.py
Adds Depends(get_current_user) to ticket endpoints (GET /tickets, POST /tickets/save, GET /tickets/{ticket_id}, POST /tickets, PATCH /tickets/{ticket_id}); enforces tenant-scoped Supabase queries using caller company_id, overwrites client-supplied user_id with authenticated user id, returns explicit 403/404/503 for profile/tenant issues, and whitelists PATCH fields with owner/elevated-role checks.
Correction logging tests
backend/tests/test_log_correction.py
Tests /ai/log_correction auth (401 when missing), successful save with authenticated user_id persisted, and no_change behavior when predictions match; uses mocked Supabase and temporary file fixtures.
Ticket endpoint auth and isolation tests
backend/tests/test_tickets_auth.py
Tests authentication and tenant isolation: GET /tickets requires auth; POST /tickets/save rejects spoofed user_id and uses authenticated id; GET /tickets/{ticket_id} enforces company isolation (403 on mismatch); POST /tickets and PATCH /tickets/{ticket_id} bind/authorize by authenticated user.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant RateLimiter
  participant API as /ai/log_correction
  participant Auth as get_current_user
  participant Executor
  participant FileLock
  participant File as corrections_log.json

  Client->>RateLimiter: POST /ai/log_correction (10/minute)
  RateLimiter->>API: allow
  API->>Auth: Depends(get_current_user)
  Auth-->>API: user (user_id)
  API->>Executor: submit read-modify-write task (entry with user_id)
  Executor->>FileLock: acquire
  FileLock->>File: read JSON, append, trim to cap, write JSON
  FileLock-->>Executor: release
  Executor-->>API: task complete
  API-->>Client: 200 (saved / no_change)
Loading
sequenceDiagram
  participant Client
  participant Endpoint as Ticket Endpoint
  participant Auth as get_current_user
  participant Supabase
  participant Profiles as profiles table

  Client->>Endpoint: Request (with auth)
  Endpoint->>Auth: Depends(get_current_user)
  Auth-->>Endpoint: user_id, company_id
  alt GET /tickets
    Endpoint->>Supabase: query tickets filtered by company_id
    Supabase-->>Endpoint: tickets
  else POST /tickets/save
    Endpoint->>Profiles: lookup profile by user_id
    Profiles-->>Endpoint: company_id
    Endpoint->>Supabase: upsert ticket with auth user_id and company_id
  else GET /tickets/{ticket_id}
    Endpoint->>Supabase: fetch ticket
    alt company_id matches
      Supabase-->>Endpoint: ticket
    else
      Endpoint-->>Client: 403 Forbidden
    end
  end
  Endpoint-->>Client: response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

gssoc, gssoc:approved, level:critical, level:advanced, quality:exceptional, type:feature, type:security

Poem

🐰 I hopped through code to mend the thread,
Locked the logs where scattered words had spread,
Tokens now guide which tickets belong,
Spoofed IDs gone — the audit sings a song,
Quiet paws fixed bugs and tucked them in bed.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 72.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary change: adding authentication to POST /tickets and PATCH /tickets/{ticket_id} endpoints, and references the fixed issue #1018.
Linked Issues check ✅ Passed The PR successfully addresses all coding requirements from #1018 (authentication, tenant isolation, user_id derivation) and #1031 (authentication, file locking, async I/O via executor, rate limiting, entry cap).
Out of Scope Changes check ✅ Passed All changes directly support the stated objectives: authentication/tenant isolation on ticket endpoints, security hardening of log_correction, and comprehensive tests for both feature areas.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

…itesh-1918#1031)

- Add aiofiles and filelock imports
- Use thread pool executor for non-blocking file I/O
- Add FileLock to prevent race conditions during read-modify-write
- Add entry cap enforcement (CORRECTIONS_LOG_MAX)
- Add rate limiting (10/minute)
- Add authentication (Depends(get_current_user))
- Log user_id with correction entry
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
backend/main.py (3)

706-716: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

owner_id is taken from the request body, not the authenticated user.

Authentication now blocks unauthenticated callers, but the new ticket's owner_id still comes from the client-provided TicketRecord, so an authenticated user can create a ticket under an arbitrary owner — the identity-spoofing concern from #1018. Bind ownership to the verified identity:

🛡️ Proposed fix
 async def create_ticket(ticket: TicketRecord, user: dict = Depends(get_current_user)):
     """Save a new ticket into the system."""
+    ticket.owner_id = user.get("id")
     # Check for duplicates before adding
     existing = next((t for t in TICKETS_DB if t.ticket_id == ticket.ticket_id), None)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/main.py` around lines 706 - 716, The create_ticket endpoint accepts a
TicketRecord whose owner_id is currently trusted from the request body—replace
that trust by binding ownership to the authenticated user: in create_ticket (and
before any duplicate-check or append to TICKETS_DB), overwrite ticket.owner_id
with the verified identity from the user dependency (e.g., use user["id"] or
user.get("id")), then perform the duplicate lookup/append using the
now-authenticated owner; this ensures TICKETS_DB and the duplicate check use the
authenticated owner rather than a client-provided owner_id.

619-637: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use the authenticated auth_user_id consistently instead of request_body.user_id.

final_data["user_id"] is correctly overwritten with the authenticated id at Line 595, but the tenant branch and audit logging still read the client-supplied request_body.user_id (Lines 622, 628, 636). Since user_id is a required field on TicketSaveRequest, the elif request_body.user_id: at Line 628 is effectively always true when reached, and the hashed audit values reflect the spoofable input rather than the verified identity. Replace these with auth_user_id.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/main.py` around lines 619 - 637, The tenant-check and audit logging
are still using the client-supplied request_body.user_id instead of the
authenticated auth_user_id; update the tenant branch to use auth_user_id for
verification and hashing (replace request_body.user_id with auth_user_id when
computing user_hash and in the elif that checks for a missing tenant), and
update the logger.info call to hash/log auth_user_id; keep existing behavior of
backfilling final_data["user_id"] from the authenticated id but ensure all
tenant logic and audit messages reference auth_user_id, and continue to use
profile_company_id and profile as before.

719-731: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

No ownership/tenant check on update.

Any authenticated user can patch any ticket in TICKETS_DB by id, including overwriting arbitrary fields via the unconstrained updates dict. Authentication alone does not enforce authorization here. Verify that the caller owns (or is permitted to modify) the ticket before applying updates, and consider restricting which fields updates may set.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/main.py` around lines 719 - 731, The update_ticket endpoint currently
applies any incoming updates to any ticket in TICKETS_DB without authorization;
modify update_ticket to first locate the ticket, verify the caller from
get_current_user (e.g., user['id'] or user['user_id']) is the owner/authorized
editor of that ticket (compare against a ticket ownership field such as
ticket.owner_id or ticket.user_id) and raise HTTPException(status_code=403) if
not permitted, and then whitelist/validate update keys (create an
ALLOWED_UPDATE_FIELDS set and only apply those keys) before merging into
ticket.dict() and constructing TicketRecord to prevent arbitrary field
overwrites.
🧹 Nitpick comments (1)
backend/main.py (1)

548-550: ⚡ Quick win

Errors are returned with HTTP 200.

Both the JSON-parse failure (Line 487) and the save failure here return {"status": "error"} with the default 200 status, so callers cannot distinguish success from failure via status code. Consider raising HTTPException (e.g. 400 for invalid body, 500 for save failure) or returning an explicit error status code.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/main.py` around lines 548 - 550, The error handling currently returns
{"status":"error"} with HTTP 200; change these handlers to raise HTTPException
with appropriate status codes so clients can detect failures: in the JSON-parse
failure path (the except around parsing the request body) raise
HTTPException(status_code=400, detail="Invalid JSON: <error>") and in the save
failure except block (the except Exception as e where logger.error("[CORRECTION
ERROR] Could not save: {e}") is called) raise HTTPException(status_code=500,
detail=str(e)); keep the logger.error calls but replace the returned dicts with
raising HTTPException to surface proper HTTP status codes to callers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/main.py`:
- Around line 696-701: The tenant check currently skips when the caller lacks
user_company_id; change it to fail-closed by rejecting requests when the
caller's company cannot be resolved and still enforce company equality when
ticket has a company. Replace the condition around
user_company_id/ticket_company_id so that if user_company_id is falsy you raise
HTTPException(403), and also raise if ticket_company_id is truthy and
user_company_id != ticket_company_id; update the block that reads
user_metadata/user_company_id/ticket_company_id to implement: if not
user_company_id or (ticket_company_id and user_company_id != ticket_company_id):
raise HTTPException(status_code=403, detail="Access denied: ticket belongs to a
different company or caller has no company_id")
- Around line 486-489: The function log_correction (and other places) reference
a missing logger, causing NameError; add a module-level logger by importing
logging if not present and creating a module logger (e.g., logger =
logging.getLogger(__name__)) near the top of the file after imports so
log_correction, any other functions (lines cited) and module-level logs use this
shared logger; you can keep save_ticket's local logger if intentionally
different, but ensure all references to logger inside log_correction use the
newly defined module-level logger.
- Around line 479-481: The endpoints (e.g., the POST route function
log_correction using the Depends annotation) reference get_current_user before
it is defined which causes an import-time NameError; fix by relocating the async
def get_current_user(...) definition to earlier in the module so it appears
above the first endpoint that uses Depends(get_current_user) (also ensure other
endpoints at the noted locations that use get_current_user are after the moved
definition), or alternatively import get_current_user from a top-level auth
module and replace the local reference so Depends resolves at import time.

In `@backend/tests/test_log_correction.py`:
- Around line 14-17: The supabase patch is only active during import so requests
hit the real get_current_user/supabase; modify the test setup to apply the patch
for the duration of the tests (e.g., start a patcher at module/fixture scope and
stop it after tests or use a pytest fixture that monkeypatches main.supabase) so
that main.supabase remains mocked while TestClient(app) is used and during
test_log_correction_saves_entry execution; ensure the patch references
"main.supabase" and remains active until after all requests complete.
- Around line 31-50: The tests currently use `@patch`("main.get_current_user")
which doesn't affect FastAPI's injected dependency; replace those patches in
TestLogCorrection (including test_log_correction_requires_auth and
test_log_correction_saves_entry) by setting
app.dependency_overrides[get_current_user] = mock_get_current_user before making
client requests and clearing the override after each test (e.g., pop or clear
dependency_overrides) so FastAPI uses mock_get_current_user; keep any
CORRECTIONS_LOG_PATH patching (or replace with a tmp path fixture) for
test_log_correction_saves_entry but remove the `@patch` decorator on
get_current_user and update imports to reference app and get_current_user from
main.

In `@backend/tests/test_tickets_auth.py`:
- Around line 42-155: Add tests to cover POST /tickets and PATCH
/tickets/{ticket_id} authentication: create four test functions named
test_create_ticket_requires_auth, test_create_ticket_with_auth,
test_update_ticket_requires_auth, and test_update_ticket_with_auth following the
existing Test* patterns; for the "requires_auth" tests call
client.post("/tickets", ...) and client.patch("/tickets/ticket-123", ...)
without an Authorization header and assert 401, and for the "with_auth" tests
patch main.get_current_user and main.supabase, mock
supabase.table(...).insert().execute() (for POST) and .update().eq().execute()
or .select().single().execute() (for PATCH) to return MagicMock data and call
the endpoints with headers={"Authorization": "Bearer test-token"}, then assert
200 and validate returned ticket id or updated fields to ensure authenticated
success paths are exercised.
- Around line 91-120: The test test_save_ticket_uses_auth_user_id currently
posts a spoofed user_id but never asserts outcome; update it to assert the API
used the authenticated user id from mock_auth (e.g., "test-user-id-123") and not
the spoofed "spoofed-user-id" by: 1) asserting response.status_code is success
and response.json() contains the expected user id or ticket owner, 2) inspecting
the insert call on mock_supabase
(mock_supabase.table.return_value.insert.assert_called_once() and examine the
call args) to assert the payload passed to insert includes "user_id":
"test-user-id-123" and does not include "spoofed-user-id", and 3) optionally
assert mock_supabase.table.return_value.insert.call_args or the returned
MOCK_TICKET.data shows the authenticated id—target symbols:
test_save_ticket_uses_auth_user_id, client.post("/tickets/save"),
mock_supabase.table.return_value.insert, and MOCK_TICKET.

---

Outside diff comments:
In `@backend/main.py`:
- Around line 706-716: The create_ticket endpoint accepts a TicketRecord whose
owner_id is currently trusted from the request body—replace that trust by
binding ownership to the authenticated user: in create_ticket (and before any
duplicate-check or append to TICKETS_DB), overwrite ticket.owner_id with the
verified identity from the user dependency (e.g., use user["id"] or
user.get("id")), then perform the duplicate lookup/append using the
now-authenticated owner; this ensures TICKETS_DB and the duplicate check use the
authenticated owner rather than a client-provided owner_id.
- Around line 619-637: The tenant-check and audit logging are still using the
client-supplied request_body.user_id instead of the authenticated auth_user_id;
update the tenant branch to use auth_user_id for verification and hashing
(replace request_body.user_id with auth_user_id when computing user_hash and in
the elif that checks for a missing tenant), and update the logger.info call to
hash/log auth_user_id; keep existing behavior of backfilling
final_data["user_id"] from the authenticated id but ensure all tenant logic and
audit messages reference auth_user_id, and continue to use profile_company_id
and profile as before.
- Around line 719-731: The update_ticket endpoint currently applies any incoming
updates to any ticket in TICKETS_DB without authorization; modify update_ticket
to first locate the ticket, verify the caller from get_current_user (e.g.,
user['id'] or user['user_id']) is the owner/authorized editor of that ticket
(compare against a ticket ownership field such as ticket.owner_id or
ticket.user_id) and raise HTTPException(status_code=403) if not permitted, and
then whitelist/validate update keys (create an ALLOWED_UPDATE_FIELDS set and
only apply those keys) before merging into ticket.dict() and constructing
TicketRecord to prevent arbitrary field overwrites.

---

Nitpick comments:
In `@backend/main.py`:
- Around line 548-550: The error handling currently returns {"status":"error"}
with HTTP 200; change these handlers to raise HTTPException with appropriate
status codes so clients can detect failures: in the JSON-parse failure path (the
except around parsing the request body) raise HTTPException(status_code=400,
detail="Invalid JSON: <error>") and in the save failure except block (the except
Exception as e where logger.error("[CORRECTION ERROR] Could not save: {e}") is
called) raise HTTPException(status_code=500, detail=str(e)); keep the
logger.error calls but replace the returned dicts with raising HTTPException to
surface proper HTTP status codes to callers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 928131eb-e5f1-48fd-a4d6-0a0156e22c24

📥 Commits

Reviewing files that changed from the base of the PR and between da8faf2 and 42ba740.

📒 Files selected for processing (3)
  • backend/main.py
  • backend/tests/test_log_correction.py
  • backend/tests/test_tickets_auth.py

Comment thread backend/main.py
Comment thread backend/main.py
Comment thread backend/main.py Outdated
Comment thread backend/tests/test_log_correction.py Outdated
Comment thread backend/tests/test_log_correction.py Outdated
Comment thread backend/tests/test_tickets_auth.py
Comment thread backend/tests/test_tickets_auth.py Outdated
Move logger = logging.getLogger(__name__) to module level so it's
available in all functions including log_correction. Remove duplicate
function-level logger definition.
…assertions

Address CodeRabbit review:
- Add assertions to user_id spoofing test
- Add test_create_ticket_requires_auth
- Add test_update_ticket_requires_auth
- Add test_create_ticket_with_auth
- Add test_update_ticket_with_auth
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
backend/main.py (5)

720-730: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

PATCH /tickets/{ticket_id} authenticates but never authorizes.

Once a caller is logged in, this handler will update any in-memory ticket whose ticket_id they know. Without an owner_id or tenant check, this is still an IDOR on writes. Return 403 unless the caller owns the ticket, or has an explicit elevated role.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/main.py` around lines 720 - 730, The update_ticket handler currently
authenticates via get_current_user but performs no authorization; before
applying updates in update_ticket (and before converting ticket.dict() to
TicketRecord), check that the current user (from user param returned by
get_current_user) is either the ticket owner (compare user['id'] to
ticket.owner_id or ticket.user_id as used in the TicketRecord) or has an
elevated role (e.g., user.get('role') in ('admin','support')); if not, raise an
HTTPException with status_code=403 to deny the operation; keep this check
adjacent to the loop that finds the ticket in TICKETS_DB so unauthorized updates
are prevented early.

683-685: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't wrap intentional HTTPExceptions as 500s.

This catches the 400/403/404/503 branches raised above and turns them into a generic 500, so the endpoint won't return the auth/tenant statuses this PR adds. Re-raise HTTPException unchanged before the fallback.

Suggested fix
-    except Exception as e:
+    except HTTPException:
+        raise
+    except Exception as e:
         traceback.print_exc()
         raise HTTPException(status_code=500, detail=str(e))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/main.py` around lines 683 - 685, The catch-all except in backend.main
(the except Exception as e block) is converting already-intended HTTPException
responses into generic 500s; change the handler so that if the caught exception
is an instance of fastapi.HTTPException (or HTTPException imported in this
module) you re-raise it unchanged, otherwise handle/log and raise the 500
HTTPException as currently done. Locate the except Exception as e block and add
an isinstance(e, HTTPException) check to re-raise the original HTTPException
before the fallback.

618-631: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Reject company_id overrides when the authenticated profile has no tenant.

If profile_company_id is missing and the request body supplies company_id, this branch accepts the caller's value and inserts the ticket anyway. That still allows cross-tenant writes for accounts without a resolved tenant. Fail closed unless the authenticated profile yields a tenant, then require an exact match.

Suggested fix
         profile_company_id = profile.get("company_id")
-        if final_data.get("company_id"):
-            # User provided company_id: verify it matches their profile.
-            if profile_company_id and final_data["company_id"] != profile_company_id:
-                user_hash = hashlib.sha256(str(request_body.user_id).encode()).hexdigest()[:8]
-                logger.warning(f"Tenant mismatch: user {user_hash} attempted {final_data['company_id']}, assigned to {profile_company_id}")
-                raise HTTPException(status_code=403, detail="User not authorized for this tenant")
-        elif profile_company_id:
+        if not profile_company_id:
+            raise HTTPException(status_code=400, detail="User has no tenant assignment")
+        if final_data.get("company_id") and final_data["company_id"] != profile_company_id:
+            user_hash = hashlib.sha256(str(auth_user_id).encode()).hexdigest()[:8]
+            logger.warning(f"Tenant mismatch: user {user_hash} attempted {final_data['company_id']}, assigned to {profile_company_id}")
+            raise HTTPException(status_code=403, detail="User not authorized for this tenant")
+        if not final_data.get("company_id"):
             # Backfill company_id from profile.
             final_data["company_id"] = profile_company_id
-        elif request_body.user_id:
-            # User has no tenant assignment.
-            raise HTTPException(status_code=400, detail="User has no tenant assignment")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/main.py` around lines 618 - 631, The current logic allows a
caller-supplied final_data["company_id"] when profile_company_id is missing;
change validation in the block handling final_data.get("company_id") so that if
profile_company_id is falsy you reject the request (raise HTTPException 403)
instead of accepting the provided company_id, and if profile_company_id exists
enforce equality as before (keep the existing tenant-mismatch logging using
user_hash built from request_body.user_id). Update the branch around
profile_company_id / final_data to: if final_data.get("company_id"): if not
profile_company_id -> raise 403; elif final_data["company_id"] !=
profile_company_id -> log and raise 403; elif profile_company_id -> backfill
final_data["company_id"] = profile_company_id; otherwise raise 400 for missing
tenant.

567-575: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Fail closed when the caller has no company_id.

user_metadata.get("company_id") or company_id lets a user with no tenant metadata either omit the filter and list every ticket, or pass any company_id and read another tenant's data. That reopens the exfiltration path this PR is meant to close.

Suggested fix
-    user_company_id = user_metadata.get("company_id") or company_id
+    user_company_id = user_metadata.get("company_id")
+    if not user_company_id:
+        raise HTTPException(status_code=403, detail="Access denied: caller has no company_id")
+    if company_id and company_id != user_company_id:
+        raise HTTPException(status_code=403, detail="Access denied: company_id mismatch")

     query = supabase.table("tickets").select("*").order("created_at", desc=True)
-    if user_company_id:
-        query = query.eq("company_id", user_company_id)
+    query = query.eq("company_id", user_company_id)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/main.py` around lines 567 - 575, The code currently sets
user_company_id = user_metadata.get("company_id") or company_id which lets
callers without tenant metadata bypass or override the tenant filter; change
this to fail closed by requiring an explicit tenant on the caller: read
user_company_id only from user_metadata (user_metadata.get("company_id")); if
it's missing or does not match the requested company_id, return an authorization
error (HTTP 403) rather than omitting the filter; then apply
query.eq("company_id", user_company_id) unconditionally when constructing the
supabase tickets query (use the existing variables user_metadata,
user_company_id, company_id, and query to locate the change).

707-716: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

POST /tickets still trusts a spoofable owner_id.

The route now requires auth, but it persists ticket.owner_id from the body untouched. Any authenticated caller can create a ticket on behalf of another user. Override owner_id from user["id"] or reject mismatches before appending to TICKETS_DB.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/main.py` around lines 707 - 716, The create_ticket route currently
trusts the owner_id from the request body; update the create_ticket function to
enforce the authenticated user by either (a) overriding ticket.owner_id with
user["id"] before duplicate checking and persisting to TICKETS_DB, or (b)
validating that ticket.owner_id == user["id"] and returning a 403/400 if it
differs. Ensure you reference the get_current_user dependency and use
TicketRecord/TICKETS_DB for the duplicate check and append, and keep logging
consistent after setting the authoritative owner_id.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@backend/main.py`:
- Around line 720-730: The update_ticket handler currently authenticates via
get_current_user but performs no authorization; before applying updates in
update_ticket (and before converting ticket.dict() to TicketRecord), check that
the current user (from user param returned by get_current_user) is either the
ticket owner (compare user['id'] to ticket.owner_id or ticket.user_id as used in
the TicketRecord) or has an elevated role (e.g., user.get('role') in
('admin','support')); if not, raise an HTTPException with status_code=403 to
deny the operation; keep this check adjacent to the loop that finds the ticket
in TICKETS_DB so unauthorized updates are prevented early.
- Around line 683-685: The catch-all except in backend.main (the except
Exception as e block) is converting already-intended HTTPException responses
into generic 500s; change the handler so that if the caught exception is an
instance of fastapi.HTTPException (or HTTPException imported in this module) you
re-raise it unchanged, otherwise handle/log and raise the 500 HTTPException as
currently done. Locate the except Exception as e block and add an isinstance(e,
HTTPException) check to re-raise the original HTTPException before the fallback.
- Around line 618-631: The current logic allows a caller-supplied
final_data["company_id"] when profile_company_id is missing; change validation
in the block handling final_data.get("company_id") so that if profile_company_id
is falsy you reject the request (raise HTTPException 403) instead of accepting
the provided company_id, and if profile_company_id exists enforce equality as
before (keep the existing tenant-mismatch logging using user_hash built from
request_body.user_id). Update the branch around profile_company_id / final_data
to: if final_data.get("company_id"): if not profile_company_id -> raise 403;
elif final_data["company_id"] != profile_company_id -> log and raise 403; elif
profile_company_id -> backfill final_data["company_id"] = profile_company_id;
otherwise raise 400 for missing tenant.
- Around line 567-575: The code currently sets user_company_id =
user_metadata.get("company_id") or company_id which lets callers without tenant
metadata bypass or override the tenant filter; change this to fail closed by
requiring an explicit tenant on the caller: read user_company_id only from
user_metadata (user_metadata.get("company_id")); if it's missing or does not
match the requested company_id, return an authorization error (HTTP 403) rather
than omitting the filter; then apply query.eq("company_id", user_company_id)
unconditionally when constructing the supabase tickets query (use the existing
variables user_metadata, user_company_id, company_id, and query to locate the
change).
- Around line 707-716: The create_ticket route currently trusts the owner_id
from the request body; update the create_ticket function to enforce the
authenticated user by either (a) overriding ticket.owner_id with user["id"]
before duplicate checking and persisting to TICKETS_DB, or (b) validating that
ticket.owner_id == user["id"] and returning a 403/400 if it differs. Ensure you
reference the get_current_user dependency and use TicketRecord/TICKETS_DB for
the duplicate check and append, and keep logging consistent after setting the
authoritative owner_id.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 943934e3-e5de-4d63-98a9-b355e6afe12f

📥 Commits

Reviewing files that changed from the base of the PR and between 42ba740 and 2458716.

📒 Files selected for processing (1)
  • backend/main.py

- Fail closed on tenant isolation: reject callers with no company_id
- Bind owner_id to authenticated user in create_ticket (prevent spoofing)
- Add authorization check in update_ticket (owner or admin/support role)
- Whitelist update fields to prevent arbitrary overwrites
- Re-raise HTTPException before catch-all to preserve 400/403/404 statuses
- Use auth_user_id consistently in audit logging instead of request_body.user_id
- Fail closed when profile_company_id is missing in save_ticket
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/tests/test_tickets_auth.py (1)

45-62: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Fix auth mocking in tests: @patch("main.get_current_user") won’t override FastAPI Depends(get_current_user)

In backend/tests/test_tickets_auth.py, the tests decorated with @patch("main.get_current_user", ...) won’t reliably affect the dependency used by the /tickets route. FastAPI expects auth dependency overrides via app.dependency_overrides, so the with_auth tests are likely exercising the real get_current_user instead of mock_get_current_user, meaning the intended identity/tenant behavior isn’t actually being controlled.

Suggested approach
# in a fixture or test setup
main.app.dependency_overrides[main.get_current_user] = mock_get_current_user

# cleanup
main.app.dependency_overrides.pop(main.get_current_user, None)

Apply this to every occurrence of @patch("main.get_current_user", ...) in the file and keep @patch("main.supabase") for the DB layer.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_tickets_auth.py` around lines 45 - 62, The tests currently
patch main.get_current_user which does not override FastAPI's dependency;
instead set main.app.dependency_overrides[main.get_current_user] =
mock_get_current_user in the test setup (or fixture) before calling client.get
and remove it after (pop main.get_current_user, None) in teardown, while keeping
the `@patch`("main.supabase") usage for DB mocking; update every test that uses
`@patch`("main.get_current_user") to use app.dependency_overrides with
mock_get_current_user so the /tickets route uses the mocked get_current_user.
♻️ Duplicate comments (1)
backend/tests/test_tickets_auth.py (1)

119-125: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Spoofing assertion is effectively optional — the test can pass without verifying the security fix.

assert response.status_code in (200, 201, 400, 422) accepts validation failures (400/422), and the user_id check on Lines 121-125 only runs if ... insert.called. If the request is rejected (or insert is never reached), the core assertion is skipped and the test still passes — leaving the anti-spoofing objective unverified.

Pin the success status and make the insert assertion unconditional:

🔒 Proposed tightening
-        assert response.status_code in (200, 201, 400, 422)
-        # Verify the insert was called with the authenticated user_id, not the spoofed one
-        if mock_supabase.table.return_value.insert.called:
-            insert_call_args = mock_supabase.table.return_value.insert.call_args
-            saved_data = insert_call_args[0][0] if insert_call_args[0] else insert_call_args[1]
-            assert saved_data.get("user_id") == "test-user-id-123", \
-                f"Expected authenticated user_id, got {saved_data.get('user_id')}"
+        assert response.status_code in (200, 201)
+        mock_supabase.table.return_value.insert.assert_called_once()
+        saved_data = mock_supabase.table.return_value.insert.call_args.args[0]
+        assert saved_data["user_id"] == MOCK_USER["id"]
+        assert saved_data["user_id"] != "spoofed-user-id"

Note: this only becomes meaningful once auth is mocked via dependency_overrides (see comment on Lines 45-62).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_tickets_auth.py` around lines 119 - 125, The test
currently allows 400/422 responses and only checks the spoofing protection if
mock_supabase.table.return_value.insert.called, which lets the test pass without
verifying anti-spoofing; change the response.status_code assertion to expect
success (e.g., assert response.status_code in (200, 201)) and remove the
conditional guard so you always assert mock_supabase.table.return_value.insert
was called and that saved_data.get("user_id") == "test-user-id-123" (use the
existing insert_call_args/saved_data extraction logic), ensuring the insert and
user_id check run unconditionally (and ensure auth is properly mocked via
dependency_overrides as noted).
🧹 Nitpick comments (1)
backend/tests/test_tickets_auth.py (1)

176-203: ⚡ Quick win

Authenticated create/update tests assert only a status range and don't verify the persisted payload.

assert response.status_code in (200, 201) / (200, 204) confirms little beyond "not an error", and neither test checks that the insert/update was actually invoked with the expected, authenticated-bound data. Combined with the dependency-override gap (Lines 45-62), these currently pass without exercising the auth-protected write paths.

Consider asserting the mocked Supabase call and returned ticket:

💚 Strengthen assertions
     def test_create_ticket_with_auth(self, mock_supabase, mock_auth):
         """Test that POST /tickets works with authentication."""
         mock_supabase.table.return_value.insert.return_value.execute.return_value = MagicMock(data=[MOCK_TICKET])
         response = client.post("/tickets", json={
             ...
         }, headers={"Authorization": "Bearer test-token"})
-        assert response.status_code in (200, 201)
+        assert response.status_code in (200, 201)
+        mock_supabase.table.return_value.insert.assert_called_once()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_tickets_auth.py` around lines 176 - 203, Update the two
tests (test_create_ticket_with_auth and test_update_ticket_with_auth) to assert
that the Supabase write was called with the expected payload and that the
response body contains the persisted ticket: for test_create_ticket_with_auth
verify mock_supabase.table().insert was called_with the ticket dict including
the authenticated user/company binding and that response.json() matches/contains
MOCK_TICKET; for test_update_ticket_with_auth verify
mock_supabase.table().update().eq was called_with the update dict and correct
ticket id, and that response.json() or response.content reflects MOCK_TICKET (or
check status and empty body for 204 accordingly). Also ensure the
get_current_user patch (used as mock_get_current_user) is applied so the
auth-bound fields are present when asserting the payload.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@backend/tests/test_tickets_auth.py`:
- Around line 45-62: The tests currently patch main.get_current_user which does
not override FastAPI's dependency; instead set
main.app.dependency_overrides[main.get_current_user] = mock_get_current_user in
the test setup (or fixture) before calling client.get and remove it after (pop
main.get_current_user, None) in teardown, while keeping the
`@patch`("main.supabase") usage for DB mocking; update every test that uses
`@patch`("main.get_current_user") to use app.dependency_overrides with
mock_get_current_user so the /tickets route uses the mocked get_current_user.

---

Duplicate comments:
In `@backend/tests/test_tickets_auth.py`:
- Around line 119-125: The test currently allows 400/422 responses and only
checks the spoofing protection if
mock_supabase.table.return_value.insert.called, which lets the test pass without
verifying anti-spoofing; change the response.status_code assertion to expect
success (e.g., assert response.status_code in (200, 201)) and remove the
conditional guard so you always assert mock_supabase.table.return_value.insert
was called and that saved_data.get("user_id") == "test-user-id-123" (use the
existing insert_call_args/saved_data extraction logic), ensuring the insert and
user_id check run unconditionally (and ensure auth is properly mocked via
dependency_overrides as noted).

---

Nitpick comments:
In `@backend/tests/test_tickets_auth.py`:
- Around line 176-203: Update the two tests (test_create_ticket_with_auth and
test_update_ticket_with_auth) to assert that the Supabase write was called with
the expected payload and that the response body contains the persisted ticket:
for test_create_ticket_with_auth verify mock_supabase.table().insert was
called_with the ticket dict including the authenticated user/company binding and
that response.json() matches/contains MOCK_TICKET; for
test_update_ticket_with_auth verify mock_supabase.table().update().eq was
called_with the update dict and correct ticket id, and that response.json() or
response.content reflects MOCK_TICKET (or check status and empty body for 204
accordingly). Also ensure the get_current_user patch (used as
mock_get_current_user) is applied so the auth-bound fields are present when
asserting the payload.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2a4d4c1e-1f59-44bc-bd55-7f3c5e9d1a0d

📥 Commits

Reviewing files that changed from the base of the PR and between 2458716 and c6b3c75.

📒 Files selected for processing (2)
  • backend/main.py
  • backend/tests/test_tickets_auth.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/main.py

…dency_overrides in tests

- Tenant isolation: reject when caller has no company_id (fail-closed)
- Tests: replace @patch('main.get_current_user') with app.dependency_overrides
- Tests: unconditional insert assertions in spoofing test
- Tests: add missing POST /tickets and PATCH /tickets/{ticket_id} auth tests
- Move extract_token() and get_current_user() before line 483 (first Depends() usage)
- Use string literal 'access_token' instead of ACCESS_COOKIE constant (defined later)
- Fix supabase mock scope in test_log_correction.py to persist during test execution

Addresses CodeRabbit review: forward reference would cause NameError at import time
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/tests/test_log_correction.py (1)

59-86: ⚠️ Potential issue | 🟠 Major

Fix CORRECTIONS_LOG_PATH mocking so open() reads/writes temp_path

  • backend/main.py’s _write_log uses open(CORRECTIONS_LOG_PATH, ...) directly (not str(CORRECTIONS_LOG_PATH)), so mocking only __str__ in backend/tests/test_log_correction.py isn’t sufficient—open() relies on os.fspath() / __fspath__. Patch CORRECTIONS_LOG_PATH with a real Path(temp_path) or set mock_path.__fspath__.return_value = str(temp_path) (plus the existing exists/stat stubs).
  • The executor timing concern is already addressed: log_correction awaits run_in_executor(..., _write_log) before returning.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_log_correction.py` around lines 59 - 86, The test's patch
of CORRECTIONS_LOG_PATH doesn't let open() resolve the patched path because
open() uses os.fspath()/__fspath__; update test_log_correction_saves_entry to
make the patched CORRECTIONS_LOG_PATH fspath-able by either assigning a real
Path(temp_path) to the patch or setting mock_path.__fspath__.return_value =
str(temp_path) (in addition to the existing mock_path.exists and mock_path.stat
stubs) so that backend.main._write_log's open(CORRECTIONS_LOG_PATH, ...)
reads/writes the temporary file and the assertions on logs[0]["user_id"]
succeed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@backend/tests/test_log_correction.py`:
- Around line 59-86: The test's patch of CORRECTIONS_LOG_PATH doesn't let open()
resolve the patched path because open() uses os.fspath()/__fspath__; update
test_log_correction_saves_entry to make the patched CORRECTIONS_LOG_PATH
fspath-able by either assigning a real Path(temp_path) to the patch or setting
mock_path.__fspath__.return_value = str(temp_path) (in addition to the existing
mock_path.exists and mock_path.stat stubs) so that backend.main._write_log's
open(CORRECTIONS_LOG_PATH, ...) reads/writes the temporary file and the
assertions on logs[0]["user_id"] succeed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ecdb54be-504d-4056-98f6-0de713301f25

📥 Commits

Reviewing files that changed from the base of the PR and between c6b3c75 and aee8445.

📒 Files selected for processing (3)
  • backend/main.py
  • backend/tests/test_log_correction.py
  • backend/tests/test_tickets_auth.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/main.py

CodeRabbit flagged that patching CORRECTIONS_LOG_PATH with a MagicMock
breaks open() calls because os.fspath() can't resolve the mock.

Fix: import the actual CORRECTIONS_LOG_PATH and patch with a real
Path(temp_path) inside a context manager so open() works correctly.
@zeroknowledge0x
Copy link
Copy Markdown
Author

All CodeRabbit review comments have been addressed in the latest commits:

  1. **Critical: undefined in ** → ✅ Fixed: Module-level logger = logging.getLogger(__name__) added at line 18
  2. Tenant isolation silently skipped → ✅ Fixed: Fail-closed — raises 403 when caller has no company_id
  3. ** patch only during import** → ✅ Fixed: Uses app.dependency_overrides[get_current_user] instead of import-time patching
  4. Coverage gap: no auth tests for POST/PATCH tickets → ✅ Fixed: Added test_create_ticket_requires_auth, test_create_ticket_with_auth, test_update_ticket_requires_auth, test_update_ticket_with_auth
  5. Missing assertions in spoofing test → ✅ Fixed: Asserts saved_data.get('user_id') != 'spoofed-user-id'
  6. CORRECTIONS_LOG_PATH mocking → ✅ Fixed: Patches with real Path(temp_path) object so open() works correctly

Commits: 7037149, aee8445, 2be5fea, c6b3c75, f7a428e

@ritesh-1918 ritesh-1918 changed the base branch from main to gssoc June 1, 2026 19:38
@ritesh-1918 ritesh-1918 added gssoc GirlScript Summer of Code gssoc:approved GSSoC Approved PR quality:exceptional Exceptional code quality level:critical Critical level difficulty type:security Security fix or improvement labels Jun 1, 2026
@ritesh-1918
Copy link
Copy Markdown
Owner

Hi @zeroknowledge0x! 🙌

Thank you so much for your excellent contribution: "fix: add authentication to POST /tickets and PATCH /tickets/{ticket_id} endpoints (Fixes #1018)"! We really appreciate the high-quality code and effort you have put into the platform.

Just a quick, friendly heads-up as we prepare our manual merging and verification queues—please make sure to complete all the mandatory community steps listed below.

Once those manual steps are verified, we'll get your PR officially merged into the gssoc branch (or keep it neatly cataloged if closed as integrated) and assign it the highest possible GSSoC S-Tier labels to maximize your leaderboard points!

Let's build something amazing together! 🚀🔥


🌟 Project Support & Developer Network (Show Some Love!)

As we prepare our manual verification and merging queues, please take a quick moment to ensure you have completed all four community steps:

  1. Star this repository: Helps our AI helpdesk get noticed! Star the repo here
  2. 🍴 Fork this repository: Keep a copy to build your own cool tools! Fork here
  3. 👤 Follow @ritesh-1918 on GitHub: Stay updated on real-time open-source projects! Follow ritesh-1918 here
  4. 💼 Connect on LinkedIn: Let's build a strong engineering connection! Connect with Ritesh on LinkedIn

Note: Having all four steps completed manually is required before your PR points are officially cleared.

@ritesh-1918
Copy link
Copy Markdown
Owner

Hi @zeroknowledge0x! 🙌

Thank you so much for your excellent contribution: "fix: add authentication to POST /tickets and PATCH /tickets/{ticket_id} endpoints (Fixes #1018)"! We really appreciate the high-quality code and effort you have put into the platform.

Just a quick, friendly heads-up as we prepare our manual merging and verification queues—please make sure to complete all the mandatory community steps listed below.

⚠️ Quick leaderboard tip: To secure the absolute highest GSSoC point tier for your contribution, please make sure you are following our project admin @ritesh-1918 manually on GitHub. Having all four onboarding steps cleared is highly preferable for manual PR approvals! ✨

Once those manual steps are verified, we'll get your PR officially merged into the gssoc branch (or keep it neatly cataloged if closed as integrated) and assign it the highest possible GSSoC S-Tier labels to maximize your leaderboard points!

Let's build something amazing together! 🚀🔥


🌟 Project Support & Developer Network (Show Some Love!)

As we prepare our manual verification and merging queues, please take a quick moment to ensure you have completed all four community steps:

  1. Star this repository: Helps our AI helpdesk get noticed! Star the repo here
  2. 🍴 Fork this repository: Keep a copy to build your own cool tools! Fork here
  3. 👤 Follow @ritesh-1918 on GitHub: Stay updated on real-time open-source projects! Follow ritesh-1918 here
  4. 💼 Connect on LinkedIn: Let's build a strong engineering connection! Connect with Ritesh on LinkedIn

Note: Having all four steps completed manually is required before your PR points are officially cleared.

@ritesh-1918 ritesh-1918 added level:intermediate Intermediate level difficulty quality:clean Clean code quality labels Jun 1, 2026
@ritesh-1918
Copy link
Copy Markdown
Owner

Hi @zeroknowledge0x! 🙌

Thank you so much for your excellent contribution: "fix: add authentication to POST /tickets and PATCH /tickets/{ticket_id} endpoints (Fixes #1018)"! We really appreciate the high-quality code and effort you have put into the platform.

Just a quick, friendly heads-up as we prepare our manual merging and verification queues—please make sure to complete all the mandatory community steps listed below.

⚠️ Quick leaderboard tip: To secure the absolute highest GSSoC point tier for your contribution, please make sure you are following our project admin @ritesh-1918 manually on GitHub. Having all four onboarding steps cleared is highly preferable for manual PR approvals! ✨

Once those manual steps are verified, we'll get your PR officially merged into the gssoc branch (or keep it neatly cataloged if closed as integrated) and assign it the highest possible GSSoC S-Tier labels to maximize your leaderboard points!

Let's build something amazing together! 🚀🔥


🌟 Project Support & Developer Network (Show Some Love!)

As we prepare our manual verification and merging queues, please take a quick moment to ensure you have completed all four community steps:

  1. Star this repository: Helps our AI helpdesk get noticed! Star the repo here
  2. 🍴 Fork this repository: Keep a copy to build your own cool tools! Fork here
  3. 👤 Follow @ritesh-1918 on GitHub: Stay updated on real-time open-source projects! Follow ritesh-1918 here
  4. 💼 Connect on LinkedIn: Let's build a strong engineering connection! Connect with Ritesh on LinkedIn

Note: Having all four steps completed manually is required before your PR points are officially cleared.

@ritesh-1918 ritesh-1918 added level:beginner Beginner level difficulty type:bug Bug fix mentor:ritesh-1918 Reviewed by Project Admin Ritesh labels Jun 1, 2026
@ritesh-1918 ritesh-1918 merged commit 7037149 into ritesh-1918:gssoc Jun 1, 2026
9 of 10 checks passed
ritesh-1918 pushed a commit that referenced this pull request Jun 1, 2026
- Fail closed on tenant isolation: reject callers with no company_id
- Bind owner_id to authenticated user in create_ticket (prevent spoofing)
- Add authorization check in update_ticket (owner or admin/support role)
- Whitelist update fields to prevent arbitrary overwrites
- Re-raise HTTPException before catch-all to preserve 400/403/404 statuses
- Use auth_user_id consistently in audit logging instead of request_body.user_id
- Fail closed when profile_company_id is missing in save_ticket
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gssoc:approved GSSoC Approved PR gssoc GirlScript Summer of Code level:beginner Beginner level difficulty level:critical Critical level difficulty level:intermediate Intermediate level difficulty mentor:ritesh-1918 Reviewed by Project Admin Ritesh quality:clean Clean code quality quality:exceptional Exceptional code quality type:bug Bug fix type:security Security fix or improvement

Projects

None yet

2 participants