Skip to content

feat(connections): import connections from Navicat#1522

Open
datlechin wants to merge 2 commits into
mainfrom
navicat-import
Open

feat(connections): import connections from Navicat#1522
datlechin wants to merge 2 commits into
mainfrom
navicat-import

Conversation

@datlechin
Copy link
Copy Markdown
Member

Closes #1485

What

Adds a Navicat importer to the existing "Import from Other App" flow. Users export their connections from Navicat (File, Export Connections) and pick the .ncx file in TablePro; connections, SSH tunnel, and SSL settings come across, and saved passwords are decrypted during import.

Approach

Navicat's on-disk store has undocumented keys and machine-bound passwords that can't be read off-machine, so the .ncx export is the reliable, sandbox-clean source. The existing five importers auto-detect an installed app and read its on-disk store, which doesn't fit a user-picked file. Rather than special-casing Navicat in the UI, the importer protocol gains a first-class file-sourced concept:

  • ForeignAppImporter gains var importFileTypes: [UTType]? (defaults to nil, so the five existing importers are untouched) and mutating func setSelectedFile(_:). The source picker presents a .fileImporter for any importer that declares file types and injects the chosen URL before importing. No if id == "navicat" branching.
  • NavicatCipher decrypts Navicat passwords with CommonCrypto: AES-128-CBC (Navicat 12+, what modern .ncx uses) and Blowfish-ECB with Navicat's custom CBC-style chaining (Navicat 11). It trial-decrypts and keeps the first result that is valid UTF-8. No third-party dependency. Constants are from the public reverse-engineering reference (HyperSine/how-does-navicat-encrypt-password).
  • NavicatImporter parses the .ncx with XMLDocument, maps ConnType to TablePro's database types, handles SQLite (file path) and Oracle, maps SSH and SSL, and gates passwords on the SavePassword flag plus the Include Passwords toggle.

DBeaver imports Navicat .ncx but does not carry passwords over; DataGrip and TablePlus have no Navicat importer. Decrypting the passwords is a difference from all three.

Notes

  • The .ncx parse uses XMLDocument(options: [.nodeLoadExternalEntitiesNever]) to avoid external-entity (XXE) resolution on an untrusted file.
  • The registry test corrected a pre-existing stale assertion (count == 4 that omitted datagrip); it now asserts the true count and lists all importers including Navicat.

Test plan

  • NavicatCipherTests: the two published golden vectors decrypt to the expected plaintext, plus empty / odd-length / non-hex return nil. Verified to pass via a standalone run.
  • NavicatImporterTests: fixture .ncx files cover ConnType mapping, SQLite by file path, SSH and SSL, password decryption and the SavePassword / Include Passwords gating, default ports, unknown ConnType pass-through, and malformed / empty input.
  • swiftlint --strict: clean.
  • Build runs in Xcode on the reviewer's machine.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

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: 4c377bbc4c

ℹ️ 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".

return ExportableCredentials(
password: password,
sshPassword: sshPassword,
keyPassphrase: nil,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve Navicat SSH key passphrases

When a Navicat connection uses private-key SSH auth (SSH_AuthenMethod is not PASSWORD), the export stores the encrypted key passphrase separately in SSH_Passphrase gated by SSH_SavePassphrase, but buildCredentials only decrypts the database password and SSH_Password and always writes keyPassphrase: nil. Imported tunnels with encrypted private keys therefore keep the key path but lose the saved passphrase, so they cannot authenticate until the user manually re-enters it.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

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: a97030297a

ℹ️ 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".

password: password,
sshPassword: sshPassword,
keyPassphrase: nil,
sslClientKeyPassphrase: nil,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restore SSL client-key passphrases

When a Navicat connection uses an encrypted SSL client key, the .ncx carries the key path in SSL_ClientKey and the saved PEM passphrase in SSL_PEMClientKeyPassword, but this importer always writes sslClientKeyPassphrase: nil. In that scenario the imported connection keeps the client key path without the secret needed to unlock it, so SSL authentication fails until the user re-enters the passphrase.

Useful? React with 👍 / 👎.

Comment on lines +200 to +201
case "MONGODB": return "MongoDB"
default: return raw
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Map Redis Navicat connection types

When importing Navicat Redis connections (ConnType="REDIS"), this switch falls through and preserves the raw uppercase value, while TablePro registers the driver as Redis (DatabaseType.redis). The preview will warn that REDIS is not installed and the imported connection will not resolve to the Redis plugin; add the Redis mapping alongside MongoDB and the other supported Navicat types.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Import connections from other apps: Navicat

1 participant