Skip to main content

Shopkeeper App (Windows) β€” Cybersecurity Audit

Date: January 31, 2026
Last Updated: February 2026
Scope: shopkeeper_app (Flutter β€” Windows primary, Linux secondary)
Auditor: Windows App Cybersecurity Expert Analysis
Platform focus: Windows desktop (print shop kiosk / point-of-sale)


Executive Summary​

The shopkeeper app handles sensitive operations: authentication, print job processing, customer file downloads, and printer control. This audit identifies vulnerabilities specific to a Windows desktop deployment and their remediation status.

Overall risk: 🟒 LOW β€” All critical, high, and medium items implemented (February 2026). Session timeout, token fallback restriction, and file integrity verification are done.


1. Vulnerability Summary​

#SeverityVulnerabilityStatus
1πŸ”΄ CriticalNo TLS certificate pinningβœ… Done
2πŸ”΄ CriticalPowerShell script injection riskβœ… Done
3πŸ”΄ CriticalNo HTTP request timeoutβœ… Done
4🟠 HighDownloaded filename path traversalβœ… Done
5🟠 HighToken fallback to plaintext SharedPreferencesβœ… Done
6🟠 HighHardcoded API URL in binaryβœ… Done
7🟠 HighVerbose logging (paths, file details)βœ… Done
8🟑 MediumNo session timeoutβœ… Done
9🟑 MediumExternal process execution (SumatraPDF, LibreOffice)⚠️ Acceptable
10🟒 LowNo file integrity verificationβœ… Done

2. Detailed Findings​

2.1 πŸ”΄ No TLS Certificate Pinning β€” βœ… Implemented​

Location: lib/services/api_service.dart β€” previously used package:http with no custom SecurityContext.

Risk: On untrusted networks (public Wi‑Fi, compromised router), an attacker can perform a man‑in‑the‑middle (MITM) attack.

Implementation (Feb 2026): lib/services/http_client_factory.dart creates HttpClient with SecurityContext using Let's Encrypt intermediate CA certificates (lib/config/letsencrypt_certs.dart). Pinning disabled in debug mode via AppConfig.certificatePinningEnabled. See Maintenance Guide for certificate rotation.

References: OWASP Mobile Top 10 (M3 – Insecure Communication).


2.2 πŸ”΄ PowerShell Script Injection β€” βœ… Implemented​

Location: lib/services/printer_service.dart lines 298–306, 348.

Issue: documentName and printerName are interpolated directly into PowerShell scripts passed to Process.run('powershell', ['-NoProfile', '-Command', psScript]):

// Line 298-299
Where-Object { \$_.Document -like "*$documentName*" -and \$_.PrinterName -eq "$printerName" }

// Line 348
Filter "Name='$printerName'"

Risk: Malicious filenames (e.g. containing " or *) or printer names could break the script or, in worst case, inject PowerShell commands. Document name comes from file.path.split(Platform.pathSeparator).last (local file path). Printer name comes from system enumeration but is user‑selected and could be tampered if the enumeration is spoofed.

Implementation (Feb 2026): _sanitizeForPowerShell() in lib/services/printer_service.dart strips unsafe characters (", ', `, $, *, ?, \, \r, \n) and limits length to 200 chars. Applied to documentName and printerName before embedding in PowerShell scripts.


2.3 πŸ”΄ No HTTP Request Timeout β€” βœ… Implemented​

Location: lib/services/api_service.dart β€” all HTTP calls.

Implementation (Feb 2026): _requestTimeout = Duration(seconds: 30) and _downloadTimeout = Duration(seconds: 120) applied to all API requests via .timeout().


2.4 🟠 Downloaded Filename Path Traversal β€” βœ… Implemented​

Location: lib/screens/dashboard_screen.dart line 543:

final tempFile = File('${tempDir.path}/batch_${fileId}_$filename');

Issue: filename comes from the API response (fileInfo['filename']). The server should sanitize, but the client should not trust it. A value like ../../../malicious.pdf could cause writes outside the temp directory.

Implementation (Feb 2026): lib/utils/filename_utils.dart provides sanitizeFilename() β€” strips path separators, replaces unsafe chars with _, limits to 200 chars. Used in dashboard_screen.dart for downloaded files.


2.5 🟠 Token Fallback to Plaintext​

Location: lib/services/api_service.dart lines 46–52, 324–332.

Issue: If flutter_secure_storage.write() fails (e.g. on some Windows setups), the token is stored in SharedPreferences, which is plaintext on disk. Any process with user access can read it.

Current mitigation: Primary path uses Flutter Secure Storage (DPAPI on Windows). Fallback is documented as a workaround.

Recommendation:

  • Log when fallback is used; consider refusing to store the token if secure storage fails in production.
  • Add a migration that moves tokens from SharedPreferences to secure storage on next successful login.

2.6 🟠 Hardcoded API URL β€” βœ… Implemented​

Location: lib/config/app_config.dart.

Implementation (Feb 2026): AppConfig.baseUrl reads from --dart-define=BASE_URL=...; falls back to production URL if empty. Build: flutter run --dart-define=BASE_URL=https://....


2.7 🟠 Verbose Logging β€” βœ… Implemented​

Location: lib/services/converter_engine.dart, lib/services/printer_service.dart, lib/screens/dashboard_screen.dart, lib/main.dart, lib/services/pdf_utils.dart.

Implementation (Feb 2026): All print() and debugPrint() statements wrapped with if (kDebugMode) so verbose output is disabled in release builds.


2.8 🟑 No Session Timeout β€” βœ… Implemented​

Implementation (Feb 2026): lib/services/session_service.dart tracks activity via touch(). 30 min idle triggers logout and navigation to login. Wrapped with Listener(onPointerDown: ...) for user activity. Cleared on logout and dispose.


2.9 🟑 External Process Execution​

Location: lib/services/printer_service.dart (SumatraPDF), lib/services/converter_engine.dart (LibreOffice).

Issue: The app spawns SumatraPDF.exe and soffice.exe (LibreOffice). If an attacker can replace these binaries, they could achieve code execution.

Current mitigation: Binaries are loaded from app directory or windows/runner/bin/. Setup scripts download from official sources.

Recommendation:

  • Verify binaries are from known locations (next to app exe or in a controlled bin folder).
  • Optionally verify checksums of SumatraPDF/LibreOffice after download.
  • Document expected install paths in setup docs.

2.10 🟒 No File Integrity Verification​

Location: lib/screens/dashboard_screen.dart β€” downloaded file bytes are written directly to disk.

Issue: No hash verification. A compromised or MITM’d response could deliver malicious PDFs. Combined with conversion/printing, this could lead to parsing exploits (e.g. malformed PDF).

Recommendation:

  • If the API provides a hash (e.g. SHA‑256), verify before processing.
  • Consider sandboxing or limiting PDF parsing to known‑safe operations.

3. Implemented Protections​

ItemStatusNotes
Flutter Secure Storageβœ…Token stored with DPAPI on Windows; fallback to SharedPreferences when needed
Server logoutβœ…Calls POST /logout on sign out
Generic error messagesβœ…getSafeErrorMessage() used on auth and critical screens
Role enforcementβœ…Rejects non‑shopkeeper users at login
HTTPS in productionβœ…AppConfig.baseUrl uses https://
Temp file cleanupβœ…Temp files deleted after successful print
Backend authβœ…Bearer token required for queue/download/confirm
Certificate pinningβœ…Intermediate CA (Let's Encrypt); http_client_factory.dart
HTTP timeoutβœ…30s requests, 120s downloads
API URL configβœ…--dart-define=BASE_URL
PowerShell injection fixβœ…_sanitizeForPowerShell() in printer_service
Path traversal sanitizationβœ…sanitizeFilename() in filename_utils
Verbose logging reductionβœ…kDebugMode guard on all print statements
Session timeoutβœ…SessionService 30 min idle; Listener for touch
Token fallback restrictionβœ…No SharedPreferences fallback in release builds
File integrity verificationβœ…verifyFileIntegrity() when API sends X-Content-SHA256

4. Priority Remediation List​

Completed (Feb 2026)​

  1. PowerShell injection β€” _sanitizeForPowerShell() in printer_service

  2. HTTP timeout β€” 30s / 120s on all API calls

  3. Filename sanitization β€” sanitizeFilename() in filename_utils

  4. Certificate pinning β€” Intermediate CA via http_client_factory

  5. Verbose logging β€” kDebugMode guard on all print statements

  6. API URL configuration β€” --dart-define=BASE_URL

  7. Session timeout β€” Done (30 min idle via SessionService).

  8. Token fallback β€” Done (no SharedPreferences in production).

  9. File integrity β€” Done (verifies when API provides X-Content-SHA256).

Lower priority​

  1. External binaries β€” Document and optionally checksum SumatraPDF/LibreOffice.

5. Windows‑Specific Considerations​

  • DPAPI: Flutter Secure Storage uses Windows Data Protection API. Tokens are encrypted per‑user. On shared PCs, each user has separate storage.
  • UAC: App runs with user privileges. No admin required for printing.
  • Defender / Antivirus: May flag PowerShell use or process spawning. Consider code signing and vendor whitelisting.
  • Updates: Ensure Flutter and dependencies are kept up to date; run flutter pub audit regularly.

6. References​

  • OWASP Mobile Security Testing Guide
  • OWASP Mobile Top 10
  • project-docs/docs/development/security-audit.md (main platform audit)
  • SHOPKEEPER_APP_SECURITY_STATUS.md (code vs audit comparison)

Report generated: January 31, 2026
Last updated: February 2026 β€” Critical and high items implemented.
Next review: After implementing session timeout or before major release