diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..a26ae1e --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,624 @@ +# PyGtk-Posting Architecture Documentation + +## System Architecture Overview + +PyGtk-Posting follows a traditional desktop application architecture with a three-tier design: + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ PRESENTATION LAYER │ +│ (GTK3 UI) │ +│ ┌──────────────┬──────────────┬──────────────┬──────────────┐ │ +│ │ Main Window │ Invoice UI │ Reports UI │ Admin UI │ │ +│ │ (Glade XML) │ (Glade XML) │ (Glade XML) │ (Glade XML) │ │ +│ └──────┬───────┴──────┬───────┴──────┬───────┴──────┬───────┘ │ +└─────────┼──────────────┼──────────────┼──────────────┼─────────┘ + │ │ │ │ + │ └──────┬───────┘ │ + │ │ │ +┌─────────▼─────────────────────▼──────────────────────▼─────────┐ +│ BUSINESS LOGIC LAYER │ +│ (Python Modules) │ +│ ┌───────────┬───────────┬────────────┬──────────┬──────────┐ │ +│ │ Invoice │ Reports │ Inventory │ Payroll │ Admin │ │ +│ │ Module │ Module │ Module │ Module │ Module │ │ +│ └─────┬─────┴─────┬─────┴──────┬─────┴─────┬────┴────┬─────┘ │ +│ │ │ │ │ │ │ +│ └─────────┬─┴────────────┴───────────┴─────────┘ │ +│ │ │ +│ ┌─────────▼─────────┐ │ +│ │ Transactor │ (Accounting Transaction Logic) │ +│ │ (db/transactor) │ │ +│ └─────────┬─────────┘ │ +└──────────────────┼──────────────────────────────────────────────┘ + │ +┌──────────────────▼──────────────────────────────────────────────┐ +│ DATA ACCESS LAYER │ +│ (Database Drivers) │ +│ ┌──────────────────────────┬──────────────────────────┐ │ +│ │ psycopg2 │ APSW │ │ +│ │ (PostgreSQL Driver) │ (SQLite Driver) │ │ +│ └───────────┬──────────────┴────────────┬─────────────┘ │ +└──────────────┼───────────────────────────┼─────────────────────┘ + │ │ +┌──────────────▼──────────────┐ ┌─────────▼────────────────────┐ +│ PostgreSQL Database │ │ SQLite Database │ +│ (Business Data) │ │ (User Preferences) │ +│ ┌───────────────────────┐ │ │ ┌────────────────────────┐ │ +│ │ - gl_accounts │ │ │ │ - postgres_conn │ │ +│ │ - gl_transactions │ │ │ │ - settings │ │ +│ │ - gl_entries │ │ │ │ - keybindings │ │ +│ │ - contacts │ │ │ │ - window_positions │ │ +│ │ - products │ │ │ │ - db_connections │ │ +│ │ - invoices │ │ │ └────────────────────────┘ │ +│ │ - purchase_orders │ │ └────────────────────────────┘ │ +│ │ - payments_incoming │ │ │ +│ │ - payments_outgoing │ │ │ +│ │ - payroll.* │ │ │ +│ └───────────────────────┘ │ │ +└─────────────────────────────┘ │ +``` + +## Component Interactions + +### 1. Application Startup Sequence + +``` +┌──────────┐ +│ User │ +└────┬─────┘ + │ Launches application + ▼ +┌────────────────┐ +│ pygtk-posting │ (Shell wrapper script) +└───────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ main.py │ +└───────┬─────────┘ + │ + ├──▶ Load SQLite preferences (sqlite_utils.py) + │ └─▶ Create tables if needed + │ ├─ postgres_conn + │ ├─ settings + │ ├─ keybindings + │ └─ window_positions + │ + ├──▶ Connect to PostgreSQL (DB connection) + │ └─▶ Authenticate with stored credentials + │ + ├──▶ Initialize constants.py + │ ├─ Set global DB reference + │ ├─ Load ACCOUNTS module + │ └─ Start Broadcast system + │ └─▶ LISTEN on PostgreSQL channels: + │ ├─ products + │ ├─ contacts + │ ├─ accounts + │ ├─ time_clock_entries + │ ├─ invoices + │ └─ purchase_orders + │ + └──▶ Launch main_window.MainGUI() + ├─▶ Load main_window.ui (Glade XML) + ├─▶ Setup menu structure + ├─▶ Load keybindings from SQLite + ├─▶ Connect GTK signal handlers + └─▶ Start GTK main loop +``` + +### 2. Invoice Creation Workflow + +``` +┌──────────┐ +│ User │ Clicks "New Invoice" +└────┬─────┘ + │ + ▼ +┌──────────────────────┐ +│ main_window.py │ Opens invoice_window +└───────┬──────────────┘ + │ + ▼ +┌──────────────────────┐ +│ invoice_window.py │ +│ ┌──────────────────┐ │ +│ │ Load .ui file │ │ +│ │ Connect signals │ │ +│ └──────────────────┘ │ +└───────┬──────────────┘ + │ + │ 1. User selects customer + ├──▶ Query contacts table + ├──▶ Load tax exemptions + ├──▶ Load payment terms + └──▶ Load customer markup + │ + │ 2. User adds products + ├──▶ Query products table + ├──▶ Calculate prices (with markup) + ├──▶ Calculate taxes + └──▶ Update totals + │ + │ 3. User clicks "Post Invoice" + ▼ +┌──────────────────────────────────────────┐ +│ invoice_create.py │ +│ ┌──────────────────────────────────────┐ │ +│ │ Validation │ │ +│ │ - Check customer selected │ │ +│ │ - Check products added │ │ +│ │ - Verify calculations │ │ +│ └──────────────────────────────────────┘ │ +│ │ +│ BEGIN TRANSACTION │ +│ ┌──────────────────────────────────────┐ │ +│ │ 1. INSERT INTO invoices │ │ +│ │ - customer_id │ │ +│ │ - date_created │ │ +│ │ - total_amount │ │ +│ │ RETURNING invoice_id │ │ +│ │ │ │ +│ │ 2. INSERT INTO invoice_items │ │ +│ │ - invoice_id (FK) │ │ +│ │ - product_id (FK) │ │ +│ │ - quantity, price, tax │ │ +│ │ (repeat for each line item) │ │ +│ │ │ │ +│ │ 3. Call transactor for GL entries │ │ +│ └──────────────────────────────────────┘ │ +└───────┬──────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────┐ +│ db/transactor.py │ +│ ┌──────────────────────────────────────┐ │ +│ │ CustomerInvoice class │ │ +│ │ │ │ +│ │ 1. INSERT gl_transactions │ │ +│ │ - date_inserted │ │ +│ │ RETURNING transaction_id │ │ +│ │ │ │ +│ │ 2. INSERT gl_entries (Debit) │ │ +│ │ - debit_account: Accounts Receivable│ +│ │ - amount: total │ │ +│ │ - gl_transaction_id │ │ +│ │ │ │ +│ │ 3. INSERT gl_entries (Credit) │ │ +│ │ - credit_account: Revenue │ │ +│ │ - amount: total │ │ +│ │ - gl_transaction_id │ │ +│ └──────────────────────────────────────┘ │ +│ COMMIT TRANSACTION │ +└───────┬──────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────┐ +│ PostgreSQL │ +│ NOTIFY invoices, 'invoice_id' │ +└───────┬──────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────┐ +│ constants.Broadcast (All clients) │ +│ ┌──────────────────────────────────────┐ │ +│ │ poll_connection() │ │ +│ │ ├─ Receive NOTIFY from DB │ │ +│ │ └─ emit('invoices_changed', id) │ │ +│ └──────────────────────────────────────┘ │ +└───────┬──────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────┐ +│ Connected UI Modules │ +│ - invoice_history.py (refresh list) │ +│ - unpaid_invoices.py (update totals) │ +│ - contact_history.py (update customer) │ +└──────────────────────────────────────────┘ +``` + +### 3. Real-Time Broadcast System + +``` +┌─────────────────────────────────────────────────────────────┐ +│ PostgreSQL LISTEN/NOTIFY │ +│ │ +│ ┌──────────────┐ NOTIFY ┌──────────────────────┐ │ +│ │ Client A │────product──▶│ PostgreSQL │ │ +│ │ (Modifies │ │ Notification │ │ +│ │ product) │ │ Queue │ │ +│ └──────────────┘ └───┬──────────────────┘ │ +│ │ │ +│ ┌──────────────────┼──────────────────┐ │ +│ │ │ │ │ +│ NOTIFY │ NOTIFY │ NOTIFY │ │ +│ product│ product│ product│ │ +│ ▼ ▼ ▼ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ +│ │ Client B │ │ Client C │ │ Client D │ +│ │ (Receives) │ │ (Receives) │ │ (Receives) │ +│ └──────────────┘ └──────────────┘ └──────────────┘ +└─────────────────────────────────────────────────────────────┘ + +Implementation Flow: + +1. Client A modifies a product: + ├─▶ UPDATE products SET ... WHERE id = 123 + └─▶ NOTIFY products, '123' + +2. PostgreSQL broadcasts to all listeners + +3. Each client's Broadcast.poll_connection() (runs every 1 second): + ├─▶ DB.poll() # Check for notifications + ├─▶ while DB.notifies: + │ ├─ notify = DB.notifies.pop(0) + │ ├─ if notify.channel == "products": + │ └─▶ emit('products_changed') + └─▶ GTK signals propagate to connected handlers + +4. UI modules respond: + └─▶ on_products_changed(self): + └─▶ Refresh product list/data +``` + +### 4. Database Transaction Pattern + +All accounting transactions follow this pattern: + +```python +# Example: Customer Payment +class CustomerInvoicePayment: + def __init__(self, date, total): + # 1. Create transaction header + c = DB.cursor() + c.execute("INSERT INTO gl_transactions (date_inserted) VALUES (%s) RETURNING id", (date,)) + self.transaction_id = c.fetchone()[0] + c.close() + + def bank_check(self, payment_id): + # 2. Record double-entry + # Debit: Check Clearing Account + # Credit: Accounts Receivable + c = DB.cursor() + c.execute(""" + INSERT INTO gl_entries + (debit_account, credit_account, amount, gl_transaction_id) + VALUES ( + (SELECT account FROM gl_account_flow WHERE function = 'check_payment'), + (SELECT account FROM gl_account_flow WHERE function = 'post_invoice'), + %s, %s + ) RETURNING id + """, (self.total, self.transaction_id)) + entry_id = c.fetchone()[0] + c.close() + return entry_id +``` + +**Key Principle:** Every financial transaction creates: +1. One `gl_transactions` record (header) +2. Multiple `gl_entries` records (debits and credits that balance) +3. Related business records (invoice, payment, etc.) + +## Module Organization + +### Core Modules + +``` +src/ +├── main.py # Application entry point +├── main_window.py # Primary GUI container +├── constants.py # Global state (DB, broadcaster, flags) +├── sqlite_utils.py # Local preferences (SQLite) +├── dateutils.py # Date utilities +├── printing.py # Print formatting +└── traceback_handler.py # Error logging +``` + +### Business Domain Modules + +#### Invoice Management +``` +invoice/ +├── invoice_create.py # Invoice posting logic +├── invoice_template.py # Template handling +└── ... (8 more modules) +``` + +#### Reports +``` +reports/ +├── aging_payables.py # AP aging report +├── aging_receivables.py # AR aging report +├── balance_sheet.py # Balance sheet +├── profit_and_loss.py # P&L statement +├── contact_history.py # Customer/vendor history +└── ... (15+ more reports) +``` + +#### Administration +``` +admin/ +├── contact_import.py # Bulk contact import +├── product_import.py # Bulk product import +├── data_export.py # Data export +├── duplicate_contact.py # Deduplication +└── ... (4 more modules) +``` + +#### Database +``` +db/ +├── transactor.py # Transaction factory classes +├── database_tools.py # Admin tools +├── database_backup.py # Backup utility +├── database_restore.py # Restore utility +├── version.py # Schema versioning +└── sql_window.py # SQL query tool +``` + +## Data Flow Patterns + +### 1. Read Pattern (Display Data) +``` +User Action + ↓ +UI Module (e.g., invoice_window.py) + ↓ +SQL Query (SELECT) + ↓ +PostgreSQL + ↓ +psycopg2 cursor.fetchall() + ↓ +Populate GTK TreeView/ListStore + ↓ +Display to User +``` + +### 2. Write Pattern (Modify Data) +``` +User Action + ↓ +UI Module validation + ↓ +Business Logic Module + ↓ +BEGIN TRANSACTION + ├─ INSERT/UPDATE business tables + ├─ Call transactor for GL entries + └─ COMMIT + ↓ +NOTIFY appropriate channel + ↓ +Broadcast to all clients + ↓ +Connected UI modules refresh +``` + +### 3. Report Generation Pattern +``` +User selects report + ↓ +Report Module (e.g., profit_and_loss.py) + ↓ +Query aggregated data (GROUP BY, SUM, etc.) + ↓ +Py3o Template Engine + ↓ +Load .odt template (templates/profit_loss.odt) + ↓ +Fill template with data + ↓ +LibreOffice UNO Bridge + ↓ +Convert to PDF/XLS + ↓ +Display/Print result +``` + +## Security Architecture + +### Authentication & Authorization + +``` +┌──────────────────────────────────────────────────────────────┐ +│ Application Security │ +├──────────────────────────────────────────────────────────────┤ +│ │ +│ 1. Database Authentication (PostgreSQL) │ +│ ├─ User credentials stored in SQLite │ +│ ├─ Connection established at startup │ +│ └─ All queries use authenticated connection │ +│ │ +│ 2. Admin Mode Toggle (constants.is_admin) │ +│ ├─ Separate login for admin functions │ +│ ├─ Controlled via admin_utils.py │ +│ └─ Restricts access to: │ +│ ├─ Data import/export │ +│ ├─ Database tools │ +│ ├─ GL entry editing │ +│ └─ User management │ +│ │ +│ 3. Database-Level Security │ +│ ├─ PostgreSQL GRANT/REVOKE │ +│ ├─ Row-level security (potential) │ +│ └─ Audit logging (log schema) │ +│ │ +│ 4. File System Security │ +│ ├─ SQLite file permissions (user read/write) │ +│ ├─ Log files (restricted access) │ +│ └─ Backup files (encrypted recommended) │ +└──────────────────────────────────────────────────────────────┘ +``` + +### Data Protection + +- **Passwords:** Stored in SQLite (plain text - consider encryption) +- **Sensitive Data:** Protected by PostgreSQL access controls +- **Backups:** Database backup/restore utilities included +- **Audit Trail:** Log schema for tracking changes + +## Performance Considerations + +### Database Connection +- **Single Connection:** One PostgreSQL connection per application instance +- **No Connection Pool:** Not needed for desktop application +- **Auto-reconnect:** On connection loss (handled by psycopg2) + +### Caching +- **Accounts List:** Cached in `constants.ACCOUNTS` module +- **UI State:** Cached in SQLite (window positions, filter states) +- **No Data Caching:** All data queried fresh from database + +### Real-Time Updates +- **Poll Interval:** 1 second (Broadcast.poll_connection) +- **Selective Updates:** Only affected UI modules refresh +- **Efficient Queries:** Use of indexes on foreign keys + +## Deployment Architecture + +### Single-User Desktop + +``` +┌─────────────────────────────────────┐ +│ User's Computer │ +│ ┌───────────────────────────────┐ │ +│ │ PyGtk-Posting Application │ │ +│ │ (Python/GTK) │ │ +│ └──────────┬───────────┬────────┘ │ +│ │ │ │ +│ ┌──────▼────┐ ┌────▼──────┐ │ +│ │PostgreSQL │ │ SQLite │ │ +│ │ (System) │ │ (~/.local)│ │ +│ └───────────┘ └───────────┘ │ +└─────────────────────────────────────┘ +``` + +### Multi-User Network + +``` +┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ +│ Workstation 1 │ │ Workstation 2 │ │ Workstation 3 │ +│ ┌────────────┐ │ │ ┌────────────┐ │ │ ┌────────────┐ │ +│ │ PyGtk App │ │ │ │ PyGtk App │ │ │ │ PyGtk App │ │ +│ └─────┬──────┘ │ │ └─────┬──────┘ │ │ └─────┬──────┘ │ +│ ┌─────▼──────┐ │ │ ┌─────▼──────┐ │ │ ┌─────▼──────┐ │ +│ │ SQLite │ │ │ │ SQLite │ │ │ │ SQLite │ │ +│ │(Local Prefs)│ │ │ │(Local Prefs)│ │ │ │(Local Prefs)│ │ +│ └────────────┘ │ │ └────────────┘ │ │ └────────────┘ │ +└────────┬─────────┘ └────────┬─────────┘ └────────┬─────────┘ + │ │ │ + │ Network (TCP/IP - Port 5432) │ + │ │ │ + └──────────────────────┼──────────────────────┘ + │ + ┌───────────▼───────────┐ + │ Database Server │ + │ ┌─────────────────┐ │ + │ │ PostgreSQL │ │ + │ │ (Shared Data) │ │ + │ └─────────────────┘ │ + └───────────────────────┘ +``` + +**Benefits:** +- Real-time synchronization via LISTEN/NOTIFY +- Concurrent access with ACID guarantees +- Centralized data storage and backups +- Multi-user collaboration + +## Technology Stack Details + +### Python Dependencies + +**Core Libraries:** +- `psycopg2` - PostgreSQL adapter (v2.9+) +- `apsw` - SQLite wrapper (v3.x) +- `gi` - PyGObject/GTK3 bindings + +**UI/Graphics:** +- `cairocffi` - Cairo graphics +- `matplotlib` - Charts and graphs + +**Document Processing:** +- `genshi` - Template engine +- `lxml` - XML/HTML processing +- `py3o` - LibreOffice document generation + +**Data Import/Export:** +- `xlrd` - Excel reading +- `xlsxwriter` - Excel writing + +**System Integration:** +- `python3-uno` - LibreOffice UNO bridge +- `python3-sane` - Scanner support + +### System Dependencies + +**Required:** +- Python 3.6+ +- PostgreSQL 10+ +- GTK+ 3.x +- LibreOffice (for document generation) + +**Optional:** +- Scanner (for document scanning) +- Barcode font (code128.ttf included) + +## Scalability Considerations + +### Current Limitations +- **Desktop Only:** Not web-accessible +- **Linux Only:** GTK3 dependency +- **Single Connection:** One connection per client +- **No Caching:** All queries hit database + +### Potential Improvements +- **Web Interface:** Add REST API + web frontend +- **Connection Pooling:** For high-concurrency scenarios +- **Query Caching:** Redis for frequently accessed data +- **Background Jobs:** For long-running reports +- **Horizontal Scaling:** PostgreSQL replication + +## Extension Points + +### 1. Custom Reports +Add new reports by: +- Creating new module in `reports/` +- Adding menu item in `main_window.py` +- Creating .odt template in `templates/` + +### 2. New Transaction Types +Extend accounting by: +- Adding new class in `db/transactor.py` +- Defining GL account routing in `gl_account_flow` +- Creating UI module for data entry + +### 3. Additional Modules +Add features by: +- Creating new .py + .ui files +- Registering in `main_window.populate_modules()` +- Following existing module patterns + +### 4. Custom Fields +Extend data model: +- Add columns to PostgreSQL tables +- Update UI definitions (.ui files) +- Add to queries in module code + +## Conclusion + +PyGtk-Posting uses a straightforward, maintainable architecture suitable for desktop accounting applications. The three-tier design provides clear separation between UI, business logic, and data access. The real-time broadcast system enables multi-user collaboration, while the transaction factory pattern ensures accounting integrity. + +**Strengths:** +- Clear architectural layers +- Strong accounting domain modeling +- Real-time multi-user support +- Extensible design + +**Future Enhancements:** +- Web API layer +- Improved caching strategy +- More comprehensive error handling +- Automated testing framework diff --git a/CODEBASE_ANALYSIS.md b/CODEBASE_ANALYSIS.md new file mode 100644 index 0000000..e589e52 --- /dev/null +++ b/CODEBASE_ANALYSIS.md @@ -0,0 +1,719 @@ +# PyGtk-Posting Codebase Analysis + +**Analysis Date:** January 22, 2026 +**Version:** 0.5.34 +**Total Python Files:** 181 +**Total Lines of Code:** ~43,896 lines +**UI Definition Files:** 141 (.ui Glade files) + +--- + +## Executive Summary + +PyGtk-Posting is a **comprehensive, feature-rich desktop accounting and business management system** for Linux, written in Python3 with GTK3 and PostgreSQL. It aims to be an open-source replacement for proprietary solutions like QuickBooks and Peachtree. The application is actively maintained and used in production by multiple businesses, though currently in alpha stage pending broader user adoption. + +**Key Strengths:** +- Comprehensive feature set covering all aspects of business accounting +- Active development and real-world usage +- Strong domain modeling with proper double-entry accounting +- Real-time multi-user support via PostgreSQL LISTEN/NOTIFY +- Extensible reporting system using LibreOffice templates + +**Key Areas for Improvement:** +- Limited automated testing infrastructure +- Potential SQL injection vulnerabilities in some modules +- Large monolithic files (1300+ lines in some modules) +- No comprehensive documentation for developers +- Limited error handling in some areas + +--- + +## 1. Project Overview + +### Purpose +An open-source accounting and business management system for Linux that handles: +- Invoicing and billing +- Purchase order management +- Inventory tracking and manufacturing +- Payroll processing +- Financial reporting and budgeting +- Customer relationship management +- Time tracking and resource scheduling + +### Technology Stack + +| Component | Technology | Version | +|-----------|-----------|---------| +| **Language** | Python | 3.x | +| **GUI Framework** | GTK+ | 3.0 | +| **Primary Database** | PostgreSQL | 10+ | +| **Local Storage** | SQLite | via APSW | +| **Document Generation** | LibreOffice/py3o | - | +| **Charting** | matplotlib | - | +| **PDF Processing** | cairocffi | - | +| **Excel I/O** | xlrd, xlsxwriter | - | +| **Build System** | GNU Autotools | - | + +--- + +## 2. Architecture Overview + +### High-Level Architecture + +``` +┌─────────────────────────────────────────────────────────┐ +│ GTK3 Desktop UI │ +│ (main_window.py + 140+ .ui) │ +└─────────────────────┬───────────────────────────────────┘ + │ +┌─────────────────────▼───────────────────────────────────┐ +│ Business Logic Layer (Python) │ +│ ┌──────────┬──────────┬──────────┬──────────┬────────┐│ +│ │ Invoice │ Reports │Inventory │ Admin │ Payroll││ +│ │ (10+) │ (20+) │ (8) │ (8) │ (7) ││ +│ └──────────┴──────────┴──────────┴──────────┴────────┘│ +└─────────────────────┬───────────────────────────────────┘ + │ + ┌─────────────┴─────────────┐ + │ │ +┌───────▼────────┐ ┌────────▼─────────┐ +│ PostgreSQL │ │ SQLite │ +│ (Business DB) │ │ (User Prefs) │ +│ - Accounts │ │ - Settings │ +│ - Invoices │ │ - Keybindings │ +│ - Contacts │ │ - Connections │ +│ - Products │ │ - UI State │ +│ - Payroll │ └──────────────────┘ +└────────────────┘ +``` + +### Module Organization + +The codebase is organized by business domain, with 181 Python files in the `src/` directory: + +``` +src/ +├── main.py # Application entry point +├── main_window.py # Primary GUI container (728 lines) +├── constants.py # Global state and broadcast system +├── sqlite_utils.py # Local preferences management +├── admin/ # Data import/export, maintenance (8 modules) +├── db/ # Database utilities (6 modules) +├── invoice/ # Invoice creation and management (10 modules) +├── inventory/ # Stock tracking (8 modules) +├── manufacturing/ # Production and assembly (12 modules) +├── payroll/ # Employee and payroll (7 modules) +├── reports/ # Financial and business reports (20+ modules) +├── resources/ # Resource scheduling (7 modules) +└── [120+ feature modules] # Individual business functions +``` + +**Largest Modules:** +1. `purchase_order_window.py` - 1,325 lines +2. `invoice_window.py` - 1,174 lines +3. `py3o/template/main.py` - 1,062 lines +4. `documents_window.py` - 827 lines +5. `db/transactor.py` - 763 lines + +--- + +## 3. Database Architecture + +### PostgreSQL Schema + +The application uses a comprehensive PostgreSQL database with multiple schemas: + +#### Core Tables (public schema) +- **Accounting:** + - `gl_accounts` - Chart of accounts (assets, liabilities, equity, revenue, expenses) + - `gl_transactions` - Transaction headers + - `gl_entries` - Double-entry journal entries + - `gl_account_flow` - Account routing rules for automated entries + - `fiscal_years` - Accounting periods + +- **Business Entities:** + - `contacts` - Customers and vendors + - `terms_and_discounts` - Payment terms + - `customer_markup_percent` - Pricing overrides + - `tax_rates` - Sales tax configuration + - `locations` - Business locations + +- **Products & Inventory:** + - `products` - Product catalog + - `product_location` - Inventory by location + - `serial_numbers` - Serialized inventory tracking + - `products_markup_prices` - Customer-specific pricing + +- **Transactions:** + - `invoices` / `invoice_items` - Customer invoices + - `purchase_orders` / `purchase_order_items` - Vendor orders + - `payments_incoming` / `payments_outgoing` - Payment records + - `credit_memos` - Credit transactions + +#### Payroll Schema +- `payroll.employee_info` - Employee master data +- `payroll.pay_stubs` - Payroll records +- `payroll.tax_table` - Tax rate configuration +- `payroll.emp_payments` - Payment tracking +- `payroll.time_clock_entries` - Time tracking + +#### Manufacturing Schema +- `manufactured_products` - Assemblies and kits +- `assembly_components` - Bill of materials +- `serial_numbers` - Product serial numbers + +### Real-Time Synchronization + +The application uses PostgreSQL's **LISTEN/NOTIFY** mechanism for real-time updates across multiple users: + +**Broadcast Channels:** +- `products` - Product catalog changes +- `contacts` - Customer/vendor updates +- `accounts` - Chart of accounts modifications +- `time_clock_entries` - Time clock events +- `invoices` - Invoice changes (with invoice_id payload) +- `purchase_orders` - PO changes (with po_id payload) + +Implementation in `constants.py`: +```python +class Broadcast(GObject.GObject): + def poll_connection(self): + DB.poll() + while DB.notifies: + notify = DB.notifies.pop(0) + if notify.channel == "products": + self.emit('products_changed') + # ... handle other channels +``` + +### SQLite Schema + +Local user preferences stored in SQLite: + +- **Connection Management:** + - `postgres_conn` - Default database connection + - `db_connections` - Multiple connection profiles + +- **User Preferences:** + - `settings` - Application configuration + - `keybindings` - Keyboard shortcuts + - Window sizes and positions + - Filter states for invoices/POs + - Print settings + +--- + +## 4. Key Design Patterns + +### 1. GTK Builder Pattern +Every UI module follows this pattern: +```python +class ModuleName(Gtk.Builder): + def __init__(self): + Gtk.Builder.__init__(self) + self.add_from_file("module_name.ui") + self.connect_signals(self) + self.window = self.get_object("window") +``` + +### 2. Transaction Factory Pattern +The `db/transactor.py` module implements factory classes for accounting transactions: + +```python +class Deposit: + def __init__(self, date): + # Create transaction header + c = DB.cursor() + c.execute("INSERT INTO gl_transactions (date_inserted) VALUES (%s) RETURNING id", (date,)) + self.transaction_id = c.fetchone()[0] + + def cash(self, amount, account): + # Record cash component + + def bank(self, amount, account): + # Record bank component + return deposit_id +``` + +**Transaction Types:** +- `Deposit` - Bank deposits +- `CustomerInvoicePayment` - Customer payments +- `VendorPayment` - Vendor payments +- `LoanPayment` - Loan payments +- `Paycheck` - Employee payroll + +### 3. Observer Pattern (Broadcaster) +The `constants.Broadcast` class implements a GTK-signal-based observer: +```python +# Publisher +DB.execute("NOTIFY products") + +# Subscriber +constants.broadcaster.connect('products_changed', self.on_products_changed) +``` + +### 4. Singleton Pattern +Used for shared resources (e.g., `pyjon/utils/main.py` metaclass singleton) + +--- + +## 5. Entry Points and Workflows + +### Application Startup + +1. **Shell Script:** `pygtk-posting` (wrapper script) +2. **Python Entry:** `src/main.py` + - Load SQLite preferences + - Connect to PostgreSQL + - Initialize constants (DB, broadcaster, ACCOUNTS) + - Launch main_window.MainGUI() +3. **Main Window:** `src/main_window.py` + - Load main_window.ui (Glade XML) + - Populate menu shortcuts + - Initialize keybindings + - Start GTK main loop + +### Key User Workflows + +**Invoice Creation Flow:** +``` +User clicks "New Invoice" + ↓ +invoice_window.py loads + ↓ +User selects customer → loads contact details, tax info, payment terms + ↓ +User adds products → updates line items, recalculates totals + ↓ +User posts invoice → + - Inserts into invoices table + - Creates gl_transaction + gl_entries (debit AR, credit Revenue) + - NOTIFY invoices channel + ↓ +All connected clients receive update via broadcaster +``` + +**Payment Processing Flow:** +``` +User enters payment + ↓ +customer_payment.py or vendor_payment.py + ↓ +Creates transactor instance (CustomerInvoicePayment or VendorPayment) + ↓ +Records: + - Payment record (payments_incoming/outgoing) + - GL transaction (debit Cash, credit AR/AP) + - Updates invoice/PO paid status + ↓ +Generates payment receipt (optional) +``` + +--- + +## 6. Code Quality Analysis + +### Positive Aspects + +1. **Consistent Structure:** Every module follows the GTK Builder pattern +2. **Proper Licensing:** GPLv3/LGPLv3 headers on all files +3. **Separation of Concerns:** Clear division between UI (.ui) and logic (.py) +4. **Active Development:** Recent commits show ongoing maintenance +5. **Real Production Use:** Code is battle-tested in actual businesses + +### Areas for Improvement + +#### 1. **Limited Test Coverage** +- Only 2 test files: `py3o/template/tests/test_*.py` +- No unit tests for core business logic +- No integration tests for accounting transactions +- No UI/acceptance tests + +**Impact:** High risk of regressions when making changes + +**Recommendation:** +- Add pytest framework +- Create unit tests for `db/transactor.py` classes +- Add integration tests for invoice/PO workflows +- Consider property-based testing for accounting rules + +#### 2. **Potential SQL Injection Risks** +Found 4 SQL injection vulnerabilities using string formatting in SQL queries. + +**Vulnerable Files (see SQL_INJECTION_ANALYSIS.md for details):** +- `src/kit_products.py` - Line 61 (HIGH severity, currently dead code) +- `src/documents_window.py` - Line 112 (HIGH severity, exploitable via drag-and-drop) +- `src/complete_search.py` - Lines 90-91 (HIGH severity, exploitable via TreeView) +- `src/db/database_tools.py` - Line 219 (MEDIUM severity, database name validation needed) + +**False Positives (verified safe):** +- `src/db/transactor.py` - Uses proper parameterized queries +- `src/purchase_ordering.py` - Uses proper parameterized queries +- `src/payroll/pay_stub.py` - Uses proper parameterized queries + +**Example pattern to review:** +```python +cursor.execute("SELECT * FROM table WHERE id = %s" % user_input) # UNSAFE +# Should be: +cursor.execute("SELECT * FROM table WHERE id = %s", (user_input,)) # SAFE +``` + +**Recommendation:** See [SQL_INJECTION_ANALYSIS.md](SQL_INJECTION_ANALYSIS.md) for detailed vulnerability analysis, exploitation scenarios, and remediation steps. Prioritize fixing `documents_window.py` and `complete_search.py` immediately as they are actively exploitable. + +#### 3. **Large Monolithic Files** +Several files exceed 1000 lines: +- `purchase_order_window.py` - 1,325 lines +- `invoice_window.py` - 1,174 lines + +**Recommendation:** +- Refactor into smaller, focused classes +- Extract common logic into shared utilities +- Consider separate classes for UI, business logic, and data access + +#### 4. **Minimal Error Handling** +Only 5 exception handlers found across 181 files + +**Recommendation:** +- Add try-except blocks around database operations +- Implement graceful error messages for users +- Add logging for debugging (already has `traceback_handler.py`) + +#### 5. **No Type Hints** +Python 3 code lacks type annotations + +**Recommendation:** +- Add type hints for function signatures +- Use mypy for static type checking +- Start with core modules (transactor, constants) + +#### 6. **Password Storage** +Passwords stored in SQLite database (`postgres_conn.password`) + +**Current:** Plain text storage in local SQLite +**Risk:** Low (file system permissions protect) +**Best Practice:** Use keyring/secrets manager + +#### 7. **No Formal Documentation** +- README is minimal +- No developer guide +- No API documentation +- No architecture diagrams + +**Recommendation:** +- Add docstrings to all classes and functions +- Create CONTRIBUTING.md +- Generate Sphinx/MkDocs documentation +- Document database schema + +--- + +## 7. Security Considerations + +### Current Security Measures +1. **Authentication:** PostgreSQL database-level authentication +2. **Admin Mode:** Separate admin login for sensitive operations +3. **File Permissions:** Relies on OS file system security for SQLite +4. **Input Validation:** GTK widgets provide some input constraints + +### Security Risks + +#### High Priority +1. **SQL Injection:** Potential vulnerabilities in dynamic SQL construction +2. **Credentials Storage:** Database passwords in plain SQLite file + +#### Medium Priority +3. **Session Management:** No timeout/auto-logout for admin mode +4. **Audit Logging:** Limited audit trail for sensitive operations +5. **Data Export:** No encryption for exported financial data + +#### Low Priority +6. **Dependency Vulnerabilities:** Should audit third-party library versions +7. **Input Sanitization:** Some user inputs may not be properly validated + +### Recommendations +1. **Immediate:** Audit and fix SQL injection vulnerabilities +2. **Short-term:** Implement credential encryption (keyring) +3. **Medium-term:** Add comprehensive audit logging +4. **Long-term:** Security code review and penetration testing + +--- + +## 8. Testing Infrastructure + +### Current State +**Test Files:** 2 (both in `py3o/template/tests/`) +- `test_helpers.py` - 957 lines +- `test_templates.py` - Tests for template rendering + +**Framework:** Python `unittest` + +**Coverage:** Only py3o template engine is tested + +### Recommended Test Strategy + +#### 1. Unit Tests +**Priority modules to test:** +- `db/transactor.py` - Accounting transaction logic +- `dateutils.py` - Date calculations +- `printing.py` - Print formatting +- All calculation functions in invoice/PO modules + +**Framework:** pytest (more Pythonic than unittest) + +#### 2. Integration Tests +**Key workflows:** +- Create invoice → Post → Payment → GL entries verification +- Create PO → Receive → Payment → GL entries verification +- Payroll calculation → GL entries verification +- Inventory adjustment → GL entries verification + +**Approach:** Use test database with rollback after each test + +#### 3. Database Tests +**Verify:** +- Double-entry accounting (debits = credits) +- Referential integrity +- Trigger behavior +- LISTEN/NOTIFY functionality + +#### 4. UI Tests (Optional) +**Framework:** pytest + GTK test utilities +**Coverage:** Critical paths (invoice creation, payment entry) + +--- + +## 9. Build and Deployment + +### Build System +**Autotools (GNU Build System):** +- `configure.ac` - Autoconf configuration +- `Makefile.am` - Automake template +- `install-sh` - Installation script + +**Process:** +```bash +./configure +make +make install +``` + +### Package Distribution + +#### 1. Debian Package +**Tools:** `create_deb.py` custom script +**Output:** `pygtk_posting_0.5.34-1.deb` +**Scripts:** +- `postinst` - Post-installation setup +- `prerm` - Pre-removal cleanup +- `control` - Package metadata + +#### 2. Python Package +**Tools:** `setup.py` (setuptools) +**Command:** `python setup.py install` + +#### 3. Source Distribution +**Output:** `pygtk_posting-0.5.34.tar.gz` + +### Dependencies + +**Runtime Dependencies (from requirements.txt):** +``` +python3-apsw # SQLite with advanced features +python3-cairocffi # Cairo graphics bindings +python3-genshi # Template engine +python3-lxml # XML processing +python3-matplotlib # Charting +python3-psycopg2 # PostgreSQL driver +python3-sane # Scanner support +python3-tk # Tkinter (calendar widget) +python3-uno # LibreOffice UNO bridge +unoconv # Document conversion +python3-xlrd # Excel reading +python3-xlsxwriter # Excel writing +gir1.2-goocanvas-2.0 # Canvas widget +``` + +**Development Dependencies:** +- Python 3.6+ +- PostgreSQL 10+ +- LibreOffice (for document generation) + +--- + +## 10. Development Patterns and Best Practices + +### Current Patterns + +#### 1. File Structure +Every feature module typically includes: +- `module_name.py` - Python logic +- `module_name.ui` - Glade UI definition +- Class inherits from `Gtk.Builder` + +#### 2. Database Access +**Pattern:** Direct SQL via psycopg2 cursors +```python +c = DB.cursor() +c.execute("SELECT ... FROM ... WHERE id = %s", (id,)) +result = c.fetchone() +c.close() +``` + +**Note:** No ORM (SQLAlchemy/Django ORM) used + +#### 3. Signal Handling +**GTK Signals:** Defined in .ui files or connected in Python +**Custom Signals:** Broadcast system for inter-module communication + +#### 4. Date Handling +Custom `dateutils.py` module for date calculations and formatting + +#### 5. Printing +Custom `printing.py` module wrapping Cairo graphics + +### Recommended Improvements + +#### 1. Add Context Managers +```python +# Current +c = DB.cursor() +c.execute(...) +c.close() + +# Recommended +with DB.cursor() as c: + c.execute(...) +``` + +#### 2. Configuration Management +Move hardcoded values to configuration: +- Report file paths +- Default values +- Business rules + +#### 3. Logging Strategy +Standardize logging: +```python +import logging +logger = logging.getLogger(__name__) +logger.info("Invoice created: %s", invoice_id) +``` + +#### 4. Code Style +Apply consistent code style: +- Run `black` formatter +- Use `flake8` or `ruff` for linting +- Add `pre-commit` hooks + +--- + +## 11. Known Issues and Technical Debt + +### From TO_DO.md +1. ✓ Add paid/posted/canceled toggles to invoice history +2. ✓ Load canceled invoices in history +3. ⚠️ Finish inventory calculations for invoices/POs +4. ⚠️ Admin login state not clear in some cases +5. ⚠️ Admin should use PostgreSQL credentials +6. ✓ Add more window keybindings +7. ⚠️ PDF attachment output window scrolling issue +8. ⚠️ Add split entry feature for double-entry accounting + +### Technical Debt Identified +1. **Large Files:** Refactor 1000+ line modules +2. **Duplicate Code:** Extract common patterns +3. **Global State:** Reduce reliance on `constants` module +4. **Error Handling:** Add comprehensive exception handling +5. **Documentation:** Add docstrings and comments +6. **Testing:** Build comprehensive test suite +7. **Type Safety:** Add type hints + +--- + +## 12. Recommendations + +### Immediate Priorities (Next Sprint) +1. ✅ **Security Audit:** Review and fix SQL injection risks +2. ✅ **Add .gitignore:** Exclude .pyc, __pycache__, build artifacts +3. ✅ **Document Database Schema:** Generate ER diagram +4. ✅ **Basic Unit Tests:** Add tests for transaction classes + +### Short-term Goals (1-3 Months) +5. ✅ **Code Linting:** Set up black/flake8 +6. ✅ **Error Handling:** Add try-except to database operations +7. ✅ **Logging Framework:** Standardize logging across modules +8. ✅ **Type Hints:** Add to core modules + +### Medium-term Goals (3-6 Months) +9. ✅ **Refactor Large Files:** Break down 1000+ line modules +10. ✅ **Integration Tests:** Test key accounting workflows +11. ✅ **Developer Documentation:** Create comprehensive docs +12. ✅ **CI/CD Pipeline:** Set up automated testing + +### Long-term Vision (6-12 Months) +13. ✅ **Web Interface:** Consider web frontend option +14. ✅ **REST API:** Expose accounting functions via API +15. ✅ **Mobile App:** True mobile support (not just flag) +16. ✅ **Plugin System:** Allow third-party extensions + +--- + +## 13. Metrics and Statistics + +### Code Metrics +- **Total Python Files:** 181 +- **Total Lines of Code:** ~43,896 +- **UI Definition Files:** 141 +- **Modules by Domain:** + - Reports: 20+ + - Manufacturing: 12 + - Invoice: 10 + - Inventory: 8 + - Admin: 8 + - Payroll: 7 + - Resources: 7 + - Database: 6 + - Core: 120+ individual feature modules + +### Complexity Indicators +- **Largest File:** 1,325 lines (purchase_order_window.py) +- **Exception Handlers:** 5 (very low) +- **Test Coverage:** <5% (only py3o module) +- **TODO Comments:** Found in 10+ files + +### Maintenance Indicators +- **Active Development:** Yes (recent commits) +- **Production Use:** Yes (multiple businesses) +- **Documentation:** Minimal +- **Community:** Small but growing + +--- + +## 14. Conclusion + +PyGtk-Posting is a **mature, feature-complete desktop accounting system** with strong domain modeling and real-world usage. The codebase demonstrates solid understanding of accounting principles and effective use of GTK/PostgreSQL technologies. + +**Key Strengths:** +- Comprehensive feature set +- Proper double-entry accounting +- Real-time multi-user synchronization +- Active maintenance + +**Primary Needs:** +- Testing infrastructure +- Security hardening +- Code documentation +- Refactoring for maintainability + +The project is well-positioned for growth with the addition of modern development practices (testing, CI/CD, documentation) and security improvements. + +**Overall Assessment:** ⭐⭐⭐⭐ (4/5) +- Production-ready for core features +- Needs improvement in testing and documentation +- Solid foundation for future enhancements + +--- + +**Report Generated:** January 22, 2026 +**Analyst:** GitHub Copilot +**Next Review:** Recommended in 6 months or after major refactoring diff --git a/SQL_INJECTION_ANALYSIS.md b/SQL_INJECTION_ANALYSIS.md new file mode 100644 index 0000000..57f365a --- /dev/null +++ b/SQL_INJECTION_ANALYSIS.md @@ -0,0 +1,324 @@ +# SQL Injection Vulnerability Analysis + +## Summary + +This document identifies 4 SQL injection vulnerabilities found in the PyGtk-Posting codebase. All instances use Python's `%` string formatting to construct SQL queries with untrusted input, which can allow attackers to execute arbitrary SQL commands. + +--- + +## Vulnerability Details + +### 1. kit_products.py - Line 61 + +**File:** `src/kit_products.py` +**Line:** 61 +**Severity:** HIGH + +**Vulnerable Code:** +```python +def on_drag_data_received(self, widget, drag_context, x,y, data,info, time): + return # Note: This function has an early return, so not currently exploitable + _list_ = data.get_text().split(' ') + if len(_list_) != 2: + return + table, _id_ = _list_[0], _list_[1] + self.cursor.execute("SELECT product, remark, cost FROM %s WHERE id = %s" % (table, _id_)) + for row in self.cursor.fetchall(): + product = row[0] + remark = row[1] + price = row[2] +``` + +**Issue:** +- Uses `%` string formatting to insert `table` and `_id_` directly into SQL query +- Both `table` (table name) and `_id_` come from drag-and-drop data that could be manipulated +- **Current Status:** Function has `return` on line 56, so code is not executed (dead code) +- **Risk if enabled:** An attacker could craft malicious drag data like: + ``` + "products; DROP TABLE invoices; -- 123" + ``` + +**Recommended Fix:** +```python +# For table names, use identifier quoting (but validate against whitelist first!) +from psycopg2 import sql + +# Validate table name against whitelist +allowed_tables = ['products', 'invoice_items', 'purchase_order_items'] +if table not in allowed_tables: + return + +# Use parameterized query for ID +self.cursor.execute( + sql.SQL("SELECT product, remark, cost FROM {} WHERE id = %s").format( + sql.Identifier(table) + ), + (_id_,) +) +``` + +--- + +### 2. documents_window.py - Line 112 + +**File:** `src/documents_window.py` +**Line:** 112 +**Severity:** HIGH + +**Vulnerable Code:** +```python +def on_drag_data_received(self, widget, drag_context, x,y, data,info, time): + _list_ = data.get_text().split(' ') + if len(_list_) != 2: + return + table, _id_ = _list_[0], _list_[1] + self.cursor.execute("SELECT product, remark, price FROM %s WHERE id = %s" % (table, _id_)) + for row in self.cursor.fetchall(): + product = row[0] + remark = row[1] + price = row[2] + print ("please implement me") #FIXME +``` + +**Issue:** +- Identical vulnerability to #1 above +- Uses `%` string formatting with untrusted drag-and-drop data +- Both `table` name and `_id_` are directly interpolated into the SQL query +- **Current Status:** Function is active (no early return) +- **Risk:** HIGH - Function is currently enabled and processes drag data + +**Attack Example:** +``` +Drag data: "products; DELETE FROM invoices WHERE 1=1; -- 123" +Result: All invoices could be deleted +``` + +**Recommended Fix:** +Same as #1 - use `psycopg2.sql.Identifier()` for table names with whitelist validation, and parameterized queries for values. + +--- + +### 3. complete_search.py - Lines 90-91 + +**File:** `src/complete_search.py` +**Line:** 90-91 +**Severity:** HIGH + +**Vulnerable Code:** +```python +def treeview_row_activated (self, treeview, path, treeviewcolumn): + schema = self.store[path][0] + table = self.store[path][1] + column = self.store[path][2] + ctid = self.store[path][4] + self.cursor.execute("SELECT * FROM %s.%s WHERE ctid = '%s'"% + (schema, table, ctid)) +``` + +**Issue:** +- Uses `%` string formatting with three untrusted values: `schema`, `table`, and `ctid` +- Values come from a TreeView store that could potentially be manipulated +- The `ctid` (tuple ID) is particularly dangerous as it's used in a string comparison +- **Risk:** HIGH - Schema, table, and ctid all come from user-selectable data + +**Attack Example:** +``` +If an attacker can manipulate the TreeView store: +ctid = "' OR '1'='1" +Result: Query becomes: SELECT * FROM schema.table WHERE ctid = '' OR '1'='1' +This would return all rows instead of a specific one +``` + +**Recommended Fix:** +```python +from psycopg2 import sql + +def treeview_row_activated(self, treeview, path, treeviewcolumn): + schema = self.store[path][0] + table = self.store[path][1] + column = self.store[path][2] + ctid = self.store[path][4] + + # Validate schema and table names against whitelist if possible + # Use sql.Identifier for schema/table names and parameterized query for ctid + self.cursor.execute( + sql.SQL("SELECT * FROM {}.{} WHERE ctid = %s").format( + sql.Identifier(schema), + sql.Identifier(table) + ), + (ctid,) + ) +``` + +--- + +### 4. db/database_tools.py - Line 219 + +**File:** `src/db/database_tools.py` +**Line:** 219 +**Severity:** MEDIUM + +**Vulnerable Code:** +```python +try: + self.cursor.execute("""CREATE DATABASE "%s";""" % db_name) +except Exception as e: + print (e) + if (e.pgcode == "42P04"): + self.status_update("Database already exists!") + return +``` + +**Issue:** +- Uses `%` string formatting to insert database name into CREATE DATABASE statement +- Database name likely comes from user input +- **Risk:** MEDIUM - PostgreSQL database names have restrictions, but an attacker could still: + - Create databases with malicious names + - Use special characters to break the query + - Potentially inject additional SQL commands + +**Note:** PostgreSQL's CREATE DATABASE cannot be parameterized (doesn't support placeholders). However, the database name should still be validated and properly quoted. + +**Recommended Fix:** +```python +from psycopg2 import sql + +# Validate database name (alphanumeric, underscore, max 63 chars) +import re +if not re.match(r'^[a-zA-Z_][a-zA-Z0-9_]{0,62}$', db_name): + self.status_update("Invalid database name!") + return + +try: + # Use sql.Identifier for proper quoting + self.cursor.execute( + sql.SQL("CREATE DATABASE {}").format(sql.Identifier(db_name)) + ) +except Exception as e: + print(e) + if hasattr(e, 'pgcode') and e.pgcode == "42P04": + self.status_update("Database already exists!") + return +``` + +--- + +## False Positives (Not Vulnerabilities) + +The following files were initially flagged but do NOT contain SQL injection vulnerabilities: + +### src/db/transactor.py +- **Line 543:** `cursor.execute("UPDATE gl_entries SET amount = %s WHERE id = %s", ...)` + - ✅ **SAFE:** Uses proper parameterized query with tuple + +### src/purchase_ordering.py +- **Line 132:** `cursor.execute("SELECT format_date(%s), format_date(%s)", ...)` + - ✅ **SAFE:** Uses proper parameterized query with tuple + +### src/payroll/pay_stub.py +- **Lines 236, 239, 375, 399:** All use parameterized queries + - ✅ **SAFE:** Uses proper parameterized query with tuple in all instances + +--- + +## Summary Table + +| File | Line | Severity | Status | Exploitable | +|------|------|----------|--------|-------------| +| src/kit_products.py | 61 | HIGH | Dead Code | No (has early return) | +| src/documents_window.py | 112 | HIGH | Active | Yes | +| src/complete_search.py | 90-91 | HIGH | Active | Yes | +| src/db/database_tools.py | 219 | MEDIUM | Active | Partially | + +**Total Vulnerabilities:** 4 +**Currently Exploitable:** 3 +**Dead Code (not exploitable now):** 1 + +--- + +## Impact Assessment + +### Potential Consequences + +1. **Data Theft:** Attackers could read sensitive financial data, customer information, employee records +2. **Data Modification:** Attackers could modify invoices, payments, account balances +3. **Data Destruction:** Attackers could drop tables, delete records +4. **Privilege Escalation:** Attackers could potentially modify user permissions +5. **Business Disruption:** Database corruption could halt business operations + +### Attack Vector + +- **documents_window.py** and **kit_products.py**: Exploitable via drag-and-drop operations. An attacker would need to: + 1. Run the application + 2. Navigate to a window with drag-and-drop enabled + 3. Craft malicious drag data containing SQL injection payloads + 4. Drag the malicious data to trigger the vulnerable function + +- **complete_search.py**: Exploitable if an attacker can manipulate the TreeView store data, possibly through: + 1. Database manipulation (if they already have some access) + 2. UI manipulation via accessibility tools + 3. Memory manipulation + +- **database_tools.py**: Exploitable when creating a new database with a crafted name + +--- + +## Remediation Priority + +### Immediate (Fix Now) +1. ✅ **documents_window.py:112** - Active and exploitable via UI +2. ✅ **complete_search.py:90-91** - Active and exploitable + +### High Priority (Fix Soon) +3. ✅ **db/database_tools.py:219** - Active but limited by PostgreSQL naming restrictions +4. ✅ **kit_products.py:61** - Currently dead code, but should be fixed if ever re-enabled + +--- + +## General Recommendations + +### 1. Code-wide SQL Query Audit +Conduct a comprehensive audit of all SQL queries using automated tools: +```bash +# Search for potential SQL injection patterns +grep -r "execute.*%.*%" src/ +grep -r "execute.*\.format" src/ +grep -r 'execute.*f"' src/ +``` + +### 2. Establish Secure Coding Guidelines +- **Always use parameterized queries** for values: `cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))` +- **Use psycopg2.sql.Identifier()** for dynamic table/column names with whitelist validation +- **Never** use string formatting (`%`, `.format()`, f-strings) to build SQL queries +- **Validate all input** from users, even if it comes from UI widgets + +### 3. Implement Code Review Process +- Require peer review for all database query changes +- Use static analysis tools (e.g., Bandit, Semgrep) in CI/CD pipeline +- Add pre-commit hooks to catch dangerous patterns + +### 4. Security Testing +- Add security-focused unit tests that attempt SQL injection +- Consider penetration testing by security professionals +- Implement input validation testing in QA process + +### 5. Defense in Depth +- Use PostgreSQL role-based access control (RBAC) +- Run application with least-privilege database user +- Enable PostgreSQL audit logging +- Regular database backups + +--- + +## References + +- [OWASP SQL Injection](https://owasp.org/www-community/attacks/SQL_Injection) +- [psycopg2 SQL Composition](https://www.psycopg.org/docs/sql.html) +- [PostgreSQL Security Best Practices](https://www.postgresql.org/docs/current/sql-syntax.html) +- [CWE-89: SQL Injection](https://cwe.mitre.org/data/definitions/89.html) + +--- + +**Document Version:** 1.0 +**Analysis Date:** January 22, 2026 +**Analyst:** GitHub Copilot