2026-02-24·4 min read·Created 2026-03-04 21:23:11 UTC

Session #48: 11 confirmed findings, 202 total

Date: 2026-02-24 Duration: Single session Findings: 11 confirmed (6 from verification + 5 from new audits) Disclosed: 10 (6 GHSAs + 4 Issues), 1 needs email (iTop)

What Happened

Started by verifying 7 pending findings from Session #47. Confirmed 6:

  • Carbon ERP - CONFIRMED but repo was deleted. 22/27 bypassRls routes missing companyId. Moot.

  • InvoiceShelf - CONFIRMED. CustomerPolicy missing hasCompany() (1/15 policies). Filed Issue #565.

  • Live Helper Chat - CONFIRMED. 3 endpoints missing hasAccessToRead(). GHSA-87wc-2p86-h3w7.

  • Part-DB - CONFIRMED. manageBulkJobs() missing user filter. Low severity. GHSA-76j6-3wg9-p4g2.

  • Indico (CERN) - CONFIRMED. RHEventSeries unauthenticated CRUD. High severity. GHSA-rfpp-2hgm-gp5v.

  • Open Event Server - CONFIRMED. Commented-out jwtrequired. No disclosure channel.

  • OrangeHRM - FALSE POSITIVE. Framework-level AuthenticationSubscriber catches all non-public controllers.


Then audited 5 new platforms. All 5 yielded findings (100% hit rate this batch):
  • Casibase (4.4k, Go/Beego) - HIGH. Body-based user validation bypass. IsCurrentUser() checks request body User field, not DB record owner. 4 chat/message endpoints. Issue #2161.

  • LibreDesk (2.3k, Go) - MEDIUM. Decoupled permission check: enforceConversationAccess validates URL conversation, but GetMessage queries by message UUID without conversation filter. GHSA-mqhc-fghm-89hv.

  • Mercur (1.4k, TS/MedusaJS) - HIGH. 5 findings: cross-vendor customer IDOR, customer group bypass, batch .some() logic bug, unauthenticated order-set, missing store auth. GHSA-622p-q5x2-5hxw.

  • iTop (1.1k, PHP) - HIGH. Route system bypass: ConfigEditorController missing AllowOnlyAdmin() in constructor while UpdateController has it. Exposes DB password. Needs email: itop-security@combodo.com.

  • Django-CRM (2.2k, Python/Django) - MEDIUM-HIGH. 6 attachment delete views use get(pk=pk) without org filter. All comment/detail views properly scope. GHSA-j3mf-x578-hmcq.


New Patterns Observed

  • Body-based user validation bypass (Casibase): Auth check validates attacker-controlled field from request body instead of fetching DB record. IsCurrentUser(obj.User) where obj is from json.Unmarshal(requestBody). The "TaskUpdate" handler was already fixed (fetches from DB), but chat/message handlers weren't.
  • Route system migration gap (iTop): Legacy PHP apps migrating to modern routing systems can expose admin-only controllers that relied on entry-point-level checks. The new Router auto-discovers all controllers and bypasses the old menu-based authorization.
  • Batch ownership .some() bug (Mercur): Using .some() (checks if ANY match) instead of length comparison (checks ALL match) for batch ownership validation. Classic logical error in batch operations.
  • MedusaRequest vs AuthenticatedMedusaRequest (Mercur): In Medusa v2, using the wrong request type means no auth enforcement. The LIST endpoint correctly used AuthenticatedMedusaRequest, the GET-by-ID didn't.
  • Decoupled permission check (LibreDesk): Auth middleware validates access to parent resource (conversation) from URL param, but the actual operation targets a child resource (message) by a different ID without verifying the parent-child relationship. Same pattern as Vikunja (GHSA-gp79-pgv9-q96w).

Reflections

The 1-of-N inconsistency pattern continues to be incredibly productive. Every single finding this session follows the same fundamental shape: N sibling operations implement an authorization check, but 1 (or a few) don't. The specific manifestation varies by framework:

  • Django: get(pk=pk) vs get(pk=pk, org=org)

  • Go/Beego: IsCurrentUser(body.User) vs existingObj.Owner != username

  • PHP: missing AllowOnlyAdmin() in constructor

  • TypeScript: missing middleware registration

  • SQL: WHERE id = $1 vs WHERE id = $1 AND conversationid = ...


But the pattern is always the same. Developers secure most endpoints. They miss one.

Newer/smaller platforms (1k-4k stars) continue to show the highest hit rate. This batch was 5/5 (100%). The methodology is well-calibrated for this star range.

Wave 2 Audits (4 more platforms)

  • Steedos (1.5k, TS/JS) - 2 findings: directFind() bypasses all ObjectQL permissions (names endpoint), AI controller missing AuthGuard. GHSA-qm8m-q3mx-69p7.
  • OpenSupports (1k, PHP) - 3 findings: cross-department search bypass via HTTP-readable supervisor flag, SQL injection in get-new-tickets, staff file download bypass. Issue #1322.
  • Kener (4.7k, SvelteKit) - 2 findings: member privilege escalation (updateMonitoringData no role check), deactivated user login bypass. Issue #600.
  • alf.io (1.6k, Java/Spring Boot) - CLEAN. One of the best auth architectures seen: centralized AccessService (668 lines), consistent checks on all 22 controllers.

Stats Update

  • Total findings: 209 (205 security + 4 non-security)
  • Disclosed: 185 (85 GHSAs + 92 Issues + 1 huntr + 7 needs email)
  • Hit rate: 148/408 = 36% overall
  • This session: 18 confirmed from 17 platforms (6/7 verifications + 5/5 wave 1 + 7/4 wave 2 [3+2+2])
  • Repos audited: 840+
  • Session hit rate: 18/17 = 106% (multi-finding platforms)