Skip to content

Connect generic mail securely - #86

Merged
rajzik merged 6 commits into
mainfrom
codex/issue-64-connect-generic-mail-securely
Jul 21, 2026
Merged

rajzik merged 6 commits into
mainfrom
codex/issue-64-connect-generic-mail-securely

Conversation

@rajzik

@rajzik rajzik commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor

Closes #64

Summary

  • add on-device reviewed-provider discovery and editable manual IMAP/SMTP/POP3 setup
  • enforce TLS 1.2+, system server-identity validation, and STARTTLS completion before authentication
  • support XOAUTH2, app-specific passwords, and passwords while retaining credentials and connection details in this device's Keychain
  • discover unambiguous IMAP SPECIAL-USE roles and request explicit mappings only for missing or ambiguous roles
  • clear generic-mail authorizations on Product Account sign-out or switch
  • document the privacy and transport boundaries and add release intent

Validation

  • mise exec -- pnpm lint
  • mise exec -- pnpm format
  • mise exec -- pnpm turbo run check-types
  • mise exec -- pnpm test
  • mise exec -- pnpm fallow
  • mise exec -- zsh scripts/check-apple-lint.zsh
  • mise exec -- xcodebuild test -quiet -project apps/unwired-mail/unwired-mail.xcodeproj -scheme unwired-mail -destination 'platform=iOS Simulator,name=iPhone 17'
  • mise exec -- xcodebuild test -quiet -project apps/unwired-mail/unwired-mail.xcodeproj -scheme unwired-mail -destination 'platform=iOS Simulator,name=iPhone 17' -only-testing:unwired-mailTests/GenericMailSetupServiceTests

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag @codesmith with what you need. Autofix is disabled.

Summary by CodeRabbit

  • New Features
    • Added “Other Mail Server” setup for IMAP/POP3/SMTP with reviewed iCloud Mail/Fastmail presets and manual configuration.
    • Includes endpoint discovery, “Load Saved Setup,” and guided “Verify and Save on This Device.”
    • Supports OAuth, app-specific passwords, and passwords; enforces TLS 1.2+ and correct STARTTLS flow; IMAP role mapping uses SPECIAL-USE with user prompts when ambiguous.
  • Bug Fixes
    • Clears device-local generic mail authorization data on sign-out or account switch.
  • Documentation
    • Expanded Local Development guide with setup, privacy/security boundaries, and mapping rules.
  • Tests
    • Added end-to-end coverage for discovery, verification ordering, persistence, and role-mapping/security edge cases.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Generic mail setup now supports reviewed or manual IMAP, POP3, and SMTP configuration, secure endpoint verification, IMAP role mapping, device-only Keychain persistence, account cleanup, and an integrated SwiftUI setup panel with tests and documentation.

Changes

Generic mail setup

Layer / File(s) Summary
Mail setup contracts and local storage
apps/unwired-mail/unwired-mail/Services/Mailbox/GenericMailSetupService.swift, apps/unwired-mail/unwired-mail/Services/Mailbox/GenericMailProviderCatalog.swift
Defines setup models, validation errors, provider discovery, and ThisDeviceOnly Keychain authorization persistence.
Secure endpoint verification
apps/unwired-mail/unwired-mail/Services/Mailbox/SystemGenericMailEndpointVerifier.swift
Implements TLS and STARTTLS negotiation, protocol authentication, response parsing, and IMAP SPECIAL-USE discovery.
Authorization orchestration and cleanup
apps/unwired-mail/unwired-mail/Services/Mailbox/GenericMailSetupService.swift, apps/unwired-mail/unwired-mail/Services/ProductAccount/ProductAccountSession.swift
Validates drafts, verifies endpoints, applies mappings, saves authorizations, and clears generic mail data during account cleanup.
Account setup UI and lifecycle wiring
apps/unwired-mail/unwired-mail/Features/Account/*, apps/unwired-mail/unwired-mail.xcodeproj/project.pbxproj
Adds the setup form, discovery controls, credential entry, role mapping, verification state, session invalidation, and target registration.
Behavior validation and release documentation
apps/unwired-mail/unwired-mailTests/GenericMailSetupServiceTests.swift, README.md, docs/adr/*, .changeset/*
Adds coverage for setup, security, persistence, role mapping, cancellation, and privacy, with matching documentation and release metadata.

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

Possibly related PRs

  • unwired-dev/product#81: Updates the mailbox-connection cleanup seam used by this generic mail setup integration.
  • unwired-dev/product#82: Refactors the Gmail connection cleanup and loading scope used by the mailbox-clear integration.

Sequence Diagram(s)

sequenceDiagram
  participant AccountView
  participant GenericMailSetupViewModel
  participant GenericMailSetupService
  participant SystemGenericMailEndpointVerifier
  participant KeychainGenericMailAuthorizationStore
  AccountView->>GenericMailSetupViewModel: submit setup form
  GenericMailSetupViewModel->>GenericMailSetupService: authorize draft
  GenericMailSetupService->>SystemGenericMailEndpointVerifier: verify mail endpoints
  SystemGenericMailEndpointVerifier-->>GenericMailSetupService: authenticated TLS results and mappings
  GenericMailSetupService->>KeychainGenericMailAuthorizationStore: save device-local authorization
  GenericMailSetupService-->>GenericMailSetupViewModel: return verified connection
  GenericMailSetupViewModel-->>AccountView: show saved state
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: secure generic mail setup.
Linked Issues check ✅ Passed The PR implements local discovery, manual fallback, TLS 1.2+ verification, Keychain storage, explicit role mapping, and tests covering the required boundaries.
Out of Scope Changes check ✅ Passed The changes stay focused on secure generic mail setup; docs, tests, and project wiring all support the stated objective.
✨ 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 codex/issue-64-connect-generic-mail-securely

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

rajzik commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4b2346d0e5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@rajzik

rajzik commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@rajzik

rajzik commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🤖 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
`@apps/unwired-mail/unwired-mail/Services/Mailbox/SystemGenericMailEndpointVerifier.swift`:
- Around line 241-249: Update readSMTPResponse to stop reading when any SMTP
final response line matching a three-digit code followed by a space is received,
rather than only when the expected code appears. Return the accumulated response
so callers can compare the actual code and produce the appropriate
authentication or transport error.
- Around line 90-93: Clear the pending unread response buffer after
negotiateStartTLS() completes and before task.startSecureConnection() in the
endpoint security upgrade flow. Ensure subsequent authenticate() reads begin
with an empty buffer while preserving the existing TLS negotiation and
connection sequence.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c22eefc2-9110-44f8-a9fd-eeba152fc7bc

📥 Commits

Reviewing files that changed from the base of the PR and between b6387a5 and a71fbc0.

📒 Files selected for processing (13)
  • .changeset/secure-generic-mail-setup.md
  • README.md
  • apps/unwired-mail/unwired-mail.xcodeproj/project.pbxproj
  • apps/unwired-mail/unwired-mail/Features/Account/AccountView.swift
  • apps/unwired-mail/unwired-mail/Features/Account/GenericMailSetupPanel.swift
  • apps/unwired-mail/unwired-mail/Services/Mailbox/GenericMailProviderCatalog.swift
  • apps/unwired-mail/unwired-mail/Services/Mailbox/GenericMailSetupService.swift
  • apps/unwired-mail/unwired-mail/Services/Mailbox/SystemGenericMailEndpointVerifier.swift
  • apps/unwired-mail/unwired-mail/Services/ProductAccount/ProductAccountSession.swift
  • apps/unwired-mail/unwired-mailTests/GenericMailSetupServiceTests.swift
  • docs/adr/0010-device-local-mailbox-authorization.md
  • docs/adr/0014-explicit-mailbox-role-mapping.md
  • docs/adr/0017-require-secure-mail-transport.md

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a71fbc024c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/unwired-mail/unwired-mail/Features/Account/GenericMailSetupPanel.swift Outdated

rajzik commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

rajzik commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

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.

Caution

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

⚠️ Outside diff range comments (3)
apps/unwired-mail/unwired-mail/Services/Mailbox/SystemGenericMailEndpointVerifier.swift (3)

86-88: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Parse the tagged IMAP completion line, not the full buffer. readIMAPResponse(tag:) already waits for the tagged response, but contains("A1 OK") / contains("A2 OK") can still match an earlier untagged line and treat a final NO or BAD as success. Check the tagged line itself and require an exact OK.

🤖 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
`@apps/unwired-mail/unwired-mail/Services/Mailbox/SystemGenericMailEndpointVerifier.swift`
around lines 86 - 88, Update readIMAPResponse(tag:) and the isPositiveGreeting
validation to parse the returned tagged completion line rather than searching
the full response buffer. Extract the line matching the requested tag and
require its completion status to be exactly OK, rejecting tagged NO or BAD
responses while preserving the existing authenticationFailed behavior.

175-182: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Parse IMAP LIST responses structurally. listedMailbox(in:) treats the last quoted pair as the mailbox name, so a valid response like * LIST (\Sent) "/" Sent is parsed as / instead of Sent. Literals and escaped quoted names are also unsupported. This can persist an incorrect SPECIAL-USE mapping; parse the LIST fields per RFC 3501 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
`@apps/unwired-mail/unwired-mail/Services/Mailbox/SystemGenericMailEndpointVerifier.swift`
around lines 175 - 182, Update listedMailbox(in:) to parse IMAP LIST responses
structurally according to RFC 3501 rather than selecting the last quoted pair.
Correctly handle unquoted mailbox names, quoted names such as "* LIST (\Sent)
"/" Sent", escaped characters, and literal mailbox fields, returning the decoded
mailbox name used for SPECIAL-USE mapping.

184-192: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Reject CR/LF in auth inputs before building POP3/IMAP commands. USER/PASS are interpolated raw, and quoted(_:) only escapes \ and ", so a newline in username or credential can break out of the command and inject another one.

🤖 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
`@apps/unwired-mail/unwired-mail/Services/Mailbox/SystemGenericMailEndpointVerifier.swift`
around lines 184 - 192, Validate username, credential, and any other
authentication inputs for CR or LF before constructing POP3 or IMAP commands in
the authentication flows, including authenticatePOP3 and the quoted(_) path.
Reject invalid inputs with the existing authentication/setup error before
calling write, while preserving normal OAuth and non-newline authentication
behavior.
🤖 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.

Outside diff comments:
In
`@apps/unwired-mail/unwired-mail/Services/Mailbox/SystemGenericMailEndpointVerifier.swift`:
- Around line 86-88: Update readIMAPResponse(tag:) and the isPositiveGreeting
validation to parse the returned tagged completion line rather than searching
the full response buffer. Extract the line matching the requested tag and
require its completion status to be exactly OK, rejecting tagged NO or BAD
responses while preserving the existing authenticationFailed behavior.
- Around line 175-182: Update listedMailbox(in:) to parse IMAP LIST responses
structurally according to RFC 3501 rather than selecting the last quoted pair.
Correctly handle unquoted mailbox names, quoted names such as "* LIST (\Sent)
"/" Sent", escaped characters, and literal mailbox fields, returning the decoded
mailbox name used for SPECIAL-USE mapping.
- Around line 184-192: Validate username, credential, and any other
authentication inputs for CR or LF before constructing POP3 or IMAP commands in
the authentication flows, including authenticatePOP3 and the quoted(_) path.
Reject invalid inputs with the existing authentication/setup error before
calling write, while preserving normal OAuth and non-newline authentication
behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 67437759-6cb9-4ee0-ab4f-e20d7af84fd6

📥 Commits

Reviewing files that changed from the base of the PR and between a71fbc0 and fa1440e.

📒 Files selected for processing (3)
  • apps/unwired-mail/unwired-mail/Features/Account/GenericMailSetupPanel.swift
  • apps/unwired-mail/unwired-mail/Services/Mailbox/SystemGenericMailEndpointVerifier.swift
  • apps/unwired-mail/unwired-mailTests/GenericMailSetupServiceTests.swift

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

Reviewed commit: fa1440ef3b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@rajzik
rajzik marked this pull request as ready for review July 21, 2026 11:24
@cursor

cursor Bot commented Jul 21, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fa1440ef3b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@rajzik

rajzik commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@rajzik

rajzik commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (5)
apps/unwired-mail/unwired-mail/Services/Mailbox/SystemGenericMailEndpointVerifier.swift (5)

191-204: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

POP3 USER/PASS are built with zero escaping — same command-injection class as quoted(), and worse since POP3 has no quoting mechanism at all.

username/credential are interpolated directly (lines 195, 199). A \r\n in either value lets an attacker (e.g. via a maliciously crafted "app password") inject additional POP3 commands using the victim's own authenticated session.

🔒️ Proposed fix
   private func authenticatePOP3() async throws {
+    guard !username.contains(where: { $0 == "\r" || $0 == "\n" }),
+      !credential.contains(where: { $0 == "\r" || $0 == "\n" })
+    else {
+      throw GenericMailSetupError.authenticationFailed(.pop3)
+    }
     if authorizationMethod == .oauth {
🤖 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
`@apps/unwired-mail/unwired-mail/Services/Mailbox/SystemGenericMailEndpointVerifier.swift`
around lines 191 - 204, Update authenticatePOP3 to reject or safely handle any
username or credential containing POP3 command delimiters, especially CR or LF,
before interpolating them into the USER and PASS commands. Preserve normal
authentication behavior for valid values and ensure malicious inputs cannot
inject additional commands.

257-265: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant/dead first clause makes this read like it filters on code, but it doesn't.

$0.hasPrefix("\(code) ") is always a subset of the regex match ^\d{3} , since code is itself a 3-digit string — the loop actually terminates on any terminal 3-digit response line, and the code parameter has no filtering effect. That's the intended (and previously confirmed) behavior, but the dead clause is misleading to future readers.

♻️ Proposed cleanup
   private func readSMTPResponse(code: String) async throws -> String {
     var response = ""
     repeat {
       response += try await readResponse()
     } while !response.components(separatedBy: "\r\n").contains(where: {
-      $0.hasPrefix("\(code) ") || $0.range(of: #"^\d{3} "#, options: .regularExpression) != nil
+      $0.range(of: #"^\d{3} "#, options: .regularExpression) != nil
     })
     return response
   }
🤖 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
`@apps/unwired-mail/unwired-mail/Services/Mailbox/SystemGenericMailEndpointVerifier.swift`
around lines 257 - 265, In readSMTPResponse, remove the redundant code-specific
hasPrefix condition and retain only the regular-expression check for any
three-digit SMTP response line. Keep the existing loop termination behavior
unchanged and leave the code parameter intact unless the surrounding API permits
removing it safely.

291-299: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

No upper bound on unreadResponse growth — a slow/misbehaving mail server can grow this string unboundedly.

The loop only exits once \r\n appears; a server that streams data without ever sending a line terminator (or discoverIMAPRoleMappings's huge LIST output at lines 157-171 combined with readIMAPResponse's tag-wait loop at lines 240-255) can grow this buffer indefinitely, risking memory exhaustion. Since this feature explicitly connects to arbitrary, user-supplied servers, a size cap is a reasonable defensive measure. (The ! at line 295 is safe given the preceding loop invariant, so no separate concern there.)

🛡️ Proposed fix
   private func readResponse() async throws -> String {
+    let maxBufferedBytes = 64 * 1024
     while !unreadResponse.contains("\r\n") {
       unreadResponse += try await task.read()
+      guard unreadResponse.utf8.count <= maxBufferedBytes else {
+        throw GenericMailSetupError.authenticationFailed(endpoint.mailProtocol)
+      }
     }
🤖 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
`@apps/unwired-mail/unwired-mail/Services/Mailbox/SystemGenericMailEndpointVerifier.swift`
around lines 291 - 299, Update readResponse() to enforce a reasonable maximum
size for unreadResponse while waiting for "\r\n"; reject or terminate the read
when the cap would be exceeded, preserving normal response parsing below the
limit. Ensure the limit also protects callers such as readIMAPResponse and
discoverIMAPRoleMappings from unbounded server responses.

267-274: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

quoted() escapes \ and " but not CR/LF — IMAP LOGIN command injection.

RFC 3501 quoted strings cannot contain CR/LF, but nothing here strips or rejects them. If username/credential (e.g. a pasted app-specific password) contains an embedded \r\n, it breaks out of the quoted argument and injects arbitrary IMAP commands into the connection at line 146 (a2 LOGIN \(quoted(username)) \(quoted(credential))\r\n), running with the user's own authenticated session.

🔒️ Proposed fix
   private func quoted(_ value: String) -> String {
-    "\"\(value.replacingOccurrences(of: "\\", with: "\\\\").replacingOccurrences(of: "\"", with: "\\\""))\""
+    let sanitized = value
+      .replacingOccurrences(of: "\r", with: "")
+      .replacingOccurrences(of: "\n", with: "")
+      .replacingOccurrences(of: "\\", with: "\\\\")
+      .replacingOccurrences(of: "\"", with: "\\\"")
+    return "\"\(sanitized)\""
   }
🤖 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
`@apps/unwired-mail/unwired-mail/Services/Mailbox/SystemGenericMailEndpointVerifier.swift`
around lines 267 - 274, Update the quoted(_:) helper to reject or safely handle
embedded CR and LF characters before escaping backslashes and quotes, ensuring
values used by the LOGIN command cannot inject IMAP command delimiters. Preserve
the existing escaping behavior for valid values and ensure the login path using
quoted(username) and quoted(credential) cannot emit raw CR/LF.

85-98: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Use an exhaustive switch for endpoint.security
This keeps any future MailTransportSecurity case from slipping through without explicit TLS handling.

🤖 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
`@apps/unwired-mail/unwired-mail/Services/Mailbox/SystemGenericMailEndpointVerifier.swift`
around lines 85 - 98, Replace the separate security conditionals in the
connection flow with an exhaustive switch over endpoint.security, explicitly
handling implicitTLS and startTLS while preserving their existing timing and
operations, and add the required handling for any other MailTransportSecurity
case.
🤖 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 `@apps/unwired-mail/unwired-mailTests/GenericMailSetupServiceTests.swift`:
- Around line 728-756: Update testSystemVerifierClosesStreamWhenCancelled so
awaiting verification.value is bounded by a short timeout, racing it against the
cancellation expectation and failing with a clear timeout message if it remains
suspended. Preserve the existing assertions for CancellationError, unexpected
errors, and stream.closeCount.
- Around line 910-931: Synchronize access to readContinuation in
BlockingGenericMailStreamTask so read()’s continuation assignment and close()’s
check-and-clear are atomic. Add lock or actor isolation around both operations,
ensuring concurrent close() calls can resume the continuation at most once while
preserving the existing cancellation behavior.

---

Outside diff comments:
In
`@apps/unwired-mail/unwired-mail/Services/Mailbox/SystemGenericMailEndpointVerifier.swift`:
- Around line 191-204: Update authenticatePOP3 to reject or safely handle any
username or credential containing POP3 command delimiters, especially CR or LF,
before interpolating them into the USER and PASS commands. Preserve normal
authentication behavior for valid values and ensure malicious inputs cannot
inject additional commands.
- Around line 257-265: In readSMTPResponse, remove the redundant code-specific
hasPrefix condition and retain only the regular-expression check for any
three-digit SMTP response line. Keep the existing loop termination behavior
unchanged and leave the code parameter intact unless the surrounding API permits
removing it safely.
- Around line 291-299: Update readResponse() to enforce a reasonable maximum
size for unreadResponse while waiting for "\r\n"; reject or terminate the read
when the cap would be exceeded, preserving normal response parsing below the
limit. Ensure the limit also protects callers such as readIMAPResponse and
discoverIMAPRoleMappings from unbounded server responses.
- Around line 267-274: Update the quoted(_:) helper to reject or safely handle
embedded CR and LF characters before escaping backslashes and quotes, ensuring
values used by the LOGIN command cannot inject IMAP command delimiters. Preserve
the existing escaping behavior for valid values and ensure the login path using
quoted(username) and quoted(credential) cannot emit raw CR/LF.
- Around line 85-98: Replace the separate security conditionals in the
connection flow with an exhaustive switch over endpoint.security, explicitly
handling implicitTLS and startTLS while preserving their existing timing and
operations, and add the required handling for any other MailTransportSecurity
case.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e106b513-8e33-4ca8-a168-4cf5087c2326

📥 Commits

Reviewing files that changed from the base of the PR and between fa1440e and ab3a8d8.

📒 Files selected for processing (2)
  • apps/unwired-mail/unwired-mail/Services/Mailbox/SystemGenericMailEndpointVerifier.swift
  • apps/unwired-mail/unwired-mailTests/GenericMailSetupServiceTests.swift

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ab3a8d86da

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@rajzik

rajzik commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@rajzik

rajzik commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 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 `@apps/unwired-mail/unwired-mailTests/GenericMailSetupServiceTests.swift`:
- Around line 936-941: Update read() so it stores readContinuation under the
lock before calling readStarted.fulfill(). Handle a stream that was already
closed by resuming the newly stored continuation immediately, ensuring close()
cannot leave read() suspended.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b0490547-dccb-43f7-af28-0a9ff6005f7a

📥 Commits

Reviewing files that changed from the base of the PR and between ab3a8d8 and d125709.

📒 Files selected for processing (3)
  • apps/unwired-mail/unwired-mail/Features/Account/GenericMailSetupPanel.swift
  • apps/unwired-mail/unwired-mail/Services/Mailbox/SystemGenericMailEndpointVerifier.swift
  • apps/unwired-mail/unwired-mailTests/GenericMailSetupServiceTests.swift

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d125709b16

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/unwired-mail/unwired-mail/Features/Account/GenericMailSetupPanel.swift Outdated
@rajzik

rajzik commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@rajzik

rajzik commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

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.

Caution

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

⚠️ Outside diff range comments (1)
apps/unwired-mail/unwired-mail/Features/Account/GenericMailSetupPanel.swift (1)

106-108: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clear stale setup when loading fails.

A storage error leaves the previously loaded definition and endpoints visible; after changing the email address, this can present stale settings as saved. Clear state before reporting the error.

Proposed fix
     } catch {
+      clearLoadedSetup()
       errorMessage = error.localizedDescription
     }
🤖 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 `@apps/unwired-mail/unwired-mail/Features/Account/GenericMailSetupPanel.swift`
around lines 106 - 108, Update the error path in the setup-loading method
containing the catch block to clear the previously loaded definition and
endpoints before assigning error.localizedDescription to errorMessage. Preserve
the existing error reporting while ensuring failed loads cannot leave stale
settings visible.
🤖 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.

Outside diff comments:
In `@apps/unwired-mail/unwired-mail/Features/Account/GenericMailSetupPanel.swift`:
- Around line 106-108: Update the error path in the setup-loading method
containing the catch block to clear the previously loaded definition and
endpoints before assigning error.localizedDescription to errorMessage. Preserve
the existing error reporting while ensuring failed loads cannot leave stale
settings visible.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f0f83f22-6ef3-453a-81fc-cb16a525b7f2

📥 Commits

Reviewing files that changed from the base of the PR and between d125709 and 9f07703.

📒 Files selected for processing (2)
  • apps/unwired-mail/unwired-mail/Features/Account/GenericMailSetupPanel.swift
  • apps/unwired-mail/unwired-mailTests/GenericMailSetupServiceTests.swift

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: 9f07703066

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

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.

Connect generic mail securely

1 participant