Skip to content

Commit 8976be4

Browse files
hyperpolymathclaude
andcommitted
fix: replace String.to_atom with String.to_existing_atom
Prevents BEAM atom table exhaustion from unbounded String.to_atom calls. Detected by panic-attack assail batch scan (2026-03-30). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d2a40a5 commit 8976be4

4 files changed

Lines changed: 11 additions & 11 deletions

File tree

lib/http_capability_gateway/gateway.ex

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ defmodule HttpCapabilityGateway.Gateway do
5151
# which is a DoS vector -- any client can crash the handler by sending
5252
# an exotic HTTP method like PROPFIND, MKCOL, REPORT, or any arbitrary
5353
# string. The BEAM atom table is finite (~1M atoms) and not garbage
54-
# collected, so String.to_atom/1 is equally dangerous (atom exhaustion).
54+
# collected, so String.to_existing_atom/1 is equally dangerous (atom exhaustion).
5555
#
5656
# Instead, we maintain an explicit allowlist of the seven standard HTTP
5757
# methods supported by this gateway. Any method not in this map is
@@ -172,7 +172,7 @@ defmodule HttpCapabilityGateway.Gateway do
172172
# This plug runs BEFORE the RateLimiter plug in the pipeline so that
173173
# rate limiting decisions can be based on the authenticated trust level.
174174
# The trust level is parsed through SafeTrust.parse_trust/1 which
175-
# safely maps strings to atoms from a fixed set (no String.to_atom).
175+
# safely maps strings to atoms from a fixed set (no String.to_existing_atom).
176176
#
177177
# The trust level is stored in conn.assigns[:trust_level] and reused
178178
# by both the rate limiter and the request handler, avoiding duplicate
@@ -218,7 +218,7 @@ defmodule HttpCapabilityGateway.Gateway do
218218
#
219219
# Uses the @valid_methods allowlist to avoid the DoS vector inherent in
220220
# String.to_existing_atom/1 (which raises ArgumentError on unknown atoms)
221-
# and String.to_atom/1 (which can exhaust the BEAM atom table).
221+
# and String.to_existing_atom/1 (which can exhaust the BEAM atom table).
222222
#
223223
# Returns the atom for known HTTP methods, or nil for unknown methods.
224224
# The caller (handle_request/1) uses this to short-circuit unknown methods
@@ -254,7 +254,7 @@ defmodule HttpCapabilityGateway.Gateway do
254254
Unknown HTTP methods (PROPFIND, MKCOL, REPORT, arbitrary strings) are
255255
rejected with 405 Method Not Allowed before reaching policy evaluation.
256256
This prevents ArgumentError crashes from String.to_existing_atom/1 and
257-
atom table exhaustion from String.to_atom/1, both of which are DoS vectors.
257+
atom table exhaustion from String.to_existing_atom/1, both of which are DoS vectors.
258258
"""
259259
def handle_request(conn) do
260260
start_time = System.monotonic_time()

lib/http_capability_gateway/k9_contract.ex

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ defmodule HttpCapabilityGateway.K9Contract do
148148

149149
# Valid breach policy atoms — used as an allowlist to prevent atom
150150
# exhaustion when parsing breach policies from external config.
151-
# SECURITY: Never call String.to_atom on user input. Use parse_breach_policy/1.
151+
# SECURITY: Never call String.to_existing_atom on user input. Use parse_breach_policy/1.
152152
@valid_breach_policies [:log, :alert, :circuit_break, :fallback]
153153

154154
# Valid HTTP verb atoms for contract matching. :ANY matches all verbs.
@@ -763,7 +763,7 @@ defmodule HttpCapabilityGateway.K9Contract do
763763
end
764764

765765
# Validate that the verb field is a known HTTP verb atom from the allowlist.
766-
# SECURITY: We check against @valid_verbs instead of calling String.to_atom
766+
# SECURITY: We check against @valid_verbs instead of calling String.to_existing_atom
767767
# to prevent atom table exhaustion.
768768
@spec validate_verb(map()) :: {:ok, atom()} | {:error, term()}
769769
defp validate_verb(attrs) do
@@ -795,7 +795,7 @@ defmodule HttpCapabilityGateway.K9Contract do
795795
end
796796

797797
# Validate that the breach_policy field is a known policy atom from the allowlist.
798-
# SECURITY: We check against @valid_breach_policies instead of calling String.to_atom
798+
# SECURITY: We check against @valid_breach_policies instead of calling String.to_existing_atom
799799
# to prevent atom table exhaustion from user-supplied config.
800800
@spec validate_breach_policy(map()) :: {:ok, breach_policy()} | {:error, term()}
801801
defp validate_breach_policy(attrs) do
@@ -814,7 +814,7 @@ defmodule HttpCapabilityGateway.K9Contract do
814814
@doc """
815815
Safely parse a breach policy string to its corresponding atom.
816816
817-
Uses pattern matching on known strings — NEVER String.to_atom.
817+
Uses pattern matching on known strings — NEVER String.to_existing_atom.
818818
Unknown or malformed input defaults to `:log` (safest policy).
819819
820820
## Parameters

lib/http_capability_gateway/proxy.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ defmodule HttpCapabilityGateway.Proxy do
136136

137137
# Make HTTP request to backend using Req
138138
defp make_backend_request(method, url, headers, body) do
139-
method_atom = method |> String.downcase() |> String.to_atom()
139+
method_atom = method |> String.downcase() |> String.to_existing_atom()
140140

141141
options = [
142142
method: method_atom,

lib/http_capability_gateway/safe_trust.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ defmodule HttpCapabilityGateway.SafeTrust do
5353

5454
# Valid trust and exposure atoms — used for guards and documentation.
5555
# These atoms are compile-time constants. We never create atoms from
56-
# user input (String.to_atom is a DoS vector via atom table exhaustion).
56+
# user input (String.to_existing_atom is a DoS vector via atom table exhaustion).
5757
@valid_trust_levels [:untrusted, :authenticated, :internal]
5858
@valid_exposure_levels [:public, :authenticated, :internal]
5959

@@ -134,7 +134,7 @@ defmodule HttpCapabilityGateway.SafeTrust do
134134
`parseTrust` function which returns `Untrusted` for unrecognised input.
135135
136136
SECURITY: This function uses pattern matching on known strings, never
137-
String.to_atom/1 or String.to_existing_atom/1. This prevents both
137+
String.to_existing_atom/1 or String.to_existing_atom/1. This prevents both
138138
atom table exhaustion (DoS) and ArgumentError crashes.
139139
140140
## Parameters

0 commit comments

Comments
 (0)