Skip to content

fix(imessage): the unknown-inbound ingester must not swallow GROUP messages - #252

Merged
dshakes merged 1 commit into
masterfrom
fix/unknown-inbound-group
Sep 19, 2026
Merged

dshakes merged 1 commit into
masterfrom
fix/unknown-inbound-group

Conversation

@dshakes

@dshakes dshakes commented Sep 19, 2026

Copy link
Copy Markdown
Owner

Found by the introspection loop on its first successful audit in ~4 weeks — it wrote the fix and a regression test, then its run died on a network error (ENOTFOUND), leaving the work uncommitted in the tree. I verified the claim independently before committing.

The bug

maybeIngestUnknownInbound was called in handleInbound without the !isGroup guard that the WhatsApp bridge already has at its equivalent call site (services/whatsapp-bridge/src/session.ts:3423). An appointment-shaped message in a group could therefore be claimed by the life-event/appointment ingester, suppressing the contact reply — a silent drop in a group thread, which is exactly the class of failure the "never silent" rule exists to prevent.

isGroup is already a parameter of the enclosing handleInbound, and every sibling branch around it (lines 7317 / 7331 / 7353) is !isGroup-gated. This one was the outlier.

Verification

check result
unknown-inbound-group-scope.test.ts (new) pass
imessage-bridge full suite 118 pass / 0 fail
tsc clean

…ssages — parity with the WhatsApp guard

Found by the introspection loop on its first successful audit in ~4 weeks
(it wrote the fix + regression test before its run died on a network error,
leaving them uncommitted).

`maybeIngestUnknownInbound` was called without the `!isGroup` guard the
WhatsApp bridge already has at its call site, so an appointment-shaped
message in a GROUP could be claimed by the life-event/appointment ingester
and the contact reply suppressed — a silent drop in a group thread.
`isGroup` is already a parameter of the enclosing `handleInbound`; every
sibling branch around it is `!isGroup`-gated.

Verified: imessage-bridge 118/0 (incl. the new
unknown-inbound-group-scope regression test), tsc clean.
@github-actions github-actions Bot added the domain:core Core library / business logic label Sep 19, 2026
@compass-sdlc-bot compass-sdlc-bot Bot added the agent:reviewed-clean Reviewer found no Blocking issues this round label Sep 19, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Codex cross-audit (agent:audit)

Blocking: none.

Findings: none. The PR’s runtime change correctly prevents maybeIngestUnknownInbound from running when handleInbound is processing a group row, so group messages now fall through to the existing group gates instead of being swallowed by the unknown-sender ingester.

Residual risk: the added test is source-grep coverage, not behavioral coverage, so it protects the current call-site shape but would not catch all future refactors. I did not run the Vitest suite in this read-only sandbox; git diff --check origin/master...HEAD passed.

@github-actions

Copy link
Copy Markdown
Contributor

🔷 Gemini cross-audit (agent:audit-gemini)

Audit Result: LGTM (No Blocking Issues)

  • Correctness & Parity: Safely scopes the proactive unknown-inbound ingester to 1:1 DMs using the in-scope isGroup parameter, matching the WhatsApp bridge's guard and preventing silent suppression of group replies.
  • Idiomatic Testing: The source-parsing regex test in unknown-inbound-group-scope.test.ts is highly idiomatic and correctly aligns with the repo's established testing patterns (e.g., silent-drop-audit.test.ts).

@github-actions

Copy link
Copy Markdown
Contributor

🛡️ Vuln scan — ❌ vulnerable dependency found

cd services/control-plane && govulncheck ./...
=== Symbol Results ===

Vulnerability #1: GO-2026-6443
    Server panic via missing authority or Host headers in google.golang.org/grpc
  More info: https://pkg.go.dev/vuln/GO-2026-6443
  Module: google.golang.org/grpc
    Found in: google.golang.org/grpc@v1.82.1
    Fixed in: google.golang.org/grpc@v1.82.2
    Example traces found:
      #1: cmd/server/main.go:263:32: server.main calls grpc.Server.Serve, which eventually calls transport.http2Server.HandleStreams

Vulnerability #2: GO-2026-6348
    Heap Memory Exhaustion (OOM) via HTTP/2 DATA Frame Fragmentation in
    google.golang.org/grpc
  More info: https://pkg.go.dev/vuln/GO-2026-6348
  Module: google.golang.org/grpc
    Found in: google.golang.org/grpc@v1.82.1
    Fixed in: google.golang.org/grpc@v1.83.1
    Example traces found:
      #1: internal/handlers/runs.go:447:25: handlers.RunService.StreamRunEvents calls grpc.GenericServerStream[github.com/dshakes/lantern/gen/go/lantern/v1.StreamRunEventsRequest, github.com/dshakes/lantern/gen/go/lantern/v1.StreamEvent].Send, which eventually calls mem.BufferSlice.MaterializeToBuffer
      #2: internal/handlers/runs.go:447:25: handlers.RunService.StreamRunEvents calls grpc.GenericServerStream[github.com/dshakes/lantern/gen/go/lantern/v1.StreamRunEventsRequest, github.com/dshakes/lantern/gen/go/lantern/v1.StreamEvent].Send, which eventually calls mem.IsBelowBufferPoolingThreshold
      #3: internal/handlers/runs.go:447:25: handlers.RunService.StreamRunEvents calls grpc.GenericServerStream[github.com/dshakes/lantern/gen/go/lantern/v1.StreamRunEventsRequest, github.com/dshakes/lantern/gen/go/lantern/v1.StreamEvent].Send, which eventually calls mem.NewBuffer
      #4: internal/handlers/dataplane.go:519:30: handlers.DataPlaneService.RunStream calls grpc.GenericServerStream[github.com/dshakes/lantern/gen/go/lantern/v1.DpRunStreamClientMsg, github.com/dshakes/lantern/gen/go/lantern/v1.DpRunStreamServerMsg].Recv, which eventually calls mem.ReadAll
      #5: internal/handlers/llm_proxy.go:3919:104: handlers.LlmProxyHandler.Transcribe calls multierr.multiError.Error, which eventually calls mem.writer.Write
      #6: internal/handlers/runtime.go:316:28: handlers.grpcSchedulerClient.Exec calls grpc.clientStream.CloseSend, which eventually calls transport.ClientStream.Close
      #7: internal/handlers/runtime.go:316:28: handlers.grpcSchedulerClient.Exec calls grpc.clientStream.CloseSend, which eventually calls transport.ClientStream.Header
      #8: internal/handlers/dataplane.go:519:30: handlers.DataPlaneService.RunStream calls grpc.GenericServerStream[github.com/dshakes/lantern/gen/go/lantern/v1.DpRunStreamClientMsg, github.com/dshakes/lantern/gen/go/lantern/v1.DpRunStreamServerMsg].Recv, which eventually calls transport.ClientStream.Read
      #9: internal/handlers/runtime.go:316:28: handlers.grpcSchedulerClient.Exec calls grpc.clientStream.CloseSend, which eventually calls transport.ClientStream.RecvCompress
      #10: internal/handlers/runtime.go:316:28: handlers.grpcSchedulerClient.Exec calls grpc.clientStream.CloseSend, which eventually calls transport.ClientStream.TrailersOnly
      #11: internal/handlers/schedules.go:237:33: handlers.RESTHandler.UpdateSchedule calls time.LoadLocation, which eventually calls transport.NewHTTP2Client
      #12: cmd/server/main.go:263:32: server.main calls grpc.Server.Serve, which eventually calls transport.NewServerTransport
      #13: internal/handlers/dataplane.go:519:30: handlers.DataPlaneService.RunStream calls grpc.GenericServerStream[github.com/dshakes/lantern/gen/go/lantern/v1.DpRunStreamClientMsg, github.com/dshakes/lantern/gen/go/lantern/v1.DpRunStreamServerMsg].Recv, which eventually calls transport.ServerStream.Read
      #14: internal/handlers/dataplane.go:519:30: handlers.DataPlaneService.RunStream calls grpc.GenericServerStream[github.com/dshakes/lantern/gen/go/lantern/v1.DpRunStreamClientMsg, github.com/dshakes/lantern/gen/go/lantern/v1.DpRunStreamServerMsg].Recv, which eventually calls transport.Stream.ReadMessageHeader
      #15: internal/handlers/schedules.go:237:33: handlers.RESTHandler.UpdateSchedule calls time.LoadLocation, which eventually calls transport.http2Client.Close
      #16: internal/handlers/schedules.go:237:33: handlers.RESTHandler.UpdateSchedule calls time.LoadLocation, which eventually calls transport.http2Client.GracefulClose
      #17: internal/handlers/runtime.go:316:28: handlers.grpcSchedulerClient.Exec calls grpc.clientStream.CloseSend, which eventually calls transport.http2Client.NewStream
      #18: cmd/server/main.go:906:25: server.main calls grpc.Server.GracefulStop, which eventually calls transport.http2Server.Close
      #19: cmd/server/main.go:263:32: server.main calls grpc.Server.Serve, which eventually calls transport.http2Server.HandleStreams

Your code is affected by 2 vulnerabilities from 1 module.
This scan also found 1 vulnerability in packages you import and 19
vulnerabilities in modules you require, but your code doesn't appear to call
these vulnerabilities.
Use '-show verbose' for more details.
make: *** [Makefile:270: audit] Error 3

@dshakes
dshakes merged commit b501f9d into master Sep 19, 2026
15 of 16 checks passed
@dshakes
dshakes deleted the fix/unknown-inbound-group branch September 19, 2026 14:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent:reviewed-clean Reviewer found no Blocking issues this round domain:core Core library / business logic

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant