# SneakyScanner Phase 2 - Manual Testing Checklist **Version:** 2.0 (Phase 2) **Last Updated:** 2025-11-14 This document provides a comprehensive manual testing checklist for validating the SneakyScanner web application. Use this checklist to verify all features work correctly before deployment or release. --- ## Table of Contents 1. [Prerequisites](#prerequisites) 2. [Deployment & Startup](#deployment--startup) 3. [Authentication](#authentication) 4. [Scan Management (Web UI)](#scan-management-web-ui) 5. [Scan Management (API)](#scan-management-api) 6. [Error Handling](#error-handling) 7. [Performance & Concurrency](#performance--concurrency) 8. [Data Persistence](#data-persistence) 9. [Security](#security) 10. [Cleanup](#cleanup) --- ## Prerequisites Before starting manual testing: - [ ] Docker and Docker Compose installed - [ ] `.env` file configured with proper keys - [ ] Test scan configuration available (e.g., `configs/example-site.yaml`) - [ ] Network access for scanning (if using real targets) - [ ] Browser for web UI testing (Chrome, Firefox, Safari, Edge) - [ ] `curl` and `jq` for API testing - [ ] At least 2GB free disk space for scan results **Recommended Test Environment:** - Clean database (no existing scans) - Test config with 1-2 IPs, 2-3 expected ports - Expected scan duration: 1-3 minutes --- ## Deployment & Startup ### Test 1: Environment Configuration **Objective:** Verify environment variables are properly configured. **Steps:** 1. Check `.env` file exists: ```bash ls -la .env ``` 2. Verify required keys are set (not defaults): ```bash grep SECRET_KEY .env grep SNEAKYSCANNER_ENCRYPTION_KEY .env ``` 3. Verify keys are not default values: ```bash grep -v "your-secret-key-here" .env | grep SECRET_KEY ``` **Expected Result:** - [ ] `.env` file exists - [ ] `SECRET_KEY` is set to unique value (not `your-secret-key-here`) - [ ] `SNEAKYSCANNER_ENCRYPTION_KEY` is set to unique value - [ ] All required environment variables present ### Test 2: Docker Compose Startup **Objective:** Verify web application starts successfully. **Steps:** 1. Start services: ```bash docker-compose -f docker-compose-web.yml up -d ``` 2. Check container status: ```bash docker-compose -f docker-compose-web.yml ps ``` 3. Check logs for errors: ```bash docker-compose -f docker-compose-web.yml logs web | tail -50 ``` 4. Wait 30 seconds for healthcheck to pass **Expected Result:** - [ ] Container starts without errors - [ ] Status shows "Up" or "healthy" - [ ] No error messages in logs - [ ] Port 5000 is listening ### Test 3: Health Check **Objective:** Verify health check endpoint responds correctly. **Steps:** 1. Call health endpoint: ```bash curl -s http://localhost:5000/api/settings/health | jq '.' ``` **Expected Result:** - [ ] HTTP 200 status code - [ ] Response: `{"status": "healthy", "database": "connected"}` - [ ] No authentication required ### Test 4: Database Initialization **Objective:** Verify database was created and initialized. **Steps:** 1. Check database file exists: ```bash docker exec sneakyscanner_web ls -lh /app/data/sneakyscanner.db ``` 2. Verify database has tables: ```bash docker exec sneakyscanner_web sqlite3 /app/data/sneakyscanner.db ".tables" ``` **Expected Result:** - [ ] Database file exists (`sneakyscanner.db`) - [ ] Database file size > 0 bytes - [ ] All 11 tables present: `scans`, `scan_sites`, `scan_ips`, `scan_ports`, `scan_services`, `scan_certificates`, `scan_tls_versions`, `schedules`, `alerts`, `alert_rules`, `settings` --- ## Authentication ### Test 5: Login Page Access **Objective:** Verify unauthenticated users are redirected to login. **Steps:** 1. Open browser to http://localhost:5000/dashboard (without logging in) 2. Observe redirect **Expected Result:** - [ ] Redirected to http://localhost:5000/login - [ ] Login page displays correctly - [ ] Dark theme applied (slate/grey colors) - [ ] Username and password fields visible - [ ] "Login" button visible ### Test 6: Login with Correct Password **Objective:** Verify successful login flow. **Steps:** 1. Navigate to http://localhost:5000/login 2. Enter password (default: `admin`) 3. Click "Login" button **Expected Result:** - [ ] Redirected to http://localhost:5000/dashboard - [ ] No error messages - [ ] Navigation bar shows "Dashboard", "Scans", "Settings", "Logout" - [ ] Welcome message displayed ### Test 7: Login with Incorrect Password **Objective:** Verify failed login handling. **Steps:** 1. Navigate to http://localhost:5000/login 2. Enter incorrect password (e.g., `wrongpassword`) 3. Click "Login" button **Expected Result:** - [ ] Stays on login page (no redirect) - [ ] Error message displayed: "Invalid password" - [ ] Password field cleared - [ ] Can retry login ### Test 8: Logout **Objective:** Verify logout destroys session. **Steps:** 1. Login successfully 2. Navigate to http://localhost:5000/dashboard 3. Click "Logout" in navigation bar 4. Try to access http://localhost:5000/dashboard again **Expected Result:** - [ ] Logout redirects to login page - [ ] Flash message: "Logged out successfully" - [ ] Session destroyed (redirected to login when accessing protected pages) - [ ] Cannot access dashboard without re-logging in ### Test 9: API Authentication (Session Cookie) **Objective:** Verify API endpoints require authentication. **Steps:** 1. Call API endpoint without authentication: ```bash curl -i http://localhost:5000/api/scans ``` 2. Login and save session cookie: ```bash curl -X POST http://localhost:5000/auth/login \ -H "Content-Type: application/json" \ -d '{"password":"admin"}' \ -c cookies.txt ``` 3. Call API endpoint with session cookie: ```bash curl -b cookies.txt http://localhost:5000/api/scans ``` **Expected Result:** - [ ] Request without auth returns 401 Unauthorized - [ ] Login returns 200 OK with session cookie - [ ] Request with auth cookie returns 200 OK with scan data --- ## Scan Management (Web UI) ### Test 10: Dashboard Display **Objective:** Verify dashboard loads and displays correctly. **Steps:** 1. Login successfully 2. Navigate to http://localhost:5000/dashboard 3. Observe page content **Expected Result:** - [ ] Dashboard loads without errors - [ ] Welcome message displayed - [ ] "Run Scan Now" button visible - [ ] Recent scans section visible (may be empty) - [ ] Navigation works ### Test 11: Trigger Scan via Web UI **Objective:** Verify scan can be triggered from dashboard. **Steps:** 1. Login and go to dashboard 2. Click "Run Scan Now" button 3. Observe scan starts 4. Wait for scan to complete (1-3 minutes) **Expected Result:** - [ ] Scan starts (status shows "Running") - [ ] Scan appears in recent scans list - [ ] Scan ID assigned and displayed - [ ] Status updates to "Completed" after scan finishes - [ ] No error messages **Note:** If "Run Scan Now" button not yet implemented, use API to trigger scan (Test 15). ### Test 12: View Scan List **Objective:** Verify scan list page displays correctly. **Steps:** 1. Login successfully 2. Navigate to http://localhost:5000/scans 3. Trigger at least 3 scans (via API or UI) 4. Refresh scan list page **Expected Result:** - [ ] Scan list page loads - [ ] All scans displayed in table - [ ] Columns: ID, Timestamp, Title, Status, Actions - [ ] Pagination controls visible (if > 20 scans) - [ ] Each scan has "View" and "Delete" buttons ### Test 13: View Scan Details **Objective:** Verify scan detail page displays complete results. **Steps:** 1. From scan list, click "View" on a completed scan 2. Observe scan details page **Expected Result:** - [ ] Scan details page loads (http://localhost:5000/scans/{id}) - [ ] Scan metadata displayed (ID, timestamp, duration, status) - [ ] Sites section visible - [ ] IPs section visible with ping status - [ ] Ports section visible (TCP/UDP) - [ ] Services section visible with product/version - [ ] HTTPS services show certificate details (if applicable) - [ ] TLS versions displayed (if applicable) - [ ] Screenshot links work (if screenshots captured) - [ ] Download buttons for JSON/HTML/ZIP files ### Test 14: Delete Scan via Web UI **Objective:** Verify scan deletion removes all data and files. **Steps:** 1. Login and navigate to scan list 2. Note a scan ID to delete 3. Click "Delete" button on scan 4. Confirm deletion 5. Check database and filesystem **Expected Result:** - [ ] Confirmation prompt appears - [ ] After confirmation, scan removed from list - [ ] Scan no longer appears in database - [ ] JSON/HTML/ZIP files deleted from filesystem - [ ] Screenshot directory deleted - [ ] Success message displayed --- ## Scan Management (API) ### Test 15: Trigger Scan via API **Objective:** Verify scan can be triggered via REST API. **Steps:** 1. Login and save session cookie (see Test 9) 2. Trigger scan: ```bash curl -X POST http://localhost:5000/api/scans \ -H "Content-Type: application/json" \ -d '{"config_file":"/app/configs/example-site.yaml"}' \ -b cookies.txt | jq '.' ``` 3. Note the `scan_id` from response **Expected Result:** - [ ] HTTP 201 Created response - [ ] Response includes `scan_id` (integer) - [ ] Response includes `status: "running"` - [ ] Response includes `message: "Scan queued successfully"` ### Test 16: Poll Scan Status **Objective:** Verify scan status can be polled via API. **Steps:** 1. Trigger a scan (Test 15) and note `scan_id` 2. Poll status immediately: ```bash curl -b cookies.txt http://localhost:5000/api/scans/{scan_id}/status | jq '.' ``` 3. Wait 30 seconds and poll again 4. Continue polling until status is `completed` or `failed` **Expected Result:** - [ ] Initial status: `"running"` - [ ] Response includes `started_at` timestamp - [ ] Response includes `completed_at: null` while running - [ ] After completion: status changes to `"completed"` or `"failed"` - [ ] `completed_at` timestamp set when done - [ ] If failed, `error_message` is present ### Test 17: Get Scan Details via API **Objective:** Verify complete scan details can be retrieved via API. **Steps:** 1. Trigger a scan and wait for completion 2. Get scan details: ```bash curl -b cookies.txt http://localhost:5000/api/scans/{scan_id} | jq '.' ``` **Expected Result:** - [ ] HTTP 200 OK response - [ ] Response includes all scan metadata (id, timestamp, duration, status, title) - [ ] Response includes file paths (json_path, html_path, zip_path, screenshot_dir) - [ ] Response includes `sites` array - [ ] Each site includes `ips` array - [ ] Each IP includes `ports` array - [ ] Each port includes `services` array - [ ] HTTPS services include `certificates` array (if applicable) - [ ] Certificates include `tls_versions` array (if applicable) - [ ] All relationships properly nested ### Test 18: List Scans with Pagination **Objective:** Verify scan list API supports pagination. **Steps:** 1. Trigger at least 25 scans 2. List first page: ```bash curl -b cookies.txt "http://localhost:5000/api/scans?page=1&per_page=20" | jq '.' ``` 3. List second page: ```bash curl -b cookies.txt "http://localhost:5000/api/scans?page=2&per_page=20" | jq '.' ``` **Expected Result:** - [ ] First page returns 20 scans - [ ] Response includes `total` (total count) - [ ] Response includes `page: 1` and `pages` (total pages) - [ ] Response includes `per_page: 20` - [ ] Second page returns remaining scans - [ ] No duplicate scans between pages ### Test 19: Filter Scans by Status **Objective:** Verify scan list can be filtered by status. **Steps:** 1. Trigger scans with different statuses (running, completed, failed) 2. Filter by running: ```bash curl -b cookies.txt "http://localhost:5000/api/scans?status=running" | jq '.' ``` 3. Filter by completed: ```bash curl -b cookies.txt "http://localhost:5000/api/scans?status=completed" | jq '.' ``` 4. Filter by failed: ```bash curl -b cookies.txt "http://localhost:5000/api/scans?status=failed" | jq '.' ``` **Expected Result:** - [ ] Each filter returns only scans with matching status - [ ] Total count reflects filtered results - [ ] Empty status filter returns all scans ### Test 20: Delete Scan via API **Objective:** Verify scan deletion via REST API. **Steps:** 1. Trigger a scan and wait for completion 2. Note the `scan_id` 3. Delete scan: ```bash curl -X DELETE -b cookies.txt http://localhost:5000/api/scans/{scan_id} | jq '.' ``` 4. Verify deletion: ```bash curl -b cookies.txt http://localhost:5000/api/scans/{scan_id} ``` 5. Check filesystem for scan files **Expected Result:** - [ ] Delete returns HTTP 200 OK - [ ] Delete response: `{"message": "Scan {id} deleted successfully"}` - [ ] Subsequent GET returns HTTP 404 Not Found - [ ] JSON/HTML/ZIP files deleted from filesystem - [ ] Screenshot directory deleted - [ ] Database record removed --- ## Error Handling ### Test 21: Invalid Config File **Objective:** Verify proper error handling for invalid config files. **Steps:** 1. Trigger scan with non-existent config: ```bash curl -X POST http://localhost:5000/api/scans \ -H "Content-Type: application/json" \ -d '{"config_file":"/app/configs/nonexistent.yaml"}' \ -b cookies.txt | jq '.' ``` **Expected Result:** - [ ] HTTP 400 Bad Request - [ ] Response includes `error` and `message` fields - [ ] Error message indicates config file invalid/not found - [ ] No scan record created in database ### Test 22: Missing Required Field **Objective:** Verify API validates required fields. **Steps:** 1. Trigger scan without config_file: ```bash curl -X POST http://localhost:5000/api/scans \ -H "Content-Type: application/json" \ -d '{}' \ -b cookies.txt | jq '.' ``` **Expected Result:** - [ ] HTTP 400 Bad Request - [ ] Error message indicates missing required field ### Test 23: Non-Existent Scan ID **Objective:** Verify 404 handling for non-existent scans. **Steps:** 1. Get scan with invalid ID: ```bash curl -b cookies.txt http://localhost:5000/api/scans/99999 | jq '.' ``` **Expected Result:** - [ ] HTTP 404 Not Found - [ ] Response: `{"error": "Scan not found", "message": "Scan with ID 99999 does not exist"}` ### Test 24: Invalid Pagination Parameters **Objective:** Verify pagination parameter validation. **Steps:** 1. Request with invalid page number: ```bash curl -b cookies.txt "http://localhost:5000/api/scans?page=-1" | jq '.' ``` 2. Request with invalid per_page: ```bash curl -b cookies.txt "http://localhost:5000/api/scans?per_page=1000" | jq '.' ``` **Expected Result:** - [ ] HTTP 400 Bad Request for negative page - [ ] per_page capped at maximum (100) - [ ] Error message indicates validation failure ### Test 25: Content Negotiation **Objective:** Verify API returns JSON and web UI returns HTML for errors. **Steps:** 1. Access non-existent scan via API: ```bash curl -H "Accept: application/json" http://localhost:5000/api/scans/99999 ``` 2. Access non-existent scan via browser: - Open http://localhost:5000/scans/99999 in browser **Expected Result:** - [ ] API request returns JSON error - [ ] Browser request returns HTML error page - [ ] HTML error page matches dark theme - [ ] HTML error page has navigation back to dashboard ### Test 26: Error Templates **Objective:** Verify custom error templates render correctly. **Steps:** 1. Trigger 400 error (bad request) 2. Trigger 401 error (unauthorized - access API without login) 3. Trigger 404 error (non-existent page - http://localhost:5000/nonexistent) 4. Trigger 405 error (method not allowed - POST to GET-only endpoint) **Expected Result:** - [ ] Each error displays custom error page - [ ] Error pages use dark theme - [ ] Error pages include error code and message - [ ] Error pages have "Back to Dashboard" link - [ ] Navigation bar visible on error pages (if authenticated) ### Test 27: Request ID Tracking **Objective:** Verify request IDs are generated and included in responses. **Steps:** 1. Make API request and check headers: ```bash curl -i -b cookies.txt http://localhost:5000/api/scans ``` **Expected Result:** - [ ] Response includes `X-Request-ID` header - [ ] Request ID is 8-character hex string - [ ] Response includes `X-Request-Duration-Ms` header - [ ] Duration is positive integer (milliseconds) ### Test 28: Logging **Objective:** Verify requests are logged with request IDs. **Steps:** 1. Make API request 2. Check logs: ```bash docker-compose -f docker-compose-web.yml logs web | tail -20 ``` **Expected Result:** - [ ] Logs include request ID in brackets `[a1b2c3d4]` - [ ] Logs include HTTP method, path, status code - [ ] Logs include request duration in milliseconds - [ ] Error logs include stack traces (if applicable) --- ## Performance & Concurrency ### Test 29: Concurrent Scans **Objective:** Verify multiple scans can run concurrently. **Steps:** 1. Trigger 3 scans simultaneously: ```bash curl -X POST http://localhost:5000/api/scans \ -H "Content-Type: application/json" \ -d '{"config_file":"/app/configs/example-site.yaml"}' \ -b cookies.txt & curl -X POST http://localhost:5000/api/scans \ -H "Content-Type: application/json" \ -d '{"config_file":"/app/configs/example-site.yaml"}' \ -b cookies.txt & curl -X POST http://localhost:5000/api/scans \ -H "Content-Type: application/json" \ -d '{"config_file":"/app/configs/example-site.yaml"}' \ -b cookies.txt & ``` 2. Check all scans are running: ```bash curl -b cookies.txt "http://localhost:5000/api/scans?status=running" | jq '.total' ``` **Expected Result:** - [ ] All 3 scans start successfully - [ ] All 3 scans have status "running" - [ ] No database locking errors in logs - [ ] All 3 scans eventually complete ### Test 30: API Responsiveness During Scan **Objective:** Verify web UI and API remain responsive during long-running scans. **Steps:** 1. Trigger a long-running scan (5+ minutes) 2. While scan is running, perform these actions: - Navigate to dashboard - List scans via API - Get scan status via API - Login/logout **Expected Result:** - [ ] Web UI loads quickly (< 2 seconds) - [ ] API requests respond quickly (< 500ms) - [ ] No timeouts or slow responses - [ ] Background scan does not block HTTP requests --- ## Data Persistence ### Test 31: Database Persistence Across Restarts **Objective:** Verify database persists across container restarts. **Steps:** 1. Trigger a scan and wait for completion 2. Note the scan ID 3. Restart container: ```bash docker-compose -f docker-compose-web.yml restart web ``` 4. Wait for container to restart (check health) 5. Query scan via API **Expected Result:** - [ ] Container restarts successfully - [ ] Database file persists - [ ] Scan still accessible after restart - [ ] All scan data intact ### Test 32: File Persistence **Objective:** Verify scan files persist in volume. **Steps:** 1. Trigger a scan and wait for completion 2. Note the file paths (JSON, HTML, ZIP, screenshots) 3. Verify files exist: ```bash docker exec sneakyscanner_web ls -lh /app/output/scan_report_*.json ``` 4. Restart container 5. Verify files still exist **Expected Result:** - [ ] All scan files created (JSON, HTML, ZIP, screenshots) - [ ] Files persist after container restart - [ ] Files accessible from host (mounted volume) - [ ] File sizes are non-zero --- ## Security ### Test 33: Password Hashing **Objective:** Verify passwords are hashed with bcrypt. **Steps:** 1. Check password in database: ```bash docker exec sneakyscanner_web sqlite3 /app/data/sneakyscanner.db \ "SELECT value FROM settings WHERE key='app_password';" ``` **Expected Result:** - [ ] Password is not stored in plaintext - [ ] Password starts with `$2b$` (bcrypt hash) - [ ] Hash is ~60 characters long ### Test 34: Session Cookie Security **Objective:** Verify session cookies have secure attributes (in production). **Steps:** 1. Login via browser (with developer tools open) 2. Inspect cookies (Application > Cookies) 3. Check session cookie attributes **Expected Result:** - [ ] Session cookie has `HttpOnly` flag - [ ] Session cookie has `Secure` flag (if HTTPS) - [ ] Session cookie has `SameSite` attribute - [ ] Session cookie expires on logout ### Test 35: SQL Injection Protection **Objective:** Verify inputs are sanitized against SQL injection. **Steps:** 1. Attempt SQL injection in scan list filter: ```bash curl -b cookies.txt "http://localhost:5000/api/scans?status='; DROP TABLE scans; --" ``` 2. Check database is intact: ```bash docker exec sneakyscanner_web sqlite3 /app/data/sneakyscanner.db ".tables" ``` **Expected Result:** - [ ] No SQL injection occurs - [ ] Database tables intact - [ ] API returns validation error or empty results - [ ] No database errors in logs ### Test 36: File Path Traversal Protection **Objective:** Verify config file paths are validated against path traversal. **Steps:** 1. Attempt path traversal in config_file: ```bash curl -X POST http://localhost:5000/api/scans \ -H "Content-Type: application/json" \ -d '{"config_file":"../../../etc/passwd"}' \ -b cookies.txt ``` **Expected Result:** - [ ] Request rejected with 400 Bad Request - [ ] Error message indicates invalid config file - [ ] No file outside /app/configs accessed - [ ] Security error logged --- ## Cleanup ### Test 37: Stop Services **Objective:** Gracefully stop all services. **Steps:** 1. Stop services: ```bash docker-compose -f docker-compose-web.yml down ``` 2. Verify containers stopped: ```bash docker-compose -f docker-compose-web.yml ps ``` **Expected Result:** - [ ] Services stop gracefully (no kill signals) - [ ] All containers stopped - [ ] No error messages in logs - [ ] Volumes preserved (data, output, logs, configs) ### Test 38: Volume Cleanup (Optional) **Objective:** Remove all data volumes (only if needed). **Steps:** 1. Stop and remove volumes: ```bash docker-compose -f docker-compose-web.yml down -v ``` 2. Verify volumes removed: ```bash docker volume ls | grep sneakyscanner ``` **Expected Result:** - [ ] All volumes removed - [ ] Database deleted - [ ] Scan results deleted - [ ] Logs deleted **Warning:** This is destructive and removes all data! --- ## Summary ### Test Results Summary Total Tests: 38 | Category | Tests | Passed | Failed | |----------|-------|--------|--------| | Deployment & Startup | 4 | | | | Authentication | 5 | | | | Scan Management (Web UI) | 5 | | | | Scan Management (API) | 6 | | | | Error Handling | 8 | | | | Performance & Concurrency | 2 | | | | Data Persistence | 2 | | | | Security | 4 | | | | Cleanup | 2 | | | | **Total** | **38** | | | ### Critical Tests (Must Pass) These tests are critical and must pass for Phase 2 to be considered complete: - [ ] Test 2: Docker Compose Startup - [ ] Test 3: Health Check - [ ] Test 6: Login with Correct Password - [ ] Test 15: Trigger Scan via API - [ ] Test 16: Poll Scan Status - [ ] Test 17: Get Scan Details via API - [ ] Test 18: List Scans with Pagination - [ ] Test 20: Delete Scan via API - [ ] Test 29: Concurrent Scans - [ ] Test 31: Database Persistence Across Restarts ### Known Issues Document any known issues or test failures here: 1. **Issue:** [Description] - **Severity:** Critical | High | Medium | Low - **Workaround:** [Workaround if available] - **Fix:** [Planned fix] --- ## Notes - Tests should be run in order, as later tests may depend on earlier setup - Some tests require multiple scans - consider batch creating scans for efficiency - Performance tests are environment-dependent (Docker resources, network speed) - Security tests are basic - professional security audit recommended for production - Manual testing complements automated tests - both are important --- **Manual Testing Checklist Version:** 1.0 **Phase:** 2 - Flask Web App Core **Last Updated:** 2025-11-14