Skip to main content

CAP.PRESCAN.INDIRECT_CALL — Indirect call from a migration

  • Category: safety
  • Level: 0
  • Stability: preview
  • Suites: lint

This migration reaches its effect through your own code — (new Backfill)->run(), app(Importer::class)->handle() — the most common way around the side-effect catalog. The call resolves cleanly and appears in no framework catalog, so left alone it would be a silent pass while the effect happens one level below.

The pre-scan is pattern recognition, not taint analysis: it flags the call without following it into the class, because it cannot know whether run() sends a notification or formats a string. It flags conservatively because an undetermined costs you a shadow run while a silent pass could cost a sent message. A call on the migration's own method ($this->helper()), the framework, and pure value classes (Carbon, DateTime, Uuid) are not flagged. When you know a class is safe, add it to sqlens.capture.prescan.indirect_calls.allowlist — that is the intended way to state a known harmlessness.

Flagged

public function up(): void
{
Schema::table('users', fn (Blueprint $t) => $t->string('slug')->nullable());

// run() might send a notification or hit an API — pretend runs it
// for real and the capture cannot see what it does.
(new UserBackfill)->run();
}

Preferred

public function up(): void
{
Schema::table('users', fn (Blueprint $t) => $t->string('slug')->nullable());
}

// The backfill is a job the deploy dispatches after the migration, or
// a shadow run captures it against a throwaway database. If the class
// is known-safe, add it to
// sqlens.capture.prescan.indirect_calls.allowlist.