towerops/lib/towerops_web/plugs/graphql_introspection.ex
Graham McIntie 34fe5d7e49 Security fixes: mask credentials in logs/API, fix cookie/CSP/LIKE injection/webhooks
CRITICAL fixes:
- Mask SNMP community string in agent channel logs (CRITICAL-1)
- Remove snmpv3 passwords from REST API responses, return _set booleans (CRITICAL-2)
- Replace snmp_community with snmp_community_set in GraphQL type (CRITICAL-3)

HIGH fixes:
- Fix cookie same_site from invalid 'Towerops' to 'Lax' (HIGH-4)
- Remove unsafe-eval from CSP script-src (HIGH-6)
- Block GraphQL introspection queries in production (HIGH-7)
- Sanitize LIKE wildcards in SNMP device name search (HIGH-8)
- Reject webhooks when no secret configured instead of accepting (HIGH-9)

MEDIUM fixes:
- Hash mobile session tokens (SHA-256) before DB storage (MEDIUM-10)
- Apply security headers in all environments, not just prod (MEDIUM-14)
- Add GraphQL query complexity limit (500) in production (MEDIUM-16)
- Fix X-Frame-Options to DENY to match frame-ancestors 'none' (MEDIUM-13)
2026-02-15 09:09:04 -06:00

32 lines
893 B
Elixir

defmodule ToweropsWeb.Plugs.GraphQLIntrospection do
@moduledoc """
Blocks GraphQL introspection queries in production.
Introspection exposes the full schema to API consumers. This is useful
in development but should be disabled in production to reduce attack surface.
"""
import Plug.Conn
def init(opts), do: opts
def call(conn, _opts) do
if Application.get_env(:towerops, :env) == :prod and introspection_query?(conn) do
conn
|> put_resp_content_type("application/json")
|> send_resp(400, Jason.encode!(%{errors: [%{message: "Introspection is disabled"}]}))
|> halt()
else
conn
end
end
defp introspection_query?(conn) do
case conn.body_params do
%{"query" => query} when is_binary(query) ->
String.contains?(query, "__schema") or String.contains?(query, "__type")
_ ->
false
end
end
end