Skip to content

Refactor: Declarative ESC key handling system with security fixes#23

Merged
datlechin merged 10 commits intomainfrom
refactor/esc-key-handler-system
Jan 17, 2026
Merged

Refactor: Declarative ESC key handling system with security fixes#23
datlechin merged 10 commits intomainfrom
refactor/esc-key-handler-system

Conversation

@datlechin
Copy link
Copy Markdown
Member

Summary

This PR refactors the ESC key handling system from manual NSEvent monitoring to a declarative, environment-based approach using SwiftUI. It also adds database metadata features and fixes critical SQL injection vulnerabilities.

Key Changes

1. Declarative ESC Key Handling System (5 new files)

  • Priority-based coordination: .popup > .nestedSheet > .sheet > .view > .global
  • Declarative API: .escapeKeyHandler(priority:) { } and .escapeKeyDismiss(priority:)
  • Automatic coordination: Nested sheets close inner-first without manual state tracking
  • No global state: Pure SwiftUI environment-based approach

2. Complete Migration (12 files updated)

  • ✅ All sheets migrated to new .escapeKeyHandler() API
  • ✅ Removed all old .keyboardShortcut(.escape) patterns
  • ✅ Removed manual NSEvent monitors and global state tracking
  • ✅ Cleaned up AppState.isSheetPresented and FocusedValue tracking

3. Database Metadata & Creation Features

  • Added DatabaseMetadata model with table count, size, last accessed
  • Implemented metadata fetching for MySQL, PostgreSQL, SQLite
  • New DatabaseSwitcherSheetV2 with rich metadata display
  • New CreateDatabaseSheet for creating databases with charset/collation

4. Critical Security Fixes ⚠️

  • Fixed SQL injection vulnerabilities in database drivers:
    • MySQL: fetchDatabaseMetadata() and createDatabase()
    • PostgreSQL: fetchDatabaseMetadata() and createDatabase()
  • Added proper escaping for database names and identifiers
  • Added input validation for charset and collation parameters

Testing

Build status: ✅ BUILD SUCCEEDED

Manual Testing Checklist

  • Database Switcher (Cmd+K) → ESC → closes
  • Database Switcher → Create Database (+) → ESC → only create dialog closes
  • CreateTableView → Detail panel → ESC → panel collapses
  • All sheets (Export, Import, etc.) → ESC → close properly
  • Autocomplete popup → ESC → closes
  • Database names with special characters (test'db) don't cause SQL errors

Breaking Changes

None - this is an internal refactor with backward-compatible behavior.

Files Changed

New files:

  • Core/KeyboardHandling/ (5 files)
  • Models/DatabaseMetadata.swift
  • ViewModels/DatabaseSwitcherViewModel.swift
  • Views/DatabaseSwitcher/DatabaseSwitcherSheetV2.swift
  • Views/DatabaseSwitcher/CreateDatabaseSheet.swift

Modified files:

  • Database drivers (MySQL, PostgreSQL, SQLite)
  • All sheets and dialogs (12 files)
  • ContentView, TableProApp, MainContentAlerts

Deleted:

  • Views/DatabaseSwitcher/DatabaseSwitcherSheet.swift (replaced by V2)

- Add new declarative ESC key handling system with priority-based coordination
  - Core/KeyboardHandling/EscapeKeyHandler.swift - Handler types and priorities
  - Core/KeyboardHandling/EscapeKeyEnvironment.swift - SwiftUI environment integration
  - Core/KeyboardHandling/EscapeKeyCoordinator.swift - Global NSEvent monitor
  - Core/KeyboardHandling/View+EscapeKey.swift - Declarative SwiftUI API
  - Core/KeyboardHandling/EscapeKeyEnvironmentBridge.swift - Environment bridge

- Migrate all sheets and views to new .escapeKeyHandler() API
  - Remove all .keyboardShortcut(.escape) and .keyboardShortcut(.cancelAction)
  - Remove manual NSEvent monitors from ContentView and DatabaseSwitcherSheetV2
  - Remove .onExitCommand handlers in favor of declarative handlers

- Clean up global state tracking
  - Remove AppState.isSheetPresented tracking
  - Remove isDatabaseSwitcherOpen FocusedValue
  - Remove unnecessary conditional checks

- Add database metadata and creation features
  - Add DatabaseMetadata model with table count, size, last accessed
  - Add fetchDatabaseMetadata() to DatabaseDriver protocol
  - Add createDatabase() to DatabaseDriver protocol
  - Implement metadata fetching for MySQL, PostgreSQL, SQLite drivers
  - Add DatabaseSwitcherSheetV2 with rich metadata display
  - Add CreateDatabaseSheet for creating new databases

- Fix critical security vulnerabilities
  - Fix SQL injection in MySQLDriver.fetchDatabaseMetadata()
  - Fix SQL injection in MySQLDriver.createDatabase()
  - Fix SQL injection in PostgreSQLDriver.fetchDatabaseMetadata()
  - Fix SQL injection in PostgreSQLDriver.createDatabase()
  - Add input validation and escaping for database names and parameters

- Update menu commands to work with new ESC key system
  - Remove manual state checks in TableProApp commands
  - Install global ESC key handling via .escapeKeySystem()

Build status: Verified successful compilation
Copilot AI review requested due to automatic review settings January 17, 2026 20:14
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors ESC key handling from manual NSEvent monitoring to a declarative, SwiftUI environment-based system with priority coordination. It also introduces database metadata features and fixes SQL injection vulnerabilities in database creation operations.

Changes:

  • Implemented declarative ESC key handling system with priority-based coordination (.popup > .nestedSheet > .sheet > .view > .global)
  • Migrated all sheets and dialogs to use new .escapeKeyHandler() and .escapeKeyDismiss() APIs
  • Added database metadata features including table count, size tracking, and recent database tracking
  • Fixed SQL injection vulnerabilities in MySQL and PostgreSQL database creation and metadata fetching

Reviewed changes

Copilot reviewed 31 out of 32 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
Core/KeyboardHandling/* New declarative ESC key handling system with environment-based coordination
Views/DatabaseSwitcher/DatabaseSwitcherSheetV2.swift Redesigned database switcher with metadata display and create database functionality
Views/DatabaseSwitcher/CreateDatabaseSheet.swift New sheet for creating databases with charset/collation options
ViewModels/DatabaseSwitcherViewModel.swift ViewModel handling database fetching, metadata, and recent tracking
Models/DatabaseMetadata.swift New metadata model with table count, size, and system database detection
Core/Database/*Driver.swift Added metadata fetching and database creation with SQL injection fixes
TableProApp.swift Removed manual ESC key monitoring and installed new declarative system
ContentView.swift Removed manual NSEvent monitoring for ESC key
Multiple dialog/sheet files Migrated to new .escapeKeyHandler() API

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread TablePro/Core/Database/PostgreSQLDriver.swift
Comment thread TablePro/Core/Database/PostgreSQLDriver.swift Outdated
Comment thread TablePro/Core/Database/PostgreSQLDriver.swift Outdated
Comment thread TablePro/Core/Database/MySQLDriver.swift Outdated
Comment thread TablePro/Core/Database/MySQLDriver.swift Outdated
Comment thread TablePro/Views/DatabaseSwitcher/CreateDatabaseSheet.swift Outdated
datlechin and others added 6 commits January 18, 2026 03:16
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Ngô Quốc Đạt <datlechin@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Ngô Quốc Đạt <datlechin@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Ngô Quốc Đạt <datlechin@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Ngô Quốc Đạt <datlechin@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Ngô Quốc Đạt <datlechin@gmail.com>
Replace custom tap gesture handling with native SwiftUI List selection:
- Use List(selection:) with @published selectedDatabase binding
- Apply DoubleClickView overlay pattern (from WelcomeWindowView)
- DoubleClickView forwards single-clicks via super.mouseDown() for instant selection
- Double-clicks trigger database opening

This eliminates the 300-500ms delay from tap gesture disambiguation,
making the switcher feel native and responsive like macOS Finder.

Files:
- Rename DatabaseSwitcherSheetV2 → DatabaseSwitcherSheet
- Delete old DatabaseSwitcherSheet_OLD.swift
- Update MainContentAlerts to reference new name
- Add selectedDatabase @published property to ViewModel
@datlechin datlechin requested a review from Copilot January 17, 2026 20:53
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated 7 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +118 to +123
.onExitCommand {
// Prevent dismissing the sheet via ESC while a database is being created
if !isCreating {
dismiss()
}
}
Copy link

Copilot AI Jan 17, 2026

Choose a reason for hiding this comment

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

The .onExitCommand modifier is being used here, but according to the PR description, this refactor aims to replace all manual keyboard handling with the new .escapeKeyHandler() API. This sheet should use .escapeKeyHandler(priority: .nestedSheet) instead of .onExitCommand for consistency with the rest of the codebase.

Copilot uses AI. Check for mistakes.
Comment thread TablePro/Views/DatabaseSwitcher/DatabaseSwitcherSheet.swift
throw DatabaseError.queryFailed("Invalid character set: \(charset)")
}

var query = "CREATE DATABASE `\(escapedName)` CHARACTER SET \(charset)"
Copy link

Copilot AI Jan 17, 2026

Choose a reason for hiding this comment

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

The charset parameter is validated against a whitelist but is not escaped when interpolated into the SQL query. While the validation provides some protection, it would be safer to escape the charset value or use parameterized queries to fully prevent potential SQL injection if the validation logic is ever modified or bypassed.

Copilot uses AI. Check for mistakes.
guard collation.hasPrefix(charset), isSafe else {
throw DatabaseError.queryFailed("Invalid collation for charset")
}
query += " COLLATE \(collation)"
Copy link

Copilot AI Jan 17, 2026

Choose a reason for hiding this comment

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

The collation is validated but not escaped before being interpolated into the SQL query. While character set validation is in place, adding proper escaping would provide defense in depth against potential SQL injection.

Copilot uses AI. Check for mistakes.
throw DatabaseError.queryFailed("Invalid encoding: \(charset)")
}

var query = "CREATE DATABASE \"\(escapedName)\" ENCODING '\(normalizedCharset)'"
Copy link

Copilot AI Jan 17, 2026

Choose a reason for hiding this comment

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

The encoding/charset is validated but not escaped when interpolated into the query. Even with validation, proper escaping would provide an additional layer of protection against SQL injection.

Copilot uses AI. Check for mistakes.
Comment thread TablePro/Core/Database/PostgreSQLDriver.swift Outdated
Comment thread TablePro/Views/DatabaseSwitcher/DatabaseSwitcherSheet.swift
datlechin and others added 2 commits January 18, 2026 03:56
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Ngô Quốc Đạt <datlechin@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Ngô Quốc Đạt <datlechin@gmail.com>
@datlechin datlechin merged commit ec85090 into main Jan 17, 2026
@datlechin datlechin deleted the refactor/esc-key-handler-system branch January 17, 2026 20:57
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.

2 participants