Skip to content

fix(antd-elixir): move to grpc 1.0.5 past the critical Hex advisory, commit mix.lock (V2-1574) - #375

Merged
Nic-dorman merged 1 commit into
mainfrom
nic/v2-1574-antd-elixir-grpc-advisory
Oct 19, 2026
Merged

Nic-dorman merged 1 commit into
mainfrom
nic/v2-1574-antd-elixir-grpc-advisory

Conversation

@Nic-dorman

Copy link
Copy Markdown
Member

V2-1574. antd-elixir moves off grpc 0.11.5, which carries a critical Hex advisory, to grpc 1.0.5.

Stacked on #367 (base nic/stack-integration-preview), because Antd.GrpcAdapter and the gRPC pointer/manifest tests this has to re-run exist only in the fan-out. This PR gets retargeted to main once #367 lands.

Advisories (mix hex.audit, cross-checked with GHSA and OSV)

ID Package Severity Fixed Vulnerable code
GHSA-grp7-v8xh-rj7h (CVE-2026-48853) grpc 0.11.5 critical 1.0.0 GRPC.Codec.Erlpack.decode/2: bare binary_to_term
GHSA-q8gf-9rvj-gmgj (CVE-2026-48854) grpc high 1.0.0 server Cowboy.Handler.read_full_body/1: unbounded body
GHSA-mwr4-5g34-j5cq (CVE-2026-48599) grpc high 1.0.0 server GRPC.Server.Transcode: path-binding override
GHSA-6ccx-9c9f-327w (CVE-2026-53430) grpc high 1.0.0 GRPC.Compressor.Gzip.decompress/1: gzip bomb
GHSA-w4f7-4cxr-rv3c (CVE-2026-43966) cowlib 2.21.0 / gun medium none cow_http_struct_hd:escape_string/2: CRLF in a structured header built from attacker input

Reachability: none reachable from this client-only SDK

  • Erlpack: a server decode path. The client codec is fixed to GRPC.Codec.Proto, and Antd.GrpcClient never passes codec:.
  • read_full_body and Transcode: server only. Nothing in lib/ uses GRPC.Server; only the test mock servers do.
  • Gzip: the client only decompresses when accepted_compressors lists the encoding. That defaults to [], and antd-elixir sets neither compressor: nor accepted_compressors:.
  • cowlib: gun only calls the cow_http_hd parsers, never the vulnerable structured-header encoder. The REST client goes through req, finch and mint, which don't involve cowlib.

So the upgrade is hygiene plus defence in depth. It's still worth doing: every fresh install currently resolves the critical release.

Change

  • mix.exs:
    • {:grpc, "~> 1.0.5"}. From 1.0, grpc is split into a client package and grpc_server, and gun becomes optional.
    • {:gun, "~> 2.4.0"}, which grpc requires.
    • {:grpc_server, "~> 1.0.5", only: :test} for the mock servers.
    • Each constraint has a comment explaining it.
  • Antd.GrpcAdapter rewritten, behaviour unchanged. grpc 1.0.5 still drops the headers and trailers of a failed unary call, so the adapter stays; the x-antd-error-code trailer depends on it.
    • grpc 1.0 delivers responses through a per-stream StreamResponseProcess rather than the caller's mailbox, so the old approach of scooping messages from the mailbox hung.
    • The new adapter drains that process, notes the headers and trailers, replays the messages into a fresh one, and lets gun's own receive_data/2 decode them.
    • It's tied to grpc 1.0.x internals, which is why the range is ~> 1.0.5.
  • mix.lock is now committed, with !antd-elixir/mix.lock added to .gitignore.
    • Hex ignores a library's lock for downstream users, so mix.exs constraints are what protect users.
    • The lock gives CI and dev one reproducible tree. Its absence is how 0.11.5 slipped in.
    • Resolved tree: grpc 1.0.5, grpc_core 1.0.5, gun 2.4.1, cowlib 2.21.0, plus grpc_server 1.0.5 for tests.

Verified on dev2 (Erlang 29 / Elixir 1.19.5)

  • Full suite: 265 tests, 0 failures, 3 skipped, the same before and after.
  • grpc_pointers_test.exs: 38/38, including "the adapter returns a failed call's metadata only when asked".
  • grpc_manifests_test.exs: 10/10, including the over-4 MiB round trip.
  • mix hex.audit now reports only the cowlib/gun advisory.

Open points

  1. cowlib/gun GHSA-w4f7-4cxr-rv3c has no fixed release. mix hex.audit stays non-clean. The proposal is to accept it on the reachability above and watch for a cowlib release after 2.21.0.
  2. OTP floor: cowlib 2.21.0 uses ?=, which needs OTP ≥ 26, so a fresh install fails on OTP 25. mix.exs says elixir: "~> 1.14". A README note or a stated minimum is a follow-up.
  3. ~> 1.0.5 vs ~> 1.0: the tighter range protects the adapter. The cost is a conflict for downstream projects that need grpc 1.1 until the adapter is revalidated.

Closes V2-1574.

🤖 Generated with Claude Code

grpc < 1.0 carries GHSA-grp7-v8xh-rj7h (critical, unsafe term
deserialization in the server-side Erlpack codec) plus three high-severity
server-side advisories, all fixed in 1.0.0. antd-elixir is client-only and
does not reach the vulnerable code, but every install resolved 0.11.5 and
Hex flagged it.

- grpc ~> 1.0.5; grpc 1.0 split the client from the server, and gun is now
  an optional dependency of it, so gun ~> 2.4.0 is listed explicitly.
  grpc_server (test only) supplies the mock servers.
- Antd.GrpcAdapter rewritten for grpc 1.0's Gun adapter, which reads a
  call's response from a per-stream StreamResponseProcess instead of the
  caller's mailbox. Still needed: 1.0.5 drops headers and trailers on a
  failed unary call as 0.11.5 did.
- Commit mix.lock (un-ignored for antd-elixir only) so CI and dev resolve
  one tree; the constraints in mix.exs are what protect downstream users.

cowlib 2.21.0 / gun (EEF-CVE-2026-43966, medium) has no fixed release and
stays flagged by hex.audit; see the ticket.

Full suite on dev2: 265 tests, 0 failures, 3 skipped, before and after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@dirvine dirvine left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE — incremental PR #375 change

Reviewed exact head 947bd983219edca8dba9db5beb0aba5ad2606fc4 against preview base bc339abe3e8914104139cf63253913892ed28432, not the whole #367 fan-out.

5/6 reviewed — partial panel; provisional conclusion, not full consensus. GLM 5.3 and Qwen 3.8 completed correctness and adversarial reviews and support approval. Missing: DS4 correctness; each missing role returned an incomplete, output-truncated response on its initial attempt and one narrower same-model retry. Failed attempts are not votes. DS4 completed its other role and also supports approval.

No introduced blocking defect found. The adapter now drains the per-stream response process and replays into Gun's decoder while retaining failure metadata. The replay cleanup is in an after block; no-header and streaming calls still delegate unchanged. The source's raw-response/Gun-response tuple difference is resolved by Gun's private await normalization bridge, not an adapter mismatch.

Independently executed

  • macOS, Erlang/OTP 29, Elixir 1.20.4; exact committed lockfile, no tracked source edits.
  • mix test --seed 0: 262 passed, 3 skipped, on both head and a fresh base dependency resolution.
  • Production compile and application startup passed. grpc_server and cowboy are absent from the loaded production applications.
  • 10 additional review-only probes passed, covering metadata/error parity, timeout/dead process handling and a 40-way synthetic replay-isolation check. These are not live-daemon tests or committed regressions.
  • git diff --check passed. The changed-file formatter check fails in grpc_client.ex on both head and base; inherited formatting drift.

Non-blocking risks / follow-ups

  1. The security audit is still non-clean. mix hex.audit exits 1 for medium CVE-2026-43966 / GHSA-w4f7-4cxr-rv3c in gun/cowlib. The fresh base has the same exposure plus four grpc advisories, including one critical. Source checks support non-reachability through this SDK's default client path, not a global claim about downstream applications. Patched cowlib availability was not independently verified.
  2. The declared Elixir ~> 1.14 floor does not match resolved dependency requirements. Fresh base resolution already has this drift; document/test the supported minimum in a follow-up. No old-runtime build was run.
  3. The adapter depends on grpc internals. The ~> 1.0.5 constraint limits minor upgrades but does not prove future patch compatibility; keep the documented pointer/manifest regression gate for every upgrade. One small cleanup: an unexpected transport message gets an extra diagnostic wrapper on replay. An additional executed gun_inform 103 probe confirms both upstream and adapter still return UNKNOWN (2), with empty adapter metadata. Preserve the original cause and add a regression fixture in a follow-up. The suggested total-timeout regression is not supported: the old adapter also reset its wait for each frame.

Integration boundary

No CI check runs or statuses are reported for this head. No live Rust daemon or Linux execution was tested. #367 merged during this review at bc339abe3e8914104139cf63253913892ed28432 (the reviewed base). #375 still targets nic/stack-integration-preview, while main has since advanced: retarget/rebase onto main and rerun checks against the actual destination. This approves the reviewed incremental code, not an untested post-retarget integration tree or a merge/release.

@Nic-dorman
Nic-dorman changed the base branch from nic/stack-integration-preview to main October 19, 2026 16:20
@Nic-dorman
Nic-dorman merged commit b2051ea into main Oct 19, 2026
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.

2 participants