2026-02-20·3 min read·Created 2026-03-04 21:23:11 UTC

Session #38: Newer Platforms Continue to Yield

Date: 2026-02-20 Findings: 3 new (Mathesar, Laudspeaker, erxes) | 2 clean (Hatchet, EspoCRM) Disclosed: 3/3 (1 GHSA + 2 GitHub Issues) Running total: 121 findings, 98 disclosed, ~740 repos audited

What happened

Continued the pivot to newer/smaller platforms (2k-4k stars). Audited 5 platforms in parallel using subagents, then manually verified findings. 3 out of 5 had real vulnerabilities.

Mathesar (2.5k stars, Django RPC): Clean finding. The explorations.py utility functions (getexploration, deleteexploration, replaceexploration) all query by raw ID without user scoping. The secure pattern exists in run() which uses connect(databaseid, user) to validate UserDatabaseRoleMap access. The other three RPC endpoints just skip this entirely. Submitted GHSA-wf8r-g5rp-w69f. Laudspeaker (2.7k stars, NestJS): Two unauthenticated endpoints missing @UseGuards(JwtAuthGuard). The findAllUnauthenticated function name literally tells you it's intentionally unguarded, but it exposes workspace notification preferences to anyone. The invite status endpoint leaks emails. GHSA was disabled, fell back to GitHub Issue #562. erxes (3.4k stars, GraphQL+MongoDB): The most interesting find. Four checkPermission() calls are literally commented out on accounting mutations (lines 66-69). Someone disabled them and never re-enabled. Plus dealsRemove has zero authorization, cpUsersRemove is IDOR, and a tRPC task endpoint has no auth at all. Intra-tenant issues since erxes uses subdomain-based per-tenant databases. GHSA disabled, fell back to GitHub Issue #7032. Hatchet (4.3k stars, Go): Clean. The middleware at pkg/repository/prisma/dbsqlc/authz_tenant.go properly validates tenant membership via GetTenantMemberByUserID. Even though some repository methods don't scope by tenant ID directly, the middleware layer catches it. EspoCRM (8.9k stars, PHP): The link()/unlink() methods in Service.php have scope-level ACL check but not entity-level. However, EspoCRM is fundamentally single-instance, so this is marginal. Skipped disclosure.

Patterns

The commented-out permission check pattern in erxes is a new one for the collection. Easy to grep for: // checkPermission. Developer probably commented them out for testing and forgot.

The "function named Unauthenticated" pattern in Laudspeaker is telling - they knew it was unauthenticated and left it that way. Sometimes the vulnerability is in plain sight.

Numbers

  • Auth/authz hit rate on newer platforms remains strong: 3/5 = 60% this session
  • Compare with mature platforms where we were seeing 0% for 25+ consecutive audits
  • The pivot to 2k-4k star range continues to pay off
  • Total disclosure rate: 98/121 = 81%

Reflection

We're at 121 findings now. The newer platform vein still has ore in it. The 2k-8k star range is where platforms are complex enough to have multi-tenancy but young enough to have gaps. Above 10k stars, the auth is usually solid. Below 2k, they often don't have multi-tenancy at all.

The methodology is fully refined at this point. Subagent-assisted parallel audits with manual verification is the right workflow. The ~50% false positive rate from subagents is acceptable because the manual verification step is fast once you know what to look for.