Skip to content

test: cover E2EE with the real client and a WebCrypto implementation - #7866

Open
diegolmello wants to merge 13 commits into
fix/e2ee-no-session-key-and-key-distribution-stopfrom
test/e2ee-coverage-plan
Open

diegolmello wants to merge 13 commits into
fix/e2ee-no-session-key-and-key-distribution-stopfrom
test/e2ee-coverage-plan

Conversation

@diegolmello

@diegolmello diegolmello commented Oct 30, 2026 •

Copy link
Copy Markdown
Member

Proposed changes

This adds unit coverage for end-to-end encryption. The tests run the real Encryption client against a WebCrypto-backed fake of @rocket.chat/mobile-crypto, not jest.fn crypto stubs. The fixtures are produced the way the Rocket.Chat web client produces them, so the tests also check that mobile and web stay compatible.

Test infrastructure

  • __mocks__/@rocket.chat/mobile-crypto.ts: a Node WebCrypto implementation of the native module (AES-CBC/GCM/CTR, RSA-OAEP, PBKDF2, HMAC, SHA, and file encrypt/decrypt).
  • app/lib/encryption/__tests__/fixtures/: golden fixtures for the user key pair, v1 and v2 room keys, messages and a file. generate.mjs regenerates them, following the web client's algorithms.
  • app/lib/testUtils/inMemoryDatabase.ts (in-memory WatermelonDB) and app/lib/testUtils/e2eeWebClient.ts.
  • Babel transforms dynamic imports under Jest. The sniffler test map now covers the E2EE call sites.

Coverage

  • Client lifecycle, key distribution, and decryption of stored messages, threads, last messages, files and the file queue.
  • Prefixed base64, private key parsing, passphrases and the password policy.
  • Encryption saga start, stop and key decoding.
  • Call sites:
    • In an encrypted room, sendMessage, resendMessage and editMessage send t: 'e2e' with content and no plaintext msg.
    • In an encrypted upload, sendFileMessageV2 sends ciphertext in both the form content and the mediaConfirm payload.
    • The client answers e2ekeyRequest only for a matching key ID.
    • Suggested group keys are accepted or rejected.
    • Search filters encrypted messages.
  • Existing room.test.ts, the encryption reducer test and the SwitchItemEncrypted tests are rewritten so they don't depend on test order.

I checked each new test by breaking the code it guards: every test failed on the broken code and passed once the code was restored.

Issue(s)

https://rocketchat.atlassian.net/browse/NATIVE-1878

How to test or reproduce

Unit tests:

  1. TZ=UTC pnpm test app/lib/encryption app/lib/methods app/sagas/__tests__ app/reducers/encryption.test.ts
  2. Without TZ=UTC, some unrelated suites fail locally.

Screenshots

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • Improvement (non-breaking change which improves a current function)
  • New feature (non-breaking change which adds functionality)
  • Documentation update (if none of the other choices apply)

Checklist

  • I have read the CONTRIBUTING doc
  • I have signed the CLA
  • Lint and unit tests pass locally with my changes
  • I have added tests that prove my fix is effective or that my feature works (if applicable)
  • I have added necessary documentation (if applicable)
  • Any dependent changes have been merged and published in downstream modules

Further comments

  • Why a WebCrypto fake: the existing tests mocked crypto calls one by one, so they couldn't catch format or compatibility regressions. The fake runs real cryptography in Node, and the fixtures come from the web client's algorithms, not from the mobile code. A test therefore fails if mobile stops reading what web writes, or web stops reading what mobile writes.

Summary by CodeRabbit

  • Bug Fixes
    • Messages that cannot be encrypted are no longer sent and are marked as failed.
    • Improved the reliability of encryption key distribution during startup and shutdown.
  • Tests
    • Expanded coverage for encrypted messages, files, room keys, and encryption lifecycle behavior, including compatibility with web-client encryption formats.

…ependent and anchor E2EE flow in sniffler map
stop() never cleared the key distribution interval, and the interval was
only stored after the first run, so a stop during that run left it alive.
The next sign-in then skipped its first distribution because a timer
already existed.

NATIVE-1870
generatePassphrase loads its word list with import(), which Jest's
CommonJS runtime rejects without --experimental-vm-modules.
Runs Encryption against an in-memory WatermelonDB and the WebCrypto
fake: initialize, stop, persistKeys, createKeys, changePassword and its
force gate, provideRoomKeyToUser, getSample and key distribution,
pending and last-message decryption, decryptMessages, decryptFiles and
the file decryption queue.

NATIVE-1856
sendMessage and resendMessage passed an undefined encryption result to
chat.sendMessage, which threw into a catch that only logged, so the message
stayed TEMP with no retry. They now post nothing and leave it in ERROR.

NATIVE-1869
@coderabbitai

coderabbitai Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b0e69009-efeb-441f-b9a1-220ba5116281
Commits

Reviewing files that changed from the base of the PR and between 9f57f31 and 054e230.

Files ignored due to path filters: 1
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
Files selected for processing: 35
  • .sniffler/test-map.json
  • __mocks__/@rocket.chat/mobile-crypto.ts
  • app/containers/NewMediaCall/VoipCallLifecycle.integration.test.tsx
  • app/lib/encryption/__tests__/decryption.test.ts
  • app/lib/encryption/__tests__/file.test.ts
  • app/lib/encryption/__tests__/fixtures/file.json
  • app/lib/encryption/__tests__/fixtures/generate.mjs
  • app/lib/encryption/__tests__/fixtures/messages.json
  • app/lib/encryption/__tests__/fixtures/roomKeys.json
  • app/lib/encryption/__tests__/fixtures/user.json
  • app/lib/encryption/__tests__/keyDistribution.test.ts
  • app/lib/encryption/__tests__/lifecycle.test.ts
  • app/lib/encryption/__tests__/message.test.ts
  • app/lib/encryption/__tests__/privateKey.test.ts
  • app/lib/encryption/__tests__/roomKey.test.ts
  • app/lib/encryption/__tests__/suggestedGroupKey.test.ts
  • app/lib/encryption/__tests__/utils.test.ts
  • app/lib/encryption/encryption.test.ts
  • app/lib/encryption/encryption.ts
  • app/lib/encryption/room.test.ts
  • app/lib/methods/__tests__/search.e2ee.test.ts
  • app/lib/methods/__tests__/sendMessage.e2ee.test.ts
  • app/lib/methods/sendFileMessage/__tests__/sendFileMessageV2.e2ee.test.ts
  • app/lib/methods/sendMessage.ts
  • app/lib/methods/subscriptions/__tests__/rooms.e2ekeyRequest.test.ts
  • app/lib/testUtils/e2eeWebClient.ts
  • app/lib/testUtils/inMemoryDatabase.ts
  • app/reducers/encryption.test.ts
  • app/sagas/__tests__/encryption.decodeKey.test.ts
  • app/sagas/__tests__/encryption.init.test.ts
  • app/views/CreateChannelView/RoomSettings/SwitchItemEncrypted.test.tsx
  • app/views/E2EEncryptionSecurityView/__tests__/utils.test.ts
  • app/views/RoomView/hooks/__tests__/useE2EEStatus.test.ts
  • babel.config.js
  • package.json
Files with no reviewable changes: 3
  • app/containers/NewMediaCall/VoipCallLifecycle.integration.test.tsx
  • app/views/RoomView/hooks/tests/useE2EEStatus.test.ts
  • app/lib/encryption/encryption.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 3 remain after this review.

Recent review details
Context from checks skipped due to timeout: 3
  • GitHub Check: CodeRabbit
  • GitHub Check: ESLint and Test / run-eslint-and-test
  • GitHub Check: E2E Shard Preflight
Additional context used
ast-grep (0.50.0)
app/lib/encryption/__tests__/message.test.ts

[warning] 68-68: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(^${roomKeys.v1.keyID})
Note: [CWE-1333] Inefficient Regular Expression Complexity

(regexp-from-variable)

Betterleaks (1.9.0)
app/views/E2EEncryptionSecurityView/__tests__/utils.test.ts

[low] 3-3: Detected a potential hardcoded password literal, which may expose account credentials. Found in a test, fixture or example file.

(generic-password)

app/lib/encryption/__tests__/fixtures/roomKeys.json

[low] 5-5: Detected a Generic API Key, potentially exposing access to various services and sensitive operations. Found in a test, fixture or example file.

(generic-api-key)

app/lib/encryption/__tests__/privateKey.test.ts

[low] 9-9: Detected a potential hardcoded password literal, which may expose account credentials. Found in a test, fixture or example file.

(generic-password)

app/lib/encryption/__tests__/lifecycle.test.ts

[low] 41-41: Detected a potential hardcoded password literal, which may expose account credentials. Found in a test, fixture or example file.

(generic-password)

app/lib/encryption/__tests__/fixtures/generate.mjs

[low] 11-11: Detected a potential hardcoded password literal, which may expose account credentials. Found in a test, fixture or example file. The same value appears in 1 other place in this change.

(generic-password)

Additional comments: 32
__mocks__/@rocket.chat/mobile-crypto.ts: 1

1-234: LGTM!

app/lib/encryption/__tests__/fixtures/generate.mjs: 1

1-125: LGTM!

app/lib/encryption/__tests__/fixtures/file.json: 1

1-13: LGTM!

app/lib/encryption/__tests__/fixtures/messages.json: 1

1-15: LGTM!

app/lib/encryption/__tests__/fixtures/roomKeys.json: 1

1-12: LGTM!

app/lib/encryption/__tests__/fixtures/user.json: 1

1-8: LGTM!

app/lib/testUtils/e2eeWebClient.ts: 1

1-51: LGTM!

app/lib/testUtils/inMemoryDatabase.ts: 1

1-49: LGTM!

babel.config.js: 1

28-30: LGTM!

package.json: 1

171-171: LGTM!

app/lib/encryption/room.test.ts: 1

1-13: LGTM!

Also applies to: 27-30, 77-117, 138-144, 158-192, 233-247, 285-296, 307-337, 348-363, 397-436, 466-499, 528-654, 698-702, 716-724, 742-748, 787-810, 1025-1109, 1125-1125, 1149-1149, 1161-1161

app/lib/encryption/__tests__/message.test.ts: 1

1-102: LGTM!

app/lib/encryption/__tests__/privateKey.test.ts: 1

1-69: LGTM!

app/lib/encryption/__tests__/roomKey.test.ts: 1

1-103: LGTM!

app/lib/encryption/__tests__/suggestedGroupKey.test.ts: 1

1-93: LGTM!

app/lib/encryption/__tests__/utils.test.ts: 1

1-36: LGTM!

app/lib/encryption/encryption.ts: 1

131-134: LGTM!

Also applies to: 538-538

app/lib/encryption/__tests__/decryption.test.ts: 1

1-218: LGTM!

app/lib/encryption/__tests__/keyDistribution.test.ts: 1

1-226: LGTM!

app/lib/encryption/__tests__/lifecycle.test.ts: 1

1-215: LGTM!

app/reducers/encryption.test.ts: 1

2-2: LGTM!

Also applies to: 6-7, 11-13, 16-21, 24-30, 32-36

app/sagas/__tests__/encryption.decodeKey.test.ts: 1

1-85: LGTM!

app/sagas/__tests__/encryption.init.test.ts: 1

1-147: LGTM!

.sniffler/test-map.json: 1

163-169: LGTM!

app/lib/encryption/__tests__/file.test.ts: 1

1-107: LGTM!

app/lib/methods/__tests__/search.e2ee.test.ts: 1

1-78: LGTM!

app/lib/methods/__tests__/sendMessage.e2ee.test.ts: 1

1-160: LGTM!

app/lib/methods/sendFileMessage/__tests__/sendFileMessageV2.e2ee.test.ts: 1

1-157: LGTM!

app/lib/methods/sendMessage.ts: 1

87-90: LGTM!

Also applies to: 123-123, 181-181, 208-208, 247-249

app/lib/methods/subscriptions/__tests__/rooms.e2ekeyRequest.test.ts: 1

1-145: LGTM!

app/views/CreateChannelView/RoomSettings/SwitchItemEncrypted.test.tsx: 1

1-37: LGTM!

app/views/E2EEncryptionSecurityView/__tests__/utils.test.ts: 1

1-19: LGTM!

Walkthrough

The pull request adds cryptographic test support and broad E2E encryption coverage. It also changes key-distribution interval handling and prevents message sends when encryption returns no message.

Changes

End-to-End Encryption

Layer / File(s) Summary
Cryptographic test support and fixtures
__mocks__/@rocket.chat/mobile-crypto.ts, app/lib/encryption/__tests__/fixtures/*, app/lib/testUtils/*, babel.config.js, package.json, .sniffler/test-map.json, app/containers/NewMediaCall/VoipCallLifecycle.integration.test.tsx, app/lib/encryption/encryption.test.ts, app/views/RoomView/hooks/__tests__/useE2EEStatus.test.ts
Adds a Node.js-backed crypto mock, Web Crypto helpers, V1 and V2 fixtures, an in-memory database factory, and test-environment configuration. Removes mobile-crypto Jest mocks and a test comment.
Room, key, and message cryptography
app/lib/encryption/room.test.ts, app/lib/encryption/__tests__/{message,privateKey,roomKey,suggestedGroupKey,utils}.test.ts
Tests real cryptographic room-key, message, and file operations, including V1 and V2 formats, key sharing, fallback behavior, and handshake cases.
Encryption lifecycle and key distribution
app/lib/encryption/encryption.ts, app/lib/encryption/__tests__/{decryption,keyDistribution,lifecycle}.test.ts, app/sagas/__tests__/encryption.*.test.ts, app/reducers/encryption.test.ts
Clears the key-distribution interval on stop and starts it before the initial distribution run. Adds coverage for lifecycle, key sharing, decryption, saga flows, and reducer state.
Encrypted application workflows
app/lib/methods/sendMessage.ts, app/lib/methods/__tests__/*e2ee.test.ts, app/lib/methods/sendFileMessage/__tests__/*e2ee.test.ts, app/lib/methods/subscriptions/__tests__/rooms.e2ekeyRequest.test.ts, app/lib/encryption/__tests__/file.test.ts, app/views/CreateChannelView/RoomSettings/SwitchItemEncrypted.test.tsx, app/views/E2EEncryptionSecurityView/__tests__/utils.test.ts
Adds tests for encrypted message and file flows, room search, key requests, file decryption, and E2E settings. Message sends now use an error status and skip the send call when encryption returns no message.

Priority: Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Bug fix

Suggested labels: type: bug

Suggested reviewers: rohit3523, otaviostasiak, dnlsilva

Merge Risk: Minimal · up to 054e2

This change mostly adds encryption test coverage. It also stops room key distribution on client stop and keeps encrypted messages in an error state when no session key is available, rather than leaving them stuck as sending. No merge-blocking risk was identified.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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.
Title check Passed The title clearly and concisely describes the main change: expanded E2EE test coverage using the real client and a WebCrypto implementation.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 2 functions across 26 files. (6 skipped: 6 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Errors were encountered while retrieving linked issues.

Errors: 1
  • JIRA integration encountered authorization issues. Please disconnect and reconnect the integration in the CodeRabbit UI.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@diegolmello diegolmello changed the title test: cover E2EE with the real client and fail no-key encrypted sends test: cover E2EE with the real client and a WebCrypto implementation Oct 30, 2026
@diegolmello
diegolmello changed the base branch from develop to fix/e2ee-no-session-key-and-key-distribution-stop October 30, 2026 18:00

This branch is waiting to be deployed

1 waiting deployment
approve_e2e_testing — c1478630 Waiting Oct 30, 2026 by diegolmello via E2E Hold #7674
ios_build — 054e230d Deployed Oct 30, 2026 by diegolmello via Build iOS / Hold #7666
android_build — 054e230d Deployed Oct 30, 2026 by diegolmello via Build Android / Hold #7666
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant