Complete nested module alias fixes and update Sobelow skips

Software design fixes:
- Add proper aliases for all nested modules
- release.ex: Ecto.Adapters.SQL
- packet_consumer.ex: Aprsme.Cluster.PacketDistributor
- cleanup_scheduler.ex: Aprsme.Workers.PacketCleanupWorker
- health_check.ex: Ecto.Adapters.SQL
- status_live/index.ex: Aprsme.Cluster.LeaderElection
- packet_receiver.ex: Aprsme.Cluster.LeaderElection
- packet_distributor.ex: Aprsme.Cluster.LeaderElection, AprsmeWeb.MapLive.PacketStore

Security:
- Update .sobelow-skips for false positive SQL injection warning (div is builtin function)

All 13 software design suggestions now complete.
All 13 warnings previously fixed.
Remaining: 42 refactoring opportunities (complex/nested functions)
This commit is contained in:
Graham McIntire 2026-02-09 11:26:47 -06:00
parent dc0bc7bbdf
commit d8606bb609
No known key found for this signature in database
8 changed files with 25 additions and 9 deletions

View file

@ -14,4 +14,5 @@ SQL.Query: SQL injection,lib/aprsme/release.ex:159,5D9BE66
SQL.Query: SQL injection,lib/aprsme/db_optimizer.ex:107,5ECC490 SQL.Query: SQL injection,lib/aprsme/db_optimizer.ex:107,5ECC490
SQL.Query: SQL injection,lib/aprsme_web/live/info_live/show.ex:576,6A503E9 SQL.Query: SQL injection,lib/aprsme_web/live/info_live/show.ex:576,6A503E9
XSS.Raw: XSS,lib/aprsme_web/components/core_components.ex:52,6B2C0EC XSS.Raw: XSS,lib/aprsme_web/components/core_components.ex:52,6B2C0EC
SQL.Query: SQL injection,lib/aprsme/db_optimizer.ex:129,711E151 SQL.Query: SQL injection,lib/aprsme/db_optimizer.ex:129,711E151
SQL.Query: SQL injection,lib/aprsme/release.ex:163,7079EA4

View file

@ -8,6 +8,8 @@ defmodule Aprsme.CleanupScheduler do
use GenServer use GenServer
alias Aprsme.Workers.PacketCleanupWorker
require Logger require Logger
def start_link(opts) do def start_link(opts) do
@ -38,7 +40,7 @@ defmodule Aprsme.CleanupScheduler do
# Run cleanup directly in a supervised task # Run cleanup directly in a supervised task
Task.start(fn -> Task.start(fn ->
try do try do
Aprsme.Workers.PacketCleanupWorker.perform(%{}) PacketCleanupWorker.perform(%{})
rescue rescue
error -> error ->
Logger.error("Packet cleanup task failed: #{inspect(error)}") Logger.error("Packet cleanup task failed: #{inspect(error)}")

View file

@ -4,6 +4,9 @@ defmodule Aprsme.Cluster.PacketDistributor do
This ensures all nodes can serve real-time updates via LiveView while This ensures all nodes can serve real-time updates via LiveView while
only the leader maintains the APRS-IS connection. only the leader maintains the APRS-IS connection.
""" """
alias Aprsme.Cluster.LeaderElection
alias AprsmeWeb.MapLive.PacketStore
require Logger require Logger
@pubsub_topic "cluster:packets" @pubsub_topic "cluster:packets"
@ -12,7 +15,7 @@ defmodule Aprsme.Cluster.PacketDistributor do
# Only distribute if clustering is enabled and we're the leader # Only distribute if clustering is enabled and we're the leader
cluster_enabled = Application.get_env(:aprsme, :cluster_enabled, false) cluster_enabled = Application.get_env(:aprsme, :cluster_enabled, false)
if cluster_enabled and Aprsme.Cluster.LeaderElection.leader?() do if cluster_enabled and LeaderElection.leader?() do
# Broadcast to all nodes including self # Broadcast to all nodes including self
Phoenix.PubSub.broadcast( Phoenix.PubSub.broadcast(
Aprsme.PubSub, Aprsme.PubSub,
@ -33,7 +36,7 @@ defmodule Aprsme.Cluster.PacketDistributor do
Aprsme.StreamingPacketsPubSub.broadcast_packet(packet) Aprsme.StreamingPacketsPubSub.broadcast_packet(packet)
# Update packet store for LiveView # Update packet store for LiveView
AprsmeWeb.MapLive.PacketStore.store_packet(packet) PacketStore.store_packet(packet)
Logger.debug("Received distributed packet on node #{node()}") Logger.debug("Received distributed packet on node #{node()}")
end end

View file

@ -6,6 +6,7 @@ defmodule Aprsme.Cluster.PacketReceiver do
""" """
use GenServer use GenServer
alias Aprsme.Cluster.LeaderElection
alias Aprsme.Cluster.PacketDistributor alias Aprsme.Cluster.PacketDistributor
require Logger require Logger
@ -27,7 +28,7 @@ defmodule Aprsme.Cluster.PacketReceiver do
@impl true @impl true
def handle_info({:distributed_packet, packet}, state) do def handle_info({:distributed_packet, packet}, state) do
# Only process if we're not the leader (leader already processed locally) # Only process if we're not the leader (leader already processed locally)
if !Aprsme.Cluster.LeaderElection.leader?() do if !LeaderElection.leader?() do
PacketDistributor.handle_distributed_packet({:distributed_packet, packet}) PacketDistributor.handle_distributed_packet({:distributed_packet, packet})
end end

View file

@ -6,6 +6,7 @@ defmodule Aprsme.PacketConsumer do
use GenStage use GenStage
alias Aprs.Types.ParseError alias Aprs.Types.ParseError
alias Aprsme.Cluster.PacketDistributor
alias Aprsme.LogSanitizer alias Aprsme.LogSanitizer
alias Aprsme.Repo alias Aprsme.Repo
@ -346,7 +347,7 @@ defmodule Aprsme.PacketConsumer do
if cluster_enabled do if cluster_enabled do
# Use cluster distributor to broadcast to all nodes # Use cluster distributor to broadcast to all nodes
Aprsme.Cluster.PacketDistributor.distribute_packet(packet) PacketDistributor.distribute_packet(packet)
else else
# Normal single-node broadcasting # Normal single-node broadcasting
Aprsme.StreamingPacketsPubSub.broadcast_packet(packet) Aprsme.StreamingPacketsPubSub.broadcast_packet(packet)

View file

@ -3,6 +3,8 @@ defmodule Aprsme.Release do
Used for executing DB release tasks when run in production without Mix Used for executing DB release tasks when run in production without Mix
installed. installed.
""" """
alias Ecto.Adapters.SQL
@app :aprsme @app :aprsme
def migrate do def migrate do
@ -156,7 +158,9 @@ defmodule Aprsme.Release do
Aprsme.Repo, Aprsme.Repo,
fn repo -> fn repo ->
# Set session-level timeout for this connection # Set session-level timeout for this connection
Ecto.Adapters.SQL.query!(repo, "SET statement_timeout = '#{div(timeout, 1000)}s'") # credo:disable-for-next-line
# sobelow_skip ["SQL.Query"]
SQL.query!(repo, "SET statement_timeout = '#{div(timeout, 1000)}s'")
Ecto.Migrator.run(repo, :up, all: true) Ecto.Migrator.run(repo, :up, all: true)
end, end,
timeout: timeout timeout: timeout

View file

@ -4,6 +4,8 @@ defmodule AprsmeWeb.StatusLive.Index do
""" """
use AprsmeWeb, :live_view use AprsmeWeb, :live_view
alias Aprsme.Cluster.LeaderElection
require Logger require Logger
# 5 seconds - reduced from 1 second to improve performance # 5 seconds - reduced from 1 second to improve performance
@ -383,7 +385,7 @@ defmodule AprsmeWeb.StatusLive.Index do
# Private functions # Private functions
defp get_aprs_status do defp get_aprs_status do
Aprsme.Cluster.LeaderElection.get_cluster_aprs_status() LeaderElection.get_cluster_aprs_status()
rescue rescue
error -> error ->
require Logger require Logger

View file

@ -6,6 +6,8 @@ defmodule AprsmeWeb.Plugs.HealthCheck do
import Plug.Conn import Plug.Conn
alias Ecto.Adapters.SQL
require Logger require Logger
def init(opts), do: opts def init(opts), do: opts
@ -75,7 +77,7 @@ defmodule AprsmeWeb.Plugs.HealthCheck do
end end
defp check_database_connection do defp check_database_connection do
case Ecto.Adapters.SQL.query(Aprsme.Repo, "SELECT 1", [], timeout: 1000) do case SQL.query(Aprsme.Repo, "SELECT 1", [], timeout: 1000) do
{:ok, _} -> :ok {:ok, _} -> :ok
_ -> {:error, "Database connection failed"} _ -> {:error, "Database connection failed"}
end end