From c48518886dc0efef657494085da9fa0f92bc8ac2 Mon Sep 17 00:00:00 2001 From: Mark Kreyman Date: Sun, 12 Apr 2026 22:44:16 -0600 Subject: [PATCH] Fix verification report findings: npm_version, /swagger, route path, key decode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Copy mcp-server/package.json into Docker build so the discovery endpoint reads the real npm version instead of falling back to 0.0.0 - Add /swagger → /swaggerui redirect for discoverability - Fix route discovery: /api/openapi → /api/v1/openapi (correct path) - Fix TenantKeys: base64-decode private keys from Fly secrets before passing to :crypto.sign (keys are stored as base64 by FlyAdapter.set) Co-Authored-By: Claude Opus 4.6 (1M context) --- Dockerfile | 1 + lib/loopctl/tenant_keys.ex | 14 +++++++++++++- lib/loopctl_web/controllers/redirect_controller.ex | 11 +++++++++++ .../controllers/route_discovery_controller.ex | 2 +- lib/loopctl_web/router.ex | 5 +++++ 5 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 lib/loopctl_web/controllers/redirect_controller.ex diff --git a/Dockerfile b/Dockerfile index ace68f3..1a4657c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -43,6 +43,7 @@ RUN mix deps.compile COPY priv priv COPY lib lib COPY assets assets +COPY mcp-server/package.json mcp-server/package.json # Compile the release RUN mix compile diff --git a/lib/loopctl/tenant_keys.ex b/lib/loopctl/tenant_keys.ex index 1de6fb0..844cc1a 100644 --- a/lib/loopctl/tenant_keys.ex +++ b/lib/loopctl/tenant_keys.ex @@ -74,7 +74,10 @@ defmodule Loopctl.TenantKeys do secret_name = Secrets.audit_key_secret_name(slug) case Secrets.get(secret_name) do - {:ok, key} -> + {:ok, encoded} -> + # Fly secrets store keys as base64 (set via FlyAdapter.set/2). + # Decode to raw bytes for :crypto.sign/5. + key = decode_key(encoded) :ets.insert(@cache_table, {tenant_id, key, now + @ttl_seconds}) {:ok, key} @@ -87,4 +90,13 @@ defmodule Loopctl.TenantKeys do end end end + + # Keys may be stored as base64 (FlyAdapter encodes) or raw bytes (test mocks). + # Try base64 decode first; if it fails, assume raw bytes. + defp decode_key(value) when is_binary(value) do + case Base.decode64(value) do + {:ok, raw} when byte_size(raw) == 32 -> raw + _ -> value + end + end end diff --git a/lib/loopctl_web/controllers/redirect_controller.ex b/lib/loopctl_web/controllers/redirect_controller.ex new file mode 100644 index 0000000..b478faf --- /dev/null +++ b/lib/loopctl_web/controllers/redirect_controller.ex @@ -0,0 +1,11 @@ +defmodule LoopctlWeb.RedirectController do + @moduledoc """ + Simple redirects for common URL aliases. + """ + + use LoopctlWeb, :controller + + def swagger(conn, _params) do + redirect(conn, to: "/swaggerui") + end +end diff --git a/lib/loopctl_web/controllers/route_discovery_controller.ex b/lib/loopctl_web/controllers/route_discovery_controller.ex index 62656d3..758139b 100644 --- a/lib/loopctl_web/controllers/route_discovery_controller.ex +++ b/lib/loopctl_web/controllers/route_discovery_controller.ex @@ -606,7 +606,7 @@ defmodule LoopctlWeb.RouteDiscoveryController do }, # OpenAPI spec - %{method: "GET", path: "/api/openapi", description: "Full OpenAPI 3.0 spec (Swagger)"}, + %{method: "GET", path: "/api/v1/openapi", description: "Full OpenAPI 3.0 spec (Swagger)"}, # Superadmin endpoints %{ diff --git a/lib/loopctl_web/router.ex b/lib/loopctl_web/router.ex index 37c1cb9..236f36b 100644 --- a/lib/loopctl_web/router.ex +++ b/lib/loopctl_web/router.ex @@ -98,6 +98,11 @@ defmodule LoopctlWeb.Router do get "/", OpenApiSpex.Plug.SwaggerUI, path: "/api/v1/openapi" end + # Convenience alias — agents and humans commonly try /swagger + scope "/" do + get "/swagger", LoopctlWeb.RedirectController, :swagger + end + # Dev-only routes (dashboard, etc.) if Application.compile_env(:loopctl, :dev_routes, false) do end