Skip to content

fix: issues with letter sealing off groups#216

Merged
highesttt merged 1 commit into
mainfrom
highest/plat-37877
Jul 13, 2026
Merged

fix: issues with letter sealing off groups#216
highesttt merged 1 commit into
mainfrom
highest/plat-37877

Conversation

@highesttt

Copy link
Copy Markdown
Collaborator

No description provided.

@linear-code

linear-code Bot commented Jul 13, 2026

Copy link
Copy Markdown

PLAT-37877

@indent-zero

indent-zero Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor
PR Summary

Fixes several failure modes for LINE groups where Letter Sealing is off for some members, and where LINE returns memberMids/inviteeMids as string-valued objects (which the previous unmarshaller silently discarded, making the bridge think groups had no members). As a result, sends into LSOFF groups now degrade to plaintext instead of erroring, and E2EE group key registration succeeds in more edge cases (pending-invitee-only groups, filtered-to-self member lists).

  • FlexibleMidMap.UnmarshalJSON now accepts objects with any value type via map[string]json.RawMessage, plus explicit null handling; the []string fallback is preserved.
  • Split getChatMemberMIDs: joined members and pending-invitee state are now returned separately. Invitees are excluded from the member list (LINE rejects registrations that include them), but a new hasPendingInvitees bool lets autoRegisterGroupKey skip its cached/Matrix fallbacks when a self-only result is legitimate.
  • New groupKeyFetchError wraps "not a member" TalkExceptions as ErrNoUsableE2EEGroupKey — but only for latest-key fetches (groupKeyID == 0), so encrypt paths fall through to plaintext while decrypt paths still surface the raw error.
  • registerGroupKey replaces the "no other members" hard error with an E2EE == nil guard, skips the batch pubkey resolve when members list is empty, and still appends self so RegisterE2EEGroupKey is called with at least one entry.
  • Added tests for the JSON variants (string/bool/array), the joined/invitee split, and the new fetch-error wrapping semantics.

Issues

2 potential issues found:

  • getChatMemberMIDs's doc comment explains the invitee-exclusion rationale but never mentions the new hasPendingInvitees bool it now returns as its second value. → Autofix
  • TestRegisterGroupKeyAllowsPlaintextFallbackWithoutKnownMembers doesn't test what its name implies: newPeerKeyTestClient() leaves lc.E2EE nil, so the test short-circuits on the new E2EE-nil guard and never exercises the "filtered members reduce to only self" path this PR relaxes. → Autofix

CI Checks

Waiting for CI checks...


⚡ Autofix All Issues

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Improved encrypted group-chat key registration when member lists are incomplete or contain pending invitees.
    • Added safer plaintext fallback when no usable encrypted group key is available.
    • Improved handling of group-key retrieval errors and missing encryption configuration.
    • Corrected flexible map parsing for JSON objects with string or boolean values.
  • Tests

    • Added coverage for encrypted group-key fallback, member filtering, error handling, and flexible JSON parsing.

Walkthrough

Updates E2EE group-key fetching, joined-member discovery, registration fallback, and related tests. FlexibleMidMap now accepts JSON objects with non-boolean values while preserving array support.

Changes

E2EE group-key handling

Layer / File(s) Summary
Group-key fetch error handling
pkg/connector/e2ee_keys.go
Latest-key not-member errors are wrapped for plaintext fallback, while specific-key errors remain unchanged.
Joined-member discovery and registration
pkg/connector/e2ee_keys.go, pkg/connector/creategroup.go, pkg/connector/e2ee_keys_test.go
Invitees are excluded from joined-member lists, pending invitees affect fallback decisions, and group-key registration continues with no other members while requiring an E2EE manager.

FlexibleMidMap decoding

Layer / File(s) Summary
FlexibleMidMap JSON decoding
pkg/line/structs.go, pkg/line/structs_test.go
JSON object keys are accepted regardless of value type, with coverage for string objects, boolean objects, and arrays.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant autoRegisterGroupKey
  participant getChatMemberMIDs
  participant GetChats
  participant registerGroupKey
  participant E2EE
  autoRegisterGroupKey->>getChatMemberMIDs: discover joined member MIDs
  getChatMemberMIDs->>GetChats: retrieve chat metadata
  GetChats-->>getChatMemberMIDs: members and pending invitees
  getChatMemberMIDs-->>autoRegisterGroupKey: joined MIDs and pending flag
  autoRegisterGroupKey->>registerGroupKey: register group key
  registerGroupKey->>E2EE: generate and wrap group key
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive No pull request description was provided, so there is nothing substantive to assess. Add a short description summarizing the group-key and JSON unmarshal changes so reviewers have context.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and broadly matches the group/E2EE-related changes, though the wording is awkward.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch highest/plat-37877

Warning

Tools execution failed with the following error:

Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error)


Comment @coderabbitai help to get the list of available commands.

if !errors.Is(err, line.ErrNoUsableE2EEGroupKey) {
t.Fatalf("error = %v, want ErrNoUsableE2EEGroupKey", err)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Test doesn't exercise the intended path: newPeerKeyTestClient() returns a LineClient with E2EE unset, so registerGroupKey returns via the new lc.E2EE == nil guard on the very first line — the filtered-to-self member handling this PR added is never touched. Consider either (a) renaming this to reflect that it covers the E2EE-nil case, or (b) constructing a client with a real (or stub) E2EE manager and asserting that registerGroupKey(ctx, chatMid, []string{lc.Mid}) now proceeds to a self-only RegisterE2EEGroupKey call instead of erroring — which is the actual behavior change relative to the removed no other members to register group key for error.

// getChatMemberMIDs fetches joined member MIDs for a group chat via GetChats.
// Pending invitees are deliberately excluded: LINE validates group keys against the
// current joined-member set and rejects keys that include invitees.
func (lc *LineClient) getChatMemberMIDs(ctx context.Context, chatMid string) ([]string, bool, error) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Doc missing new return value: Comment above explains "why invitees are excluded" but doesn't document that the signature now returns (mids []string, hasPendingInvitees bool, error). A short line noting that the bool is used by autoRegisterGroupKey to gate self-only fallbacks would keep the contract clear for future readers.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
pkg/line/structs_test.go (1)

9-29: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Expand coverage for the new object-decoding contract.

The decoder now ignores arbitrary object value types and preserves null as a nil map, but the table only covers strings, booleans, and arrays. Add mixed numeric/null/nested values and assert got == nil for JSON null.

Suggested test extension
 		want []string
+		wantNil bool
@@
+		{
+			name: "mixed valued object",
+			data: `{"U-one":1,"U-two":null,"U-three":[],"U-four":{}}`,
+			want: []string{"U-one", "U-two", "U-three", "U-four"},
+		},
+		{
+			name:    "null",
+			data:    `null`,
+			wantNil: true,
+		},
@@
+			if test.wantNil != (got == nil) {
+				t.Fatalf("decoded map nil = %v, want %v", got == nil, test.wantNil)
+			}

Also applies to: 31-45

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/line/structs_test.go` around lines 9 - 29, Expand the table-driven
coverage in the visible test cases to include an object containing mixed
numeric, null, and nested values, asserting the expected keys regardless of
value types. Add a JSON null case whose expected result is nil, and update the
test assertions to compare the decoded result with the expected nil map
correctly.
pkg/connector/e2ee_keys_test.go (1)

246-269: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Consider adding coverage for the hasPendingInvitees fallback gating in autoRegisterGroupKey.

joinedGroupMemberMIDs itself is well covered, but the consuming gate (len(members) == 1 && members[0] == lc.Mid && !hasPendingInvitees in autoRegisterGroupKey, e2ee_keys.go lines 289 and 302) — the actual behavior change this PR is fixing — has no direct test confirming that a single-joined-member group with pending invitees skips the cached/Matrix fallback.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/connector/e2ee_keys_test.go` around lines 246 - 269, Add a direct
autoRegisterGroupKey test covering a group with only the local member joined and
pending invitees, asserting the cached/Matrix fallback is skipped when
hasPendingInvitees is true. Exercise the consuming len(members) == 1, local-MID,
and !hasPendingInvitees gate in autoRegisterGroupKey rather than only testing
joinedGroupMemberMIDs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@pkg/connector/e2ee_keys_test.go`:
- Around line 246-269: Add a direct autoRegisterGroupKey test covering a group
with only the local member joined and pending invitees, asserting the
cached/Matrix fallback is skipped when hasPendingInvitees is true. Exercise the
consuming len(members) == 1, local-MID, and !hasPendingInvitees gate in
autoRegisterGroupKey rather than only testing joinedGroupMemberMIDs.

In `@pkg/line/structs_test.go`:
- Around line 9-29: Expand the table-driven coverage in the visible test cases
to include an object containing mixed numeric, null, and nested values,
asserting the expected keys regardless of value types. Add a JSON null case
whose expected result is nil, and update the test assertions to compare the
decoded result with the expected nil map correctly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d2849d0a-78aa-485d-a45f-c1e9e8c26238

📥 Commits

Reviewing files that changed from the base of the PR and between 740b83d and ccd35bb.

📒 Files selected for processing (5)
  • pkg/connector/creategroup.go
  • pkg/connector/e2ee_keys.go
  • pkg/connector/e2ee_keys_test.go
  • pkg/line/structs.go
  • pkg/line/structs_test.go
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: build-docker
  • GitHub Check: Lint with 1.25
  • GitHub Check: build-docker
  • GitHub Check: Lint with 1.25
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use go fmt for code formatting across all Go files
Use goimports with -local "github.com/highesttt/matrix-line-messenger" flag to group project-local imports correctly
Use zerolog for logging throughout the codebase
Do not use Msgf in logging; use Msg with structured fields instead
Use Stringer interface where applicable in Go code

Files:

  • pkg/line/structs_test.go
  • pkg/line/structs.go
  • pkg/connector/creategroup.go
  • pkg/connector/e2ee_keys.go
  • pkg/connector/e2ee_keys_test.go
**/!(ltsm)/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/!(ltsm)/**/*.go: Run staticcheck on all Go files excluding pkg/ltsm package (transpiled WASM code)
Run go vet on all Go files excluding pkg/ltsm package (transpiled WASM code)

Files:

  • pkg/line/structs_test.go
  • pkg/line/structs.go
  • pkg/connector/creategroup.go
  • pkg/connector/e2ee_keys.go
  • pkg/connector/e2ee_keys_test.go
🔇 Additional comments (10)
pkg/line/structs.go (1)

8-17: LGTM!

pkg/connector/e2ee_keys.go (5)

26-31: LGTM!


43-51: LGTM!


221-244: LGTM!

joinedGroupMemberMIDs correctly dedups and excludes non-user MIDs, and the updated getChatMemberMIDs signature/doc clearly documents the invitee-exclusion rationale.


278-316: LGTM!

The !hasPendingInvitees guard correctly prevents overwriting a genuinely-single-joined-member state (with pending invitees) with stale cached/Matrix member lists that would otherwise incorrectly include invitees in group-key registration.


244-276: 🎯 Functional Correctness

Check for any remaining two-value getChatMemberMIDs call sites, including tests.

pkg/connector/creategroup.go (2)

73-81: LGTM!


213-295: LGTM!

The added lc.E2EE == nil guard prevents a nil-pointer panic on lc.E2EE.GenerateGroupKey() that would otherwise occur when this is called with an uninitialized E2EE manager, and skipping resolveGroupMemberPublicKeys when members is empty avoids an unnecessary batch call while still registering a self-only key.

pkg/connector/e2ee_keys_test.go (2)

214-234: LGTM!


236-244: 🎯 Functional Correctness

Confirm newPeerKeyTestClient keeps this test off the real RPC path pkg/connector/e2ee_keys_test.go:236-244
If newPeerKeyTestClient() leaves lc.E2EE non-nil, registerGroupKey will reach client.RegisterE2EEGroupKey(...) and the failure source may be transport-related rather than line.ErrNoUsableE2EEGroupKey. Either nil out lc.E2EE here or stub the RPC transport so the assertion exercises the intended path.

@highesttt highesttt merged commit 8adc093 into main Jul 13, 2026
10 checks passed
@highesttt highesttt deleted the highest/plat-37877 branch July 13, 2026 17:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant