Writing · From building ApexDebugger

The bug your linter can't see: the seam between LWC and Apex

Almost every Salesforce linter — and almost every code reviewer, human or AI — reviews one file at a time. That's fine for most bugs. It's not fine for a specific, dangerous class of bug that only exists between two files: untrusted input entering a Lightning Web Component, flowing across the wire into an Apex controller, and landing in a dynamic SOQL or DML statement with no sanitization in between. Read either file alone and it looks fine. The LWC just collects a string. The Apex method just runs a query. The vulnerability lives in the call between them — the seam — which is exactly the part no single-file tool ever looks at.

This was the problem I built the cross-language layer in ApexDebugger to solve.

Routing the review, then reasoning across it

A multi-agent orchestrator routes each changed file to the reviewer that understands its language — Apex to the Apex reviewer, LWC to the LWC reviewer — then runs two additional passes that look across those results instead of within a single file:

  • correlate — deterministic. If an LWC calls an Apex controller method that lacks CRUD/FLS enforcement or a sharing declaration, that's a security risk at the call site itself. Whether the controller is missing that enforcement is a plain, bounded fact — pure set-membership over findings the deterministic layer already produced. No model call needed; it's just code.
  • cross_reason — LLM data-flow. This traces untrusted input from the LWC into unsafe dynamic SOQL or DML in the Apex it calls. Whether that's actually exploitable is a judgment call, not a bounded fact — a value bound with a query parameter is safe; the same value concatenated directly into a query string is not. Telling those apart needs reasoning about intent, so this node runs as an LLM call with the same three-way voting used everywhere else in the pipeline.

The same rule that shapes the rest of the system shows up again here: bounded-and-decidable stays deterministic, unbounded judgment goes to the model. The seam doesn't get a different philosophy just because it spans two files.

The detail that makes it actually usable: partial diffs

Real pull requests rarely touch both sides of a seam at once. Someone tweaks an LWC and doesn't touch the Apex controller it calls that week — which is exactly the case a naive cross-file tool breaks on, because the controller isn't in the diff. ApexDebugger handles this by resolving the unchanged controller straight out of the repository with the deterministic regex layer, at no LLM cost, so an LWC-only PR still gets the full seam check instead of a silent gap. The seam gets reviewed on every relevant PR, not just the rare ones that happen to touch both files simultaneously.

Why this is the differentiator, not a feature

Plenty of tools can regex a single Apex class for a missing WITH SECURITY_ENFORCED. Very few trace a string from a Lightning input field, across the wire, into the query that runs on it — because doing that requires holding two files, two languages, and the call between them in view at once, and deciding which parts of that view are facts and which parts are judgment calls. That's the bug class that actually causes incidents in production Salesforce orgs, and it's the one single-file review — human or automated — is structurally unable to see.

View ApexDebugger on GitHub →

← Back to ashish001singh.com