Soundcheck.Check.Warning.UnscopedLookupByClientParam (soundcheck v0.1.0)

Copy Markdown View Source

Basics

This check is disabled by default.

Learn how to enable it via .credo.exs.

This check has a base priority of high and works with any version of Elixir.

Explanation

Flags Repo reads in handle_event/3 and handle_params/3 that look a record up by a client-sent param, in a clause that never consults the current user or scope. Event and URL params come from the client: any user can send any id.

Before:

def handle_event("delete", %{"id" => id}, socket) do
  post = Repo.get!(Post, id)
  {:ok, _} = Repo.delete(post)
  {:noreply, socket}
end

After:

def handle_event("delete", %{"id" => id}, socket) do
  post = Blog.get_post!(socket.assigns.current_scope, id)
  {:ok, _} = Blog.delete_post(socket.assigns.current_scope, post)
  {:noreply, socket}
end

A param taints the variables it flows into through =, <-, and pipes, within the clause; calls into helpers are not followed. A clause that reads one of scope_assigns (socket.assigns.current_user, or a %{assigns: %{current_user: user}} pattern) or calls a function matching authorizers counts as scoped. Modules matching excluded_modules are skipped. Ash calls are not Repo calls: Ash policies authorize them.

Context functions count as lookups too: a call into one of the project's own modules whose Mod.fun name matches lookup_functions (by default get_* and fetch_*, such as Blog.get_post!(id)). Calls into dependencies, the standard library, and modules that use an Ash module (domains and resources) never match.

An app that keeps its scope under another assign, such as :subject or :current_account, reports every clause until scope_assigns names it:

{Soundcheck.Check.Warning.UnscopedLookupByClientParam,
 [scope_assigns: [:subject]]}

Confidence: medium. Tier: syntactic (works without compiling).

Check-Specific Parameters

Use the following parameters to configure this check:

:excluded_paths

Paths to skip (atoms, binaries as path segments, or regexes).

This parameter defaults to [~r/\/test\//, "test"].

:strict

Unused.

This parameter defaults to false.

:repo_modules

Repo modules: atoms or regexes on the alias-expanded name.

This parameter defaults to [~r/\.Repo$/].

:lookup_functions

Context lookups in the project's own modules: Mod.fun names, regexes or exact.

This parameter defaults to [~r/\.get_\w+!?$/, ~r/\.fetch_\w+!?$/].

:scope_assigns

Assign keys whose use counts as scoping the clause.

This parameter defaults to [:current_scope, :current_user].

:authorizers

Function names (fun or Mod.fun, regexes or exact) that authorize.

This parameter defaults to [~r/authoriz/, ~r/permit/, ~r/can\?$/, ~r/allowed\?$/].

:excluded_modules

Modules to skip: atoms or regexes.

This parameter defaults to [~r/Admin/].

General Parameters

Like with all checks, general params can be applied.

Parameters can be configured via the .credo.exs config file.