Skip to content

Upgrade gun to 2.4.1 to fix security vulnerabilities#547

Open
arctarus wants to merge 1 commit into
elixir-grpc:masterfrom
arctarus:upgrade-gun-2.4.1
Open

Upgrade gun to 2.4.1 to fix security vulnerabilities#547
arctarus wants to merge 1 commit into
elixir-grpc:masterfrom
arctarus:upgrade-gun-2.4.1

Conversation

@arctarus

@arctarus arctarus commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Gun 2.2 and 2.3 contain several security vulnerabilities that are addressed in 2.4. This PR upgrades the dependency from ~> 2.2.0 to ~> 2.4.0 (resolved to 2.4.1).

Security fixes in gun 2.3 and 2.4

  • Reject unrequested HTTP/1.1 101 upgrades — 101 responses when no upgrade was requested now result in a protocol_error connection error, preventing unexpected protocol switches.
  • Restrict push promises to original request authority — Push promises are now validated against the authority of the original request, preventing cross-origin push attacks.
  • Fix keepalive_tolerance with unrequested pings — Unrequested pings no longer incorrectly consume the keepalive tolerance, which could be exploited to keep connections alive unexpectedly.

Gun 2.4 also updates Cowlib to 2.17.0, which includes its own security fixes.

Code changes

Gun 2.4 introduces invalid_request_headers (enabled by default), which rejects any header whose value contains \r or \n bytes by raising an exception. This is a deliberate security measure to prevent HTTP header injection attacks.

Two tests in server_test.exs used :erlang.term_to_binary/1 to encode a {pid, ref} pair and pass it as a raw gRPC metadata header value. Erlang's external term format is a binary serialisation that routinely produces bytes 0x0D (\r) and 0x0A (\n) as part of its encoding — for example, the PID and reference fields in the term contain embedded node information whose serialised bytes can include these values. Gun 2.4 now correctly rejects such headers, crashing the connection process.

The fix encodes the binary term as Base64 before setting the header (producing a safe ASCII string), and decodes it on the server side before deserialising:

# before
{"test-data", :erlang.term_to_binary({test_pid, ref})}
# ...
{pid, ref} = :erlang.binary_to_term(data)

# after
{"test-data", Base.encode64(:erlang.term_to_binary({test_pid, ref}))}
# ...
{pid, ref} = data |> Base.decode64!() |> :erlang.binary_to_term()

This also aligns with the gRPC spec, which requires binary metadata values to be Base64-encoded (and conventionally uses header names ending in -bin for binary metadata).

Test plan

  • mix test passes in grpc (301 tests, 0 failures)
  • mix test passes in grpc_core (94 tests, 0 failures)
  • mix test passes in grpc_server (206 tests, 0 failures)

🤖 Generated with Claude Code

Gun 2.3 and 2.4 fix several security vulnerabilities present in 2.2:
- Reject HTTP/1.1 101 responses when no upgrade was requested (protocol_error)
- Restrict push promises to the original request's authority
- Fix keepalive_tolerance with unrequested pings

Gun 2.4 also introduces `invalid_request_headers` (enabled by default),
which rejects header values containing CR/LF bytes to prevent header
injection attacks. Updated tests to Base64-encode binary Erlang terms
passed as header metadata, as raw term binaries can contain these bytes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant