Skip to content

fix: bridge state is now set to badcredentials when a user gets logge…#200

Merged
highesttt merged 2 commits into
mainfrom
highest/plat-37702
Jun 30, 2026
Merged

fix: bridge state is now set to badcredentials when a user gets logge…#200
highesttt merged 2 commits into
mainfrom
highest/plat-37702

Conversation

@highesttt

Copy link
Copy Markdown
Collaborator

…d out from another instance

@linear-code

linear-code Bot commented Jun 30, 2026

Copy link
Copy Markdown

PLAT-37702

@indent-zero

indent-zero Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor
PR Summary

Surfaces a StateBadCredentials bridge state with a Reconnect prompt when LINE forcibly logs out this Chrome-extension session, prevents silent re-login (including across bridge restarts), adds a LoginProcessWithOverride reconnect path that re-authenticates from stored credentials, and expands unit-test coverage (auth recovery, ltsm crypto, reactions, login secret/runner).

  • Treats LINE error V3_TOKEN_CLIENT_LOGGED_OUT and TalkException code 83 "invalid sender key" as forced-logout signals via new IsLoggedOut/IsInvalidSenderKey helpers.
  • Adds sessionInvalidated flag plus a persisted UserLoginMetadata.SessionInvalidated; on logout markLoggedOutByOtherClient blanks the in-memory + DB access token and saves the flag, and LoadUserLogin rehydrates the flag so a bridge restart stays gated.
  • Centralizes recovery gating in shouldAttemptTokenRecovery, threads it through pollLoop's SSE 401/403 branch and the media handlers' tryRecoverClient (new ShouldRecover hook), and adds a deep guard in runTokenRecovery that returns errLineSessionInvalidated regardless of caller.
  • Successful refresh/relogin (refreshAndSave, tryLogin) clears meta.SessionInvalidated so user-driven reconnects re-enable recovery.
  • Implements LineEmailLogin.StartWithOverride using stored email/password/certificate; finishLogin falls back to profile.Mid, preserves an existing certificate, and now reuses a single LINE client for GetProfile.
  • Refactors client.go / connector.go around test seams (newLineAPIClient, loginWithCredentials, getProfileWithToken) and adds forced_logout_test.go, handlers/handler_test.go, reaction_test.go, ltsm/*_test.go, and runner_secret_test.go.

Issues

1 potential issue found:

  • IsLoggedOut now classifies any TalkException code 83 "invalid sender key" as a forced logout, so a single such error from a decrypt/send/E2EE path will blank the access token and put the bridge into StateBadCredentials. If LINE returns code 83 for benign reasons (e.g., a peer rotated their sender key and we haven't refreshed the public key), this will force a re-login the user never needed. → Autofix
5 issues already resolved
  • finishLogin builds two LINE API clients for the same token: client := newLineAPIClient(token) and then profile, err := getProfileWithToken(token) (which internally constructs another newLineAPIClient(token)). One of them is redundant — either drop the first one or replace the second with client.GetProfile(). (fixed by commit 8718a01)
  • markLoggedOutByOtherClient clears the in-memory AccessToken and sets sessionInvalidated, but never updates UserLoginMetadata.AccessToken or calls UserLogin.Save. After a bridge restart the stale (invalidated) token is rehydrated from the DB and sessionInvalidated is reset to its Go zero value — so Connect will once again attempt to use it before re-detecting the forced-logout state. (fixed by commit 8718a01)
  • pollLoop's "SSE error: 401/403" branch calls lc.recoverToken(ctx) directly without checking isSessionInvalidated, so once another goroutine has marked the session forcibly logged out (clearing AccessToken), the in-flight SSE's next attempt will see a 401 (no V3_TOKEN_CLIENT_LOGGED_OUT substring), fall through to recovery, re-login via stored credentials, and setTokens will reset sessionInvalidated=false — silently reauthenticating despite the bridge being in StateBadCredentials. (fixed by commit 8718a01)
  • TestStartWithOverrideUsesStoredCredentials stubs loginWithCredentials to return Type: 3, Verifier: "verifier", which makes handleLoginResponse spawn a goroutine that calls newLineAPIClient("").WaitForLogin(...)newLineAPIClient isn't mocked, so the goroutine leaks past the test and can hit the real LINE API on CI runners. (fixed by commit 8718a01)
  • handlers.tryRecoverClient calls h.RecoverToken(ctx) for 401/403/refresh errors without checking isSessionInvalidated. After markLoggedOutByOtherClient blanks the token, an in-flight media download will see a 401 (no V3_TOKEN_CLIENT_LOGGED_OUT marker), take the recovery branch, and silently re-login via stored credentials, undoing the badcredentials state — same shape as the pollLoop SSE 401/403 bypass. (fixed by commit 8718a01)

CI Checks

Waiting for CI checks...


⚡ Autofix All Issues

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a99c0808-40f1-4d0e-b9f4-76c94b2ef6ef

📥 Commits

Reviewing files that changed from the base of the PR and between df576df and 8718a01.

📒 Files selected for processing (8)
  • pkg/connector/auth_recovery_test.go
  • pkg/connector/client.go
  • pkg/connector/connector.go
  • pkg/connector/forced_logout_test.go
  • pkg/connector/handle_message.go
  • pkg/connector/handlers/handler.go
  • pkg/connector/handlers/handler_test.go
  • pkg/connector/sync.go
🚧 Files skipped from review as they are similar to previous changes (6)
  • pkg/connector/handle_message.go
  • pkg/connector/forced_logout_test.go
  • pkg/connector/handlers/handler.go
  • pkg/connector/connector.go
  • pkg/connector/client.go
  • pkg/connector/sync.go
📜 Recent review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: Lint with 1.25
  • GitHub Check: build-docker
  • 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/connector/handlers/handler_test.go
  • pkg/connector/auth_recovery_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/connector/handlers/handler_test.go
  • pkg/connector/auth_recovery_test.go
🔇 Additional comments (4)
pkg/connector/auth_recovery_test.go (3)

16-18: LGTM!


192-207: LGTM!


232-247: LGTM!

pkg/connector/handlers/handler_test.go (1)

9-36: LGTM!


📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added the ability to resume email login using stored login details, including an override-based start flow.
  • Bug Fixes
    • Improved handling of forced logouts: session invalidation from other clients is now detected and triggers relogin instructions, avoiding incorrect retries.
    • Better separates recoverable token issues from non-recoverable “logged out” and sender-key scenarios.
    • Token-recovery retry logic is now applied consistently across chat sync, profiles, and group actions.
  • Tests
    • Expanded unit/integration coverage for forced logout behavior and reaction/crypto validation.

Walkthrough

Updates LINE logout detection and session recovery, routes logged-out handling through a shared callback, adds override-based credential login, and expands tests across connector auth, reactions, ltsm crypto/embind, and runner secrets.

Changes

Forced logout detection and session recovery

Layer / File(s) Summary
IsInvalidSenderKey and IsLoggedOut expansion
pkg/line/errors.go, pkg/line/errors_test.go
Adds IsInvalidSenderKey and expands IsLoggedOut to classify invalid-sender-key errors as logged out; adds a matching test case.
shouldAttemptTokenRecovery and logout handling
pkg/connector/client.go
Adds client helpers, sessionInvalidated state, shouldAttemptTokenRecovery, and markLoggedOutByOtherClient; updates token validation and login/recovery flows to use them.
Auth recovery and handler logout callback
pkg/connector/auth_recovery.go, pkg/connector/handlers/handler.go, pkg/connector/handle_message.go
Updates token-error classification, adds logout marking in LINE call wrappers, extends Handler with a logout callback, and wires it from message handling.
StartWithOverride and finishLogin handling
pkg/connector/connector.go
Adds Certificate to LineEmailLogin, implements StartWithOverride, updates submit and PIN login paths to use loginWithCredentials, and derives certificate and MID during finishLogin.
shouldAttemptTokenRecovery rollout
pkg/connector/sync.go, pkg/connector/userinfo.go, pkg/connector/creategroup.go, pkg/connector/e2ee_keys.go
Replaces inline refresh-or-logout checks with shouldAttemptTokenRecovery across sync, userinfo, create-group, and E2EE key paths.
Auth recovery and forced logout tests
pkg/connector/auth_recovery_test.go, pkg/connector/forced_logout_test.go
Updates auth-recovery test cases and adds tests covering logged-out validation, override login, and session-invalidated recovery rejection.

Reaction API and connector reaction tests

Layer / File(s) Summary
LINE reaction API request body and error tests
pkg/line/reaction_test.go
Adds test HTTP transport and tests for React/CancelReaction request serialization, IsInvalidPaidReactionType, and IsNotAMemberError detection.
Connector reaction helper tests
pkg/connector/reaction_test.go
Adds tests for normalizeMatrixReactionKey, nextReqSeq, trackReqSeq, parseLineSticonURL, linePaidReactionForMatrixEmoji, parseReactionTargetMessageID, and reaction error constructors.

LTSM crypto, embind, and runner secret tests

Layer / File(s) Summary
LTSM crypto primitive and Mix66 tests
pkg/ltsm/crypto_test.go, pkg/ltsm/mix_test.go
Adds roundtrip and format tests for EncryptV1/DecryptV1, EncryptV2/DecryptV2, processMIDforAAD, pkcs7Pad/pkcs7Unpad, and Mix66 output range.
LTSM embind WASM module tests
pkg/ltsm/embind_test.go, pkg/ltsm/embind_debug_test.go
Adds initModule and tests for embind registration plus SecureKey, Hmac, AesKey, and Curve25519Key crypto flows, including debug logging helpers.
LTSM V2 vector generation and transpiled Mix66 validation
pkg/ltsm/vector_test.go, pkg/ltsm/wbc_test.go
Adds V2 vector generation tests and validates transpiled Mix66 output against the hand-written implementation.
Runner secret, signature, and debug export tests
pkg/runner_secret_test.go, pkg/runner_vectors_replay_test.go
Adds unit and integration tests for login secret generation, PIN format, public-key normalization, runner secret/signature generation, debug export behavior, and skips the obsolete vector replay test.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • beeper/line#188: Introduced the LINE auth token recovery helpers this PR extends with shouldAttemptTokenRecovery, logout marking, and session-invalidated handling.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: setting bridge state to badcredentials after logout from another instance.
Description check ✅ Passed The description is brief but directly describes the same forced-logout behavior changed in the PR.
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-37702

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

Comment thread pkg/connector/sync.go
Comment thread pkg/line/errors.go
return false
}
return strings.Contains(err.Error(), "V3_TOKEN_CLIENT_LOGGED_OUT")
return strings.Contains(err.Error(), "V3_TOKEN_CLIENT_LOGGED_OUT") || IsInvalidSenderKey(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.

Code 83 may be too broad for IsLoggedOut: every API call site now reads code 83 ("invalid sender key") as a forced-logout signal, which means markLoggedOutByOtherClient blanks the access token and the bridge enters StateBadCredentials after a single such error — even when it bubbles up from message decryption / sendMessage / E2EE key resolution where it could simply mean a peer's sender key rotated. Can you confirm code 83 always implies the Chrome-extension session is gone? If not, consider scoping the IsInvalidSenderKey mapping to specific call sites (e.g., token validation only) instead of routing it through the generic IsLoggedOut.

Comment thread pkg/connector/handlers/handler.go

@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.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/connector/client.go (1)

106-116: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Don't clear sessionInvalidated from generic token writes.

setTokens can still run in a refresh/relogin goroutine that started before markLoggedOutByOtherClient(). If that older request stores a non-empty token here, it flips sessionInvalidated back to false and can undo the forced-reconnect gate before the user clicks Reconnect. Reset this flag only from an explicit user-driven reconnect path, or guard token writes with an invalidation epoch so stale recoveries can't re-arm the session.

🤖 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/client.go` around lines 106 - 116, Generic token writes in
LineClient.setTokens are incorrectly clearing sessionInvalidated, which can undo
the forced-reconnect state from markLoggedOutByOtherClient. Update setTokens so
it only assigns AccessToken/RefreshToken under tokenMu and does not reset
sessionInvalidated for ordinary refresh/relogin flows. If needed, move the flag
reset to the explicit user-driven reconnect path or add an invalidation epoch
check so stale goroutines cannot re-arm the session.
🧹 Nitpick comments (1)
pkg/connector/auth_recovery_test.go (1)

203-207: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the invalidation helper instead of toggling the field directly.

Setting lc.sessionInvalidated here bypasses the invalidation API added in this cohort, so this test can miss regressions if invalidation later clears more state than the boolean. Drive the setup through the helper used by production code instead.

🤖 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/auth_recovery_test.go` around lines 203 - 207, The test is
setting the session invalidation flag directly instead of using the invalidation
helper, which can bypass any additional state cleanup. Update the setup in
auth_recovery_test.go to drive invalidation through the same helper used by
production code on the lc instance, and keep the assertion against
isTokenError(errAuthRequired) unchanged so the test exercises the real
invalidation path.
🤖 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.

Inline comments:
In `@pkg/connector/connector.go`:
- Line 324: The PIN continuation path in loginWithCredentials is using
ll.Certificate, but fresh logins can leave that field empty because the
certificate only arrives in the res.Certificate branch. Update the login flow
around the res.Certificate handling and the AwaitingPIN continuation so the
challenge certificate is persisted on the login state before any PIN retry, then
have the continuation reuse that stored certificate instead of relying on
ll.Certificate being present.

In `@pkg/connector/sync.go`:
- Around line 1239-1241: The SSE auth retry path in sync logic is bypassing the
centralized token-recovery gate, so update the 401/403 handling in the sync flow
to consult shouldAttemptTokenRecovery before calling recoverToken. Keep the
behavior in the existing SSE error branch, but route recovery through the same
predicate used elsewhere so session-invalidated safeguards and any other
centralized conditions are honored. Use the lc.recoverToken and
shouldAttemptTokenRecovery symbols to locate and align this logic.

In `@pkg/ltsm/embind_test.go`:
- Around line 307-311: The roundtrip test is ignoring errors from
imp.ReadEmvalBytes, which can hide a failed export and make the equality check
pass on empty data. Update the affected roundtrip assertions to capture and
check the ReadEmvalBytes error right after exporting the key, and apply the same
fix in the matching re-import path so both paths fail fast instead of comparing
potentially empty byte slices.

In `@pkg/ltsm/vector_test.go`:
- Line 10: The vector test helpers are currently always emitting sensitive
material and writing to fixed temp files, so gate that behavior behind an
explicit opt-in in vector_test.go. Update the export paths in the relevant test
helpers and calls so dumps only happen when enabled, and ensure any file
creation uses restrictive 0600 permissions; use the existing test/vector export
symbols in the file to locate the dump logic and adjust the shared helper paths
consistently.

In `@pkg/ltsm/wbc_test.go`:
- Around line 56-69: Before slicing mod.mem with the hard-coded addresses in the
WBC test, add an explicit memory bounds check so the test fails clearly instead
of panicking if NewModule changes the allocation size. Update the setup around
xAddr, yAddr, dstAddr in the test to validate that each address range plus the
66-byte inputs fits within mod.mem before calling copy, and use a test failure
message that points to the offending address range. Also apply the same guard to
the other hard-coded memory write at the second referenced location in the test.
- Around line 83-89: The Mix66 test currently logs a success message even after
a mismatch because the loop in wbc_test.go only calls t.Errorf and then
continues. Update the test logic around the transOut/refOut comparison so that
the success log after the loop is only reached when no mismatches are found,
using the existing comparison loop in the test function (or a helper around it)
to short-circuit or track failures before calling t.Logf.

In `@pkg/runner_vectors_replay_test.go`:
- Around line 7-13: Update the skip note in TestReplayLTSMVectors so the
replacement-test breadcrumb points to the current LTSM coverage under
pkg/ltsm/*.go instead of pkg/wasm/ltsm_test.go. Keep the comment accurate to the
wazero-based Runner rewrite and ensure the reference name stays tied to
TestReplayLTSMVectors so future maintainers can find the right test location.

---

Outside diff comments:
In `@pkg/connector/client.go`:
- Around line 106-116: Generic token writes in LineClient.setTokens are
incorrectly clearing sessionInvalidated, which can undo the forced-reconnect
state from markLoggedOutByOtherClient. Update setTokens so it only assigns
AccessToken/RefreshToken under tokenMu and does not reset sessionInvalidated for
ordinary refresh/relogin flows. If needed, move the flag reset to the explicit
user-driven reconnect path or add an invalidation epoch check so stale
goroutines cannot re-arm the session.

---

Nitpick comments:
In `@pkg/connector/auth_recovery_test.go`:
- Around line 203-207: The test is setting the session invalidation flag
directly instead of using the invalidation helper, which can bypass any
additional state cleanup. Update the setup in auth_recovery_test.go to drive
invalidation through the same helper used by production code on the lc instance,
and keep the assertion against isTokenError(errAuthRequired) unchanged so the
test exercises the real invalidation path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e90093c9-cfeb-4c98-a43e-e2c8695884cb

📥 Commits

Reviewing files that changed from the base of the PR and between be98960 and df576df.

📒 Files selected for processing (23)
  • pkg/connector/auth_recovery.go
  • pkg/connector/auth_recovery_test.go
  • pkg/connector/client.go
  • pkg/connector/connector.go
  • pkg/connector/creategroup.go
  • pkg/connector/e2ee_keys.go
  • pkg/connector/forced_logout_test.go
  • pkg/connector/handle_message.go
  • pkg/connector/handlers/handler.go
  • pkg/connector/reaction_test.go
  • pkg/connector/sync.go
  • pkg/connector/userinfo.go
  • pkg/line/errors.go
  • pkg/line/errors_test.go
  • pkg/line/reaction_test.go
  • pkg/ltsm/crypto_test.go
  • pkg/ltsm/embind_debug_test.go
  • pkg/ltsm/embind_test.go
  • pkg/ltsm/mix_test.go
  • pkg/ltsm/vector_test.go
  • pkg/ltsm/wbc_test.go
  • pkg/runner_secret_test.go
  • pkg/runner_vectors_replay_test.go
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: build-docker
  • GitHub Check: Lint with 1.25
  • GitHub Check: Lint with 1.25
🧰 Additional context used
📓 Path-based instructions (3)
**/*.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/connector/handlers/handler.go
  • pkg/ltsm/wbc_test.go
  • pkg/ltsm/mix_test.go
  • pkg/line/errors_test.go
  • pkg/runner_vectors_replay_test.go
  • pkg/ltsm/embind_debug_test.go
  • pkg/connector/handle_message.go
  • pkg/ltsm/vector_test.go
  • pkg/connector/creategroup.go
  • pkg/line/errors.go
  • pkg/connector/reaction_test.go
  • pkg/connector/forced_logout_test.go
  • pkg/connector/e2ee_keys.go
  • pkg/runner_secret_test.go
  • pkg/connector/auth_recovery_test.go
  • pkg/ltsm/crypto_test.go
  • pkg/line/reaction_test.go
  • pkg/connector/auth_recovery.go
  • pkg/connector/userinfo.go
  • pkg/connector/sync.go
  • pkg/ltsm/embind_test.go
  • pkg/connector/connector.go
  • pkg/connector/client.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/connector/handlers/handler.go
  • pkg/ltsm/wbc_test.go
  • pkg/ltsm/mix_test.go
  • pkg/line/errors_test.go
  • pkg/runner_vectors_replay_test.go
  • pkg/ltsm/embind_debug_test.go
  • pkg/connector/handle_message.go
  • pkg/ltsm/vector_test.go
  • pkg/connector/creategroup.go
  • pkg/line/errors.go
  • pkg/connector/reaction_test.go
  • pkg/connector/forced_logout_test.go
  • pkg/connector/e2ee_keys.go
  • pkg/runner_secret_test.go
  • pkg/connector/auth_recovery_test.go
  • pkg/ltsm/crypto_test.go
  • pkg/line/reaction_test.go
  • pkg/connector/auth_recovery.go
  • pkg/connector/userinfo.go
  • pkg/connector/sync.go
  • pkg/ltsm/embind_test.go
  • pkg/connector/connector.go
  • pkg/connector/client.go
pkg/connector/connector.go

📄 CodeRabbit inference engine (AGENTS.md)

Implement bridgev2.NetworkConnector and bridgev2.NetworkAPI interfaces in the connector package for bridge logic

Files:

  • pkg/connector/connector.go
🔇 Additional comments (22)
pkg/connector/auth_recovery_test.go (1)

17-18: LGTM!

Also applies to: 192-202, 208-214

pkg/connector/forced_logout_test.go (2)

13-46: LGTM!

Also applies to: 48-85


3-11: 📐 Maintainability & Code Quality

Run the Go hygiene checks for pkg/connector before merge. Keep the new tests formatted with gofmt/goimports, and make sure go vet and staticcheck pass on the package.

pkg/line/reaction_test.go (1)

1-141: LGTM!

pkg/connector/reaction_test.go (1)

1-411: LGTM!

pkg/connector/connector.go (1)

149-149: LGTM!

Also applies to: 162-162, 186-210, 217-227, 358-358, 410-434, 446-446

pkg/connector/sync.go (1)

37-46: LGTM!

Also applies to: 77-85, 293-312, 457-466, 500-541, 867-878, 1153-1165, 1727-1736, 1789-1797, 1872-1880, 1920-1928

pkg/connector/userinfo.go (1)

141-152: LGTM!

Also applies to: 210-250, 333-341, 379-386

pkg/connector/creategroup.go (1)

34-40: LGTM!

Also applies to: 162-190, 259-265

pkg/connector/e2ee_keys.go (1)

32-51: LGTM!

Also applies to: 169-179

pkg/runner_secret_test.go (2)

11-124: LGTM!


126-159: 🩺 Stability & Availability

Confirm runner reset between env-gated tests. If GetRunner caches process-wide state, t.Setenv("LTSM_ENABLE_DEBUG_EXPORT", "1") won’t affect an already-initialized runner and these tests become order-dependent.

pkg/line/errors.go (1)

31-42: LGTM!

pkg/line/errors_test.go (1)

54-56: LGTM!

pkg/connector/client.go (1)

21-31: LGTM!

Also applies to: 49-52, 119-137, 219-220, 252-288, 347-350, 364-367, 458-458, 486-497, 537-543

pkg/connector/auth_recovery.go (1)

40-45: LGTM!

Also applies to: 62-64, 73-82

pkg/connector/handlers/handler.go (1)

21-21: LGTM!

Also applies to: 36-42

pkg/connector/handle_message.go (1)

33-33: LGTM!

pkg/ltsm/crypto_test.go (1)

1-196: LGTM!

pkg/ltsm/mix_test.go (1)

1-20: LGTM!

pkg/ltsm/embind_debug_test.go (1)

1-54: LGTM!

pkg/ltsm/wbc_test.go (1)

10-50: 📐 Maintainability & Code Quality

Run gofmt on the stub import declarations.

if ll.AwaitingPIN {
client := line.NewClient("")
res, err := client.Login(ll.Email, ll.Password, "")
res, err := loginWithCredentials(ll.Email, ll.Password, ll.Certificate)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Persist the challenge certificate before the PIN continuation.

Line 324 now depends on ll.Certificate, but the res.Certificate branch only sets AwaitingPIN; fresh logins clear ll.Certificate, so the continuation can retry with an empty certificate.

Proposed fix
 if res.Certificate != "" {
+	ll.Certificate = res.Certificate
 	ll.AwaitingPIN = true
 	return &bridgev2.LoginStep{
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
res, err := loginWithCredentials(ll.Email, ll.Password, ll.Certificate)
if res.Certificate != "" {
ll.Certificate = res.Certificate
ll.AwaitingPIN = true
return &bridgev2.LoginStep{
🤖 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/connector.go` at line 324, The PIN continuation path in
loginWithCredentials is using ll.Certificate, but fresh logins can leave that
field empty because the certificate only arrives in the res.Certificate branch.
Update the login flow around the res.Certificate handling and the AwaitingPIN
continuation so the challenge certificate is persisted on the login state before
any PIN retry, then have the continuation reuse that stored certificate instead
of relying on ll.Certificate being present.

Comment thread pkg/connector/sync.go
Comment thread pkg/ltsm/embind_test.go
Comment on lines +307 to +311
h1, err := imp.CallMethod("SecureKey", "exportKey", skPtr)
if err != nil {
t.Fatalf("exportKey failed: %v", err)
}
key1, _ := imp.ReadEmvalBytes(h1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Don't ignore embind read errors in the roundtrip assertion.

Ignoring ReadEmvalBytes on Line 311 and Line 339 can turn this into a false positive: the re-import path may be fed empty bytes, and both signatures can collapse to "" while the equality check still passes.

Suggested fix
 	h1, err := imp.CallMethod("SecureKey", "exportKey", skPtr)
 	if err != nil {
 		t.Fatalf("exportKey failed: %v", err)
 	}
-	key1, _ := imp.ReadEmvalBytes(h1)
+	key1, err := imp.ReadEmvalBytes(h1)
+	if err != nil {
+		t.Fatalf("read exportKey result failed: %v", err)
+	}
 	imp.emval.DecRef(h1)
+	if len(key1) == 0 {
+		t.Fatal("exportKey returned empty bytes")
+	}
 	t.Logf("exported key: %d bytes", len(key1))
@@
-		sig, _ := imp.ReadEmvalBytes(rh)
+		sig, err := imp.ReadEmvalBytes(rh)
+		if err != nil {
+			t.Fatalf("read digest result failed: %v", err)
+		}
 		imp.emval.DecRef(rh)
 		return base64.StdEncoding.EncodeToString(sig)
 	}

Also applies to: 326-341

🤖 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/ltsm/embind_test.go` around lines 307 - 311, The roundtrip test is
ignoring errors from imp.ReadEmvalBytes, which can hide a failed export and make
the equality check pass on empty data. Update the affected roundtrip assertions
to capture and check the ReadEmvalBytes error right after exporting the key, and
apply the same fix in the matching re-import path so both paths fail fast
instead of comparing potentially empty byte slices.

Comment thread pkg/ltsm/vector_test.go
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Gate vector dumps instead of emitting them on every test run.

These tests currently dump private-key/shared-secret vector material to stdout/logs and fixed world-readable /tmp files. Make vector export opt-in and write with 0600 permissions.

Proposed adjustment
-	"fmt"
 	"os"
 	"testing"
@@
-	t.Logf("\n--- JSON Test Vector ---\n%s", string(jsonBytes))
+	if os.Getenv("LTSM_LOG_TEST_VECTORS") != "" {
+		t.Logf("\n--- JSON Test Vector ---\n%s", string(jsonBytes))
+	}
 
-	// Write to file
-	if err := os.WriteFile("/tmp/ltsm_v2_test_vector.json", jsonBytes, 0644); err != nil {
-		t.Logf("Warning: could not write test vector file: %v", err)
-	} else {
-		t.Logf("Test vector written to /tmp/ltsm_v2_test_vector.json")
+	if out := os.Getenv("LTSM_V2_TEST_VECTOR_PATH"); out != "" {
+		if err := os.WriteFile(out, jsonBytes, 0600); err != nil {
+			t.Logf("Warning: could not write test vector file: %v", err)
+		} else {
+			t.Logf("Test vector written to %s", out)
+		}
 	}
@@
-	fmt.Println(string(jsonBytes))
+	if os.Getenv("LTSM_LOG_TEST_VECTORS") != "" {
+		t.Logf("%s", string(jsonBytes))
+	}
 
-	// Write to file
-	if err := os.WriteFile("/tmp/ltsm_v2_deterministic_vector.json", jsonBytes, 0644); err != nil {
-		t.Logf("Warning: could not write test vector file: %v", err)
-	} else {
-		t.Logf("Test vector written to /tmp/ltsm_v2_deterministic_vector.json")
+	if out := os.Getenv("LTSM_V2_DETERMINISTIC_VECTOR_PATH"); out != "" {
+		if err := os.WriteFile(out, jsonBytes, 0600); err != nil {
+			t.Logf("Warning: could not write test vector file: %v", err)
+		} else {
+			t.Logf("Test vector written to %s", out)
+		}
 	}

Also applies to: 197-204, 369-377

🤖 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/ltsm/vector_test.go` at line 10, The vector test helpers are currently
always emitting sensitive material and writing to fixed temp files, so gate that
behavior behind an explicit opt-in in vector_test.go. Update the export paths in
the relevant test helpers and calls so dumps only happen when enabled, and
ensure any file creation uses restrictive 0600 permissions; use the existing
test/vector export symbols in the file to locate the dump logic and adjust the
shared helper paths consistently.

Comment thread pkg/ltsm/wbc_test.go
Comment on lines +56 to +69
xAddr := uint32(8000000)
yAddr := uint32(8001000)
dstAddr := uint32(8002000)

// Create test inputs (base-8 digits, 0..7)
var x, y [66]byte
for i := 0; i < 66; i++ {
x[i] = byte((i * 3) % 8)
y[i] = byte((i * 5) % 8)
}

// Copy inputs to module memory
copy(mod.mem[xAddr:], x[:])
copy(mod.mem[yAddr:], y[:])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Validate module memory before slicing hard-coded offsets.

If NewModule changes its memory size, these slices panic instead of producing a useful test failure.

Proposed guard
 	xAddr := uint32(8000000)
 	yAddr := uint32(8001000)
 	dstAddr := uint32(8002000)
+	requiredMem := int(dstAddr) + 66
+	if len(mod.mem) < requiredMem {
+		t.Fatalf("module memory too small: len=%d, need=%d", len(mod.mem), requiredMem)
+	}

Also applies to: 80-80

🤖 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/ltsm/wbc_test.go` around lines 56 - 69, Before slicing mod.mem with the
hard-coded addresses in the WBC test, add an explicit memory bounds check so the
test fails clearly instead of panicking if NewModule changes the allocation
size. Update the setup around xAddr, yAddr, dstAddr in the test to validate that
each address range plus the 66-byte inputs fits within mod.mem before calling
copy, and use a test failure message that points to the offending address range.
Also apply the same guard to the other hard-coded memory write at the second
referenced location in the test.

Comment thread pkg/ltsm/wbc_test.go
Comment on lines +83 to +89
for i := 0; i < 66; i++ {
if transOut[i] != refOut[i] {
t.Errorf("mismatch at index %d: transpiled=%d, reference=%d", i, transOut[i], refOut[i])
}
}

t.Logf("transpiled mix output matches hand-written Mix66: %v", transOut[:10])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Stop before logging success on mismatches.

t.Errorf marks the test failed but continues, so Line 89 can still log that outputs match.

Proposed fix
-	for i := 0; i < 66; i++ {
+	for i := range transOut {
 		if transOut[i] != refOut[i] {
-			t.Errorf("mismatch at index %d: transpiled=%d, reference=%d", i, transOut[i], refOut[i])
+			t.Fatalf("mismatch at index %d: transpiled=%d, reference=%d", i, transOut[i], refOut[i])
 		}
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for i := 0; i < 66; i++ {
if transOut[i] != refOut[i] {
t.Errorf("mismatch at index %d: transpiled=%d, reference=%d", i, transOut[i], refOut[i])
}
}
t.Logf("transpiled mix output matches hand-written Mix66: %v", transOut[:10])
for i := range transOut {
if transOut[i] != refOut[i] {
t.Fatalf("mismatch at index %d: transpiled=%d, reference=%d", i, transOut[i], refOut[i])
}
}
t.Logf("transpiled mix output matches hand-written Mix66: %v", transOut[:10])
🤖 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/ltsm/wbc_test.go` around lines 83 - 89, The Mix66 test currently logs a
success message even after a mismatch because the loop in wbc_test.go only calls
t.Errorf and then continues. Update the test logic around the transOut/refOut
comparison so that the success log after the loop is only reached when no
mismatches are found, using the existing comparison loop in the test function
(or a helper around it) to short-circuit or track failures before calling
t.Logf.

Comment on lines +7 to +13
// TestReplayLTSMVectors previously replayed captured JSON IPC vectors through
// the Node.js runner's call() method. Since the runner now uses wazero directly
// (no JSON IPC), this test needs a rewrite to use the typed Runner methods.
// The WASM-level correctness is covered by pkg/wasm/ltsm_test.go (sign vectors,
// AES roundtrip, Curve25519 roundtrip).
func TestReplayLTSMVectors(t *testing.T) {
t.Skip("vector replay test needs rewrite for wazero-based runner (no JSON IPC)")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the replacement-test reference.

The skip note points readers to pkg/wasm/ltsm_test.go, but the supplied PR context places the current LTSM coverage under pkg/ltsm/*.go. Please update the breadcrumb to the actual replacement tests so this does not send the next maintainer to a stale path.

🤖 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/runner_vectors_replay_test.go` around lines 7 - 13, Update the skip note
in TestReplayLTSMVectors so the replacement-test breadcrumb points to the
current LTSM coverage under pkg/ltsm/*.go instead of pkg/wasm/ltsm_test.go. Keep
the comment accurate to the wazero-based Runner rewrite and ensure the reference
name stays tied to TestReplayLTSMVectors so future maintainers can find the
right test location.

@highesttt highesttt merged commit bbb1a5d into main Jun 30, 2026
10 checks passed
@highesttt highesttt deleted the highest/plat-37702 branch June 30, 2026 15:45
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