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β
| # | Severity | Vulnerability | Status |
|---|---|---|---|
| 1 | π΄ Critical | No TLS certificate pinning | β Done |
| 2 | π΄ Critical | PowerShell script injection risk | β Done |
| 3 | π΄ Critical | No HTTP request timeout | β Done |
| 4 | π High | Downloaded filename path traversal | β Done |
| 5 | π High | Token fallback to plaintext SharedPreferences | β Done |
| 6 | π High | Hardcoded API URL in binary | β Done |
| 7 | π High | Verbose logging (paths, file details) | β Done |
| 8 | π‘ Medium | No session timeout | β Done |
| 9 | π‘ Medium | External process execution (SumatraPDF, LibreOffice) | β οΈ Acceptable |
| 10 | π’ Low | No 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
binfolder). - 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β
| Item | Status | Notes |
|---|---|---|
| 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)β
-
PowerShell injectionβ_sanitizeForPowerShell()in printer_service -
HTTP timeoutβ 30s / 120s on all API calls -
Filename sanitizationβsanitizeFilename()in filename_utils -
Certificate pinningβ Intermediate CA via http_client_factory -
Verbose loggingβ kDebugMode guard on all print statements -
API URL configurationβ--dart-define=BASE_URL -
Session timeoutβ Done (30 min idle via SessionService). -
Token fallbackβ Done (no SharedPreferences in production). -
File integrityβ Done (verifies when API provides X-Content-SHA256).
Lower priorityβ
- 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 auditregularly.
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