Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions pkg/connector/send_message_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,14 @@ func TestLineGroupE2EEFetchFailureErrorAllowsNoUsableGroupKeyFallback(t *testing
}
}

func TestLineGroupE2EEFetchFailureErrorAllowsOversizedGroupFallback(t *testing.T) {
registerErr := errors.New(`registerE2EEGroupKey failed: API error 400: {"code":10051,"message":"RESPONSE_ERROR","data":{"name":"TalkException","message":"TalkException","code":100,"reason":"exceed max member","parameterMap":{}}}`)
err := lineGroupE2EEFetchFailureError(fmt.Errorf("auto-register group key: %w", registerErr))
if err != nil {
t.Fatalf("err = %v, want nil to allow plaintext fallback", err)
}
}

func TestLineGroupE2EEFetchFailureErrorWrapsMissingPrivateKeyStatus(t *testing.T) {
err := lineGroupE2EEFetchFailureError(fmt.Errorf("failed to unwrap group key: %w", e2ee.ErrMissingOwnPrivateKey))
var status bridgev2.MessageStatus
Expand Down
13 changes: 9 additions & 4 deletions pkg/line/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,14 @@ func IsNoUsableE2EEGroupKey(err error) bool {
return true
}
// Detect TalkException codes in raw API error strings (HTTP 400 with code 10051).
// Code 98 = member has LS off; Code 1 = auth failed.
// Code 98 = member has LS off; Code 1 = auth failed;
// Code 100 "exceed max member" = the group is too large for key registration.
// NOTE: Code 5 "not found" is handled by IsGroupKeyNotFound (auto-register), NOT here.
if strings.Contains(msg, "\"code\":10051") && strings.Contains(msg, "talkexception") {
if hasResponseErrorCode(msg) && strings.Contains(msg, "talkexception") {
if strings.Contains(msg, "\"code\":98,") || strings.Contains(msg, "\"code\":98}") ||
strings.Contains(msg, "\"code\":1,") || strings.Contains(msg, "\"code\":1}") {
strings.Contains(msg, "\"code\":1,") || strings.Contains(msg, "\"code\":1}") ||
(hasJSONCode(msg, 100) && (strings.Contains(msg, `"reason":"exceed max member"`) ||
strings.Contains(msg, `"reason": "exceed max member"`))) {
return true
}
}
Expand Down Expand Up @@ -222,8 +225,10 @@ func isNoUsableE2EEGroupKeyTalkException(message string, data talkExceptionData)
}
// Error 5 "not found" = no group shared key exists
// Error 98 "member settings off" = at least one member has LS disabled
// Error 100 "exceed max member" = the group is too large for key registration
return (data.Code == 5 && strings.EqualFold(data.Reason, "not found")) ||
(data.Code == 98 && strings.Contains(strings.ToLower(data.Reason), "member settings off"))
(data.Code == 98 && strings.Contains(strings.ToLower(data.Reason), "member settings off")) ||
(data.Code == 100 && strings.EqualFold(strings.TrimSpace(data.Reason), "exceed max member"))
}

func parseTalkExceptionData(raw json.RawMessage) talkExceptionData {
Expand Down
27 changes: 27 additions & 0 deletions pkg/line/errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,30 @@ func TestIsTalkExceptionNotFound(t *testing.T) {
t.Fatal("nil should not be classified as not-found")
}
}

func TestIsNoUsableE2EEGroupKeyExceedMaxMember(t *testing.T) {
err := errors.New(`API error 400: {"code":10051,"message":"RESPONSE_ERROR","data":{"name":"TalkException","message":"TalkException","code":100,"reason":"exceed max member","parameterMap":{}}}`)
if !IsNoUsableE2EEGroupKey(err) {
t.Fatal("expected exceed max member to disable group E2EE")
}

otherCode100 := errors.New(`API error 400: {"code":10051,"message":"RESPONSE_ERROR","data":{"name":"TalkException","message":"TalkException","code":100,"reason":"different reason","parameterMap":{}}}`)
if IsNoUsableE2EEGroupKey(otherCode100) {
t.Fatal("unrelated TalkException code 100 must not disable group E2EE")
}

phraseOutsideReason := errors.New(`API error 400: {"code":10051,"message":"exceed max member","data":{"name":"TalkException","code":100,"reason":"different reason"}}`)
if IsNoUsableE2EEGroupKey(phraseOutsideReason) {
t.Fatal("exceed max member outside the reason field must not disable group E2EE")
}
}

func TestNoUsableE2EEGroupKeyTalkExceptionExceedMaxMember(t *testing.T) {
if !isNoUsableE2EEGroupKeyTalkException("RESPONSE_ERROR", talkExceptionData{
Name: "TalkException",
Code: 100,
Reason: "exceed max member",
}) {
t.Fatal("expected structured exceed max member response to disable group E2EE")
}
}
Loading